initial commit
[emacs-init.git] / nxhtml / related / php-imenu.el
1 ;;; php-imenu.el --- object-oriented, hierarchical imenu for PHP
2 ;;;
3 ;;; Maintainer: Marcel Cary <marcel-cary of care2.com>
4 ;;; Keywords: php languages oop
5 ;;; Created: 2008-06-23
6 ;;; Modified: 2008-07-18
7 ;;; X-URL: http://www.oak.homeunix.org/~marcel/blog/articles/2008/07/14/nested-imenu-for-php
8 ;;;
9 ;;; Copyright (C) 2008 Marcel Cary
10 ;;;
11 ;;; License
12 ;;;
13 ;;; This program is free software; you can redistribute it and/or
14 ;;; modify it under the terms of the GNU General Public License
15 ;;; as published by the Free Software Foundation; either version 2
16 ;;; of the License, or (at your option) any later version.
17 ;;;
18 ;;; This program is distributed in the hope that it will be useful,
19 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 ;;; GNU General Public License for more details.
22 ;;;
23 ;;; You should have received a copy of the GNU General Public License
24 ;;; along with this program; if not, write to the Free Software
25 ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26 ;;;
27 ;;;
28 ;;; Usage
29 ;;;
30 ;;; Rename this file to php-imenu.el if it isn't already then place it in
31 ;;; your Emacs lisp path (eg. site-lisp) and add to your .emacs file:
32 ;;;
33 ;;;--------------cut here-------------------------------------------
34 ;   ;; Load the php-imenu index function
35 ;   (autoload 'php-imenu-create-index "php-imenu" nil t)
36 ;   ;; Add the index creation function to the php-mode-hook
37 ;   (add-hook 'php-mode-user-hook 'php-imenu-setup)
38 ;   (defun php-imenu-setup ()
39 ;     (setq imenu-create-index-function (function php-imenu-create-index))
40 ;     ;; uncomment if you prefer speedbar:
41 ;     ;(setq php-imenu-alist-postprocessor (function reverse))
42 ;     (imenu-add-menubar-index)
43 ;   )
44 ;;;----------------end here--------------------------------------------
45 ;;;
46 ;;; Commentary
47 ;;;
48 ;;; Refines php-mode's imenu support.  Imenu provides a menubar entry called
49 ;;; "Index" that allows you to jump to a structural element of a file.  While
50 ;;; php-mode generates separate lists of functions and classes in imenu,
51 ;;; php-imenu.el (this code) generates a tree of class and function
52 ;;; definitions.  It lists functions under the classes in which they're
53 ;;; defined.  The hierarchical display of functions within their classes makes
54 ;;; the "Index" menu far more useful in understanding the high-level structure
55 ;;; of a file, and it makes it easier to find a method when a file contains
56 ;;; multiple by the same name.
57 ;;;
58 ;;; Code:
59
60 (eval-when-compile (require 'cl))
61 (require 'imenu)
62 (require 'thingatpt)
63
64 ;;; Alas, speedbar shows menu items in reverse, but only below the top level.
65 ;;; Provide a way to fix it. See sample configuration in file comment.
66 (defvar php-imenu-alist-postprocessor (function identity))
67
68 ;;; Want to see properties or defines?  Add an entry for them here.
69 (defvar php-imenu-patterns nil)
70 (setq php-imenu-patterns
71   (list
72    ;; types: classes and interfaces
73    (list
74     ;; for some reason [:space:] and \s- aren't matching \n
75     (concat "^\\s-*"
76             "\\(\\(abstract[[:space:]\n]+\\)?class\\|interface\\)"
77             "[[:space:]\n]+"
78             "\\([a-zA-Z0-9_]+\\)[[:space:]\n]*" ; class/iface name
79             "\\([a-zA-Z0-9_[:space:]\n]*\\)" ; extends / implements clauses
80             "[{]")
81     (lambda ()
82       (message "%S %S"
83                (match-string-no-properties 3)
84                (match-string-no-properties 1))
85       (concat (match-string-no-properties 3)
86               " - "
87               (match-string-no-properties 1)))
88     (lambda ()
89       (save-excursion
90         (backward-up-list 1)
91         (forward-sexp)
92         (point))))
93    ;; functions
94    (list
95     (concat "^[[:space:]\n]*"
96             "\\(\\(public\\|protected\\|private\\|"
97                   "static\\|abstract\\)[[:space:]\n]+\\)*"
98             "function[[:space:]\n]*&?[[:space:]\n]*"
99             "\\([a-zA-Z0-9_]+\\)[[:space:]\n]*" ; function name
100             "[(]")
101     (lambda ()
102       (concat (match-string-no-properties 3) "()"))
103     (lambda ()
104       (save-excursion
105         (backward-up-list 1)
106         (forward-sexp)
107         (when (not (looking-at "\\s-*;"))
108             (forward-sexp))
109         (point))))
110    ))
111
112 ;;; Global variable to pass to imenu-progress-message in multiple functions
113 (defvar php-imenu-prev-pos nil)
114
115 ;;; An implementation of imenu-create-index-function
116 (defun php-imenu-create-index ()
117   (let (prev-pos)
118     (imenu-progress-message php-imenu-prev-pos 0)
119     (let ((result (php-imenu-create-index-helper (point-min) (point-max) nil)))
120       ;(message "bye %S" result)
121       (imenu-progress-message php-imenu-prev-pos 100)
122       result)))
123
124 (defun php-imenu-create-index-helper (min max name)
125   (let ((combined-pattern
126          (concat "\\("
127                  (mapconcat
128                   (function (lambda (pat) (first pat)))
129                   php-imenu-patterns "\\)\\|\\(")
130                  "\\)"))
131         (index-alist '()))
132     (goto-char min)
133     (save-match-data
134       (while (re-search-forward combined-pattern max t)
135         (let ((pos (set-marker (make-marker) (match-beginning 0)))
136               (min (match-end 0))
137               (pat (save-excursion
138                      (goto-char (match-beginning 0))
139                      (find-if (function
140                                (lambda (pat) (looking-at (first pat))))
141                               php-imenu-patterns))))
142           (when (not pat)
143             (message "php-imenu: How can no pattern get us here! %S" pos))
144           (when (and pat
145                       (not (php-imenu-in-string-p))
146                      )
147             (let* ((name (funcall (second pat)))
148                    (max  (funcall (third pat)))
149                    (children (php-imenu-create-index-helper min max name)))
150               ;; should validate max: what happens if unmatched curly?
151               ;(message "%S %S %S" nm name (mapcar (function first) children))
152               (if (equal '() children)
153                   (push (cons name pos) index-alist)
154                 (push (cons name
155                             (funcall php-imenu-alist-postprocessor
156                                      (cons (cons "*go*" pos)
157                                            children)))
158                       index-alist))
159               ))
160           (imenu-progress-message php-imenu-prev-pos nil)
161           )))
162     (reverse index-alist)))
163
164 ;;; Recognize when in quoted strings or heredoc-style string literals
165 (defun php-imenu-in-string-p ()
166   (save-match-data
167     (or (in-string-p)
168         (let ((pt (point)))
169           (save-excursion
170             (and (re-search-backward "<<<\\([A-Za-z0-9_]+\\)$" nil t)
171                  (not (re-search-forward (concat "^"
172                                                  (match-string-no-properties 1)
173                                                  ";$")
174                                          pt t))))))))