summaryrefslogtreecommitdiffstats
path: root/emacs.d/nxhtml/related/php-imenu.el
blob: 560bac032a71b70312722e88b4ec11e5f6b13b6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
;;; php-imenu.el --- object-oriented, hierarchical imenu for PHP
;;;
;;; Maintainer: Marcel Cary <marcel-cary of care2.com>
;;; Keywords: php languages oop
;;; Created: 2008-06-23
;;; Modified: 2008-07-18
;;; X-URL: http://www.oak.homeunix.org/~marcel/blog/articles/2008/07/14/nested-imenu-for-php
;;;
;;; Copyright (C) 2008 Marcel Cary
;;;
;;; License
;;;
;;; This program is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU General Public License
;;; as published by the Free Software Foundation; either version 2
;;; of the License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program; if not, write to the Free Software
;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
;;;
;;;
;;; Usage
;;;
;;; Rename this file to php-imenu.el if it isn't already then place it in
;;; your Emacs lisp path (eg. site-lisp) and add to your .emacs file:
;;;
;;;--------------cut here-------------------------------------------
;   ;; Load the php-imenu index function
;   (autoload 'php-imenu-create-index "php-imenu" nil t)
;   ;; Add the index creation function to the php-mode-hook
;   (add-hook 'php-mode-user-hook 'php-imenu-setup)
;   (defun php-imenu-setup ()
;     (setq imenu-create-index-function (function php-imenu-create-index))
;     ;; uncomment if you prefer speedbar:
;     ;(setq php-imenu-alist-postprocessor (function reverse))
;     (imenu-add-menubar-index)
;   )
;;;----------------end here--------------------------------------------
;;;
;;; Commentary
;;;
;;; Refines php-mode's imenu support.  Imenu provides a menubar entry called
;;; "Index" that allows you to jump to a structural element of a file.  While
;;; php-mode generates separate lists of functions and classes in imenu,
;;; php-imenu.el (this code) generates a tree of class and function
;;; definitions.  It lists functions under the classes in which they're
;;; defined.  The hierarchical display of functions within their classes makes
;;; the "Index" menu far more useful in understanding the high-level structure
;;; of a file, and it makes it easier to find a method when a file contains
;;; multiple by the same name.
;;;
;;; Code:

(eval-when-compile (require 'cl))
(require 'imenu)
(require 'thingatpt)

;;; Alas, speedbar shows menu items in reverse, but only below the top level.
;;; Provide a way to fix it. See sample configuration in file comment.
(defvar php-imenu-alist-postprocessor (function identity))

;;; Want to see properties or defines?  Add an entry for them here.
(defvar php-imenu-patterns nil)
(setq php-imenu-patterns
  (list
   ;; types: classes and interfaces
   (list
    ;; for some reason [:space:] and \s- aren't matching \n
    (concat "^\\s-*"
            "\\(\\(abstract[[:space:]\n]+\\)?class\\|interface\\)"
            "[[:space:]\n]+"
            "\\([a-zA-Z0-9_]+\\)[[:space:]\n]*" ; class/iface name
            "\\([a-zA-Z0-9_[:space:]\n]*\\)" ; extends / implements clauses
            "[{]")
    (lambda ()
      (message "%S %S"
               (match-string-no-properties 3)
               (match-string-no-properties 1))
      (concat (match-string-no-properties 3)
              " - "
              (match-string-no-properties 1)))
    (lambda ()
      (save-excursion
        (backward-up-list 1)
        (forward-sexp)
        (point))))
   ;; functions
   (list
    (concat "^[[:space:]\n]*"
            "\\(\\(public\\|protected\\|private\\|"
                  "static\\|abstract\\)[[:space:]\n]+\\)*"
            "function[[:space:]\n]*&?[[:space:]\n]*"
            "\\([a-zA-Z0-9_]+\\)[[:space:]\n]*" ; function name
            "[(]")
    (lambda ()
      (concat (match-string-no-properties 3) "()"))
    (lambda ()
      (save-excursion
        (backward-up-list 1)
        (forward-sexp)
        (when (not (looking-at "\\s-*;"))
            (forward-sexp))
        (point))))
   ))

;;; Global variable to pass to imenu-progress-message in multiple functions
(defvar php-imenu-prev-pos nil)

;;; An implementation of imenu-create-index-function
(defun php-imenu-create-index ()
  (let (prev-pos)
    (imenu-progress-message php-imenu-prev-pos 0)
    (let ((result (php-imenu-create-index-helper (point-min) (point-max) nil)))
      ;(message "bye %S" result)
      (imenu-progress-message php-imenu-prev-pos 100)
      result)))

(defun php-imenu-create-index-helper (min max name)
  (let ((combined-pattern
         (concat "\\("
                 (mapconcat
                  (function (lambda (pat) (first pat)))
		  php-imenu-patterns "\\)\\|\\(")
		 "\\)"))
        (index-alist '()))
    (goto-char min)
    (save-match-data
      (while (re-search-forward combined-pattern max t)
        (let ((pos (set-marker (make-marker) (match-beginning 0)))
              (min (match-end 0))
              (pat (save-excursion
                     (goto-char (match-beginning 0))
                     (find-if (function
                               (lambda (pat) (looking-at (first pat))))
                              php-imenu-patterns))))
          (when (not pat)
            (message "php-imenu: How can no pattern get us here! %S" pos))
          (when (and pat
                      (not (php-imenu-in-string-p))
                     )
            (let* ((name (funcall (second pat)))
                   (max  (funcall (third pat)))
                   (children (php-imenu-create-index-helper min max name)))
              ;; should validate max: what happens if unmatched curly?
              ;(message "%S %S %S" nm name (mapcar (function first) children))
              (if (equal '() children)
                  (push (cons name pos) index-alist)
                (push (cons name
                            (funcall php-imenu-alist-postprocessor
                                     (cons (cons "*go*" pos)
                                           children)))
                      index-alist))
              ))
          (imenu-progress-message php-imenu-prev-pos nil)
          )))
    (reverse index-alist)))

;;; Recognize when in quoted strings or heredoc-style string literals
(defun php-imenu-in-string-p ()
  (save-match-data
    (or (in-string-p)
        (let ((pt (point)))
          (save-excursion
            (and (re-search-backward "<<<\\([A-Za-z0-9_]+\\)$" nil t)
                 (not (re-search-forward (concat "^"
                                                 (match-string-no-properties 1)
                                                 ";$")
                                         pt t))))))))