1 ;;; php-imenu.el --- object-oriented, hierarchical imenu for PHP
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
9 ;;; Copyright (C) 2008 Marcel Cary
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.
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.
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.
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:
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)
44 ;;;----------------end here--------------------------------------------
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.
60 (eval-when-compile (require 'cl))
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))
68 ;;; Want to see properties or defines? Add an entry for them here.
69 (defvar php-imenu-patterns nil)
70 (setq php-imenu-patterns
72 ;; types: classes and interfaces
74 ;; for some reason [:space:] and \s- aren't matching \n
76 "\\(\\(abstract[[:space:]\n]+\\)?class\\|interface\\)"
78 "\\([a-zA-Z0-9_]+\\)[[:space:]\n]*" ; class/iface name
79 "\\([a-zA-Z0-9_[:space:]\n]*\\)" ; extends / implements clauses
83 (match-string-no-properties 3)
84 (match-string-no-properties 1))
85 (concat (match-string-no-properties 3)
87 (match-string-no-properties 1)))
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
102 (concat (match-string-no-properties 3) "()"))
107 (when (not (looking-at "\\s-*;"))
112 ;;; Global variable to pass to imenu-progress-message in multiple functions
113 (defvar php-imenu-prev-pos nil)
115 ;;; An implementation of imenu-create-index-function
116 (defun php-imenu-create-index ()
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)
124 (defun php-imenu-create-index-helper (min max name)
125 (let ((combined-pattern
128 (function (lambda (pat) (first pat)))
129 php-imenu-patterns "\\)\\|\\(")
134 (while (re-search-forward combined-pattern max t)
135 (let ((pos (set-marker (make-marker) (match-beginning 0)))
138 (goto-char (match-beginning 0))
140 (lambda (pat) (looking-at (first pat))))
141 php-imenu-patterns))))
143 (message "php-imenu: How can no pattern get us here! %S" pos))
145 (not (php-imenu-in-string-p))
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)
155 (funcall php-imenu-alist-postprocessor
156 (cons (cons "*go*" pos)
160 (imenu-progress-message php-imenu-prev-pos nil)
162 (reverse index-alist)))
164 ;;; Recognize when in quoted strings or heredoc-style string literals
165 (defun php-imenu-in-string-p ()
170 (and (re-search-backward "<<<\\([A-Za-z0-9_]+\\)$" nil t)
171 (not (re-search-forward (concat "^"
172 (match-string-no-properties 1)