blob: 738de7d82544ee582f84b2ede4f809abe0735dd6 [file] [log] [blame]
Guido van Rossum3ec97e51995-10-09 21:27:37 +00001;;; PYIMENU.EL ---
2
3;; Copyright (C) 1995 Perry A. Stoll
4
5;; Author: Perry A. Stoll <stoll@atr-sw.atr.co.jp>
6;; Created: 12 May 1995
7;; Version: 1.0
8;; Keywords: tools python imenu
9
10;; This program is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; This program is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; A copy of the GNU General Public License can be obtained from the
21;; Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
22;; USA.
23
24;;;; COMMENTS
25
26;; I use the imenu package a lot for looking at Lisp and C/C++
27;; code. When I started using Python, I was dismayed that I couldn't
28;; use it to look at Python source. So here's a rough go at it.
29
30;;;; USAGE
31
32;; This program is used in conjunction with the imenu package. When
33;; you call imenu in python-mode in a python buffer, a list of
34;; functions and classes is built. The top level menu has a list of
35;; all functions and classes defined at the top indentation
36;; level. Classes which have methods defined in them have a submenu
37;; which contains all definitions contained in them. Selecting any
38;; item will bring you to that point in the file.
39
40;;;; INSTALLATION
41
42;; You know the routine:
43;; 1) Save this file as pyimenu.el,
44;; 2) Place that file somewhere in your emacs load path (maybe ~/emacs
45;; or ~/emacs/lisp),
46;; 3) Byte compile it (M-x byte-compile-file),
47;; 4) Add the following (between "cut here" and "end here") to your
48;; ~/.emacs file,
49;; 5) Reboot. (joke: DON'T do that, although you'll probably have to
50;; either reload your ~/.emacs file or start a new emacs)
51
52;;--------------cut here-------------------------------------------
53;;;; Load the pyimenu index function
54;;(autoload 'imenu "imenu" nil t)
55;;(autoload 'imenu-example--create-python-index "pyimenu")
56;;;; Add the index creation function to the python-mode-hook
57;;(add-hook 'python-mode-hook
58;; (function
59;; (lambda ()
60;; (setq imenu-create-index-function
61;; (function imenu-example--create-python-index)))))
62;;----------------end here--------------------------------------------
63;;
64;; That is all you need. Of course, the following provides a more
65;; useful interface. i.e. this is how I have it set up ;-)
66;;
67;;----------------optionally cut here----------------------------------
68;;(autoload 'imenu-add-to-menubar "imenu" nil t)
69;;(defun my-imenu-install-hook ()
70;; (imenu-add-to-menubar (format "%s-%s" "IM" mode-name)))
71;;(add-hook 'python-mode-hook (function my-imenu-install-hook))
72;;;; Bind imenu to some convenient (?) mouse key. This really lets you
73;;;; fly around the buffer. Here it is set to Meta-Shift-Mouse3Click.
74;;(global-set-key [M-S-down-mouse-3] (function imenu))
75;;-----------------optionaly end here-----------------------------------
76
77;;;; CAVEATS/NOTES
78
79;; 0) I'm not a professional elisp programmer and it shows in the code
80;; below. If anyone there has suggestions/changes, I'd love to
81;; hear them. I've tried the code out on a bunch of python files
82;; from the python-1.1.1 Demo distribution and it worked with
83;; them - your mileage may very.
84;;
85;; 1) You must have the imenu package to use this file. This file
86;; works with imenu version 1.11 (the version included with emacs
87;; 19.28) and imenu version 1.14; if you have a later version, this
88;; may not work with it.
89;;
90;; 2) This setup assumes you also have python-mode.el, so that it can
91;; use the python-mode-hook. It comes with the python distribution.
92;;
93;; 3) I don't have the Python 1.2 distribution yet, so again, this may
94;; not work with that.
95;;
96
97(require 'imenu)
98
99;;;
100;;; VARIABLES: customizable in your .emacs file.
101;;;
102
103(defvar imenu-example--python-show-method-args-p nil
104 "*When using imenu package with python-mode, whether the arguments of
105the function/methods should be printed in the imenu buffer in addition
106to the function/method name. If non-nil, args are printed.")
107
108;;;
109;;; VARIABLES: internal use.
110;;;
111(defvar imenu-example--python-class-regexp
112 (concat ; <<classes>>
113 "\\(" ;
114 "^[ \t]*" ; newline and maybe whitespace
115 "\\(class[ \t]+[a-zA-Z0-9_]+\\)" ; class name
116 ; possibly multiple superclasses
117 "\\([ \t]*\\((\\([a-zA-Z0-9_, \t\n]\\)*)\\)?\\)"
118 "[ \t]*:" ; and the final :
119 "\\)" ; >>classes<<
120 )
121 "Regexp for Python classes for use with the imenu package."
122)
123
124(defvar imenu-example--python-method-regexp
125 (concat ; <<methods and functions>>
126 "\\(" ;
127 "^[ \t]*" ; new line and maybe whitespace
128 "\\(def[ \t]+" ; function definitions start with def
129 "\\([a-zA-Z0-9_]+\\)" ; name is here
130 ; function arguments...
131 "[ \t]*(\\([a-zA-Z0-9_=,\* \t\n]*\\))"
132 "\\)" ; end of def
133 "[ \t]*:" ; and then the :
134 "\\)" ; >>methods and functions<<
135 )
136 "Regexp for Python methods/functions for use with the imenu package."
137 )
138
139(defvar imenu-example--python-method-no-arg-parens '(2 8)
140 "Indicies into the parenthesis list of the regular expression for
141python for use with imenu. Using these values will result in smaller
142imenu lists, as arguments to functions are not listed.
143
144See the variable imenu-example--python-show-method-args-p to for
145information")
146
147(defvar imenu-example--python-method-arg-parens '(2 7)
148 "Indicies into the parenthesis list of the regular expression for
149python for use with imenu. Using these values will result in large
150imenu lists, as arguments to functions are listed.
151
152See the variable imenu-example--python-show-method-args-p to for
153information")
154
155;; Note that in this format, this variable can still be used with the
156;; imenu--generic-function. Otherwise, there is no real reason to have
157;; it.
158(defvar imenu-example--generic-python-expression
159 (cons
160 (concat
161 imenu-example--python-class-regexp
162 "\\|" ; or...
163 imenu-example--python-method-regexp
164 )
165 imenu-example--python-method-no-arg-parens)
166 "Generic Python expression which may be used directly with imenu by
167setting the variable imenu-generic-expression to this value. Also, see
168the function \\[imenu-example--create-python-index] for an alternate
169way of finding the index.")
170
171;; These next two variables are used when searching for the python
172;; class/definitions. Just saving some time in accessing the
173;; generic-python-expression, really.
174(defvar imenu-example--python-generic-regexp)
175(defvar imenu-example--python-generic-parens)
176
177;;;
178;;; CODE:
179;;;
180
181;; Note:
182;; At first, I tried using some of the functions supplied by
183;; python-mode to navigate through functions and classes, but after a
184;; while, I decided dump it. This file is relatively self contained
185;; and I liked it that.
186
187;;;###autoload
188(defun imenu-example--create-python-index ()
189 "Interface function for imenu package to find all python classes and
190functions/methods. Calls function
191\\[imenu-example--create-python-index-engine]. See that function for
192the details of how this works."
193 (setq imenu-example--python-generic-regexp
194 (car imenu-example--generic-python-expression))
195 (setq imenu-example--python-generic-parens
196 (if imenu-example--python-show-method-args-p
197 imenu-example--python-method-arg-parens
198 imenu-example--python-method-no-arg-parens))
199 (goto-char (point-min))
200 (imenu-example--create-python-index-engine nil))
201
202(defun imenu-example--create-python-index-engine (&optional start-indent)
203"Function for finding all definitions (classes, methods, or functions)
204in a python file for the imenu package.
205
206Retuns a possibly nested alist of the form \(INDEX-NAME
207 INDEX-POSITION). The second element of the alist may be an alist,
208producing a nested list as in \(INDEX-NAME . INDEX-ALIST).
209
210This function should not be called directly, as it calls itself
211recursively and requires some setup. Rather this is the engine for the
212function \\[imenu-example--create-python-index].
213
214It works recursively by looking for all definitions at the current
215indention level. When it finds one, it adds it to the alist. If it
216finds a definition at a greater indentation level, it removes the
217previous definition from the alist. In it's place it adds all
218definitions found at the next indentation level. When it finds a
219definition that is less indented then the current level, it retuns the
220alist it has created thus far.
221
222 The optional argument START-INDENT indicates the starting indentation
223at which to continue looking for python classes, methods, or
224functions. If this is not supplied, the function uses the indentation
225of the first definition found. "
226 (let ((index-alist '())
227 (sub-method-alist '())
228 looking-p
229 def-name prev-name
230 cur-indent def-pos
231 (class-paren (first imenu-example--python-generic-parens))
232 (def-paren (second imenu-example--python-generic-parens)))
233 (setq looking-p
234 (re-search-forward imenu-example--python-generic-regexp
235 (point-max) t))
236 (while looking-p
237 (save-excursion
238 ;; used to set def-name to this value but generic-extract-name is
239 ;; new to imenu-1.14. this way it still works with imenu-1.11
240 ;;(imenu--generic-extract-name imenu-example--python-generic-parens))
241 (let ((cur-paren (if (match-beginning class-paren)
242 class-paren def-paren)))
243 (setq def-name
244 (buffer-substring (match-beginning cur-paren)
245 (match-end cur-paren))))
246 (beginning-of-line)
247 (setq cur-indent (current-indentation)))
248
249 ;; HACK: want to go to the correct definition location. Assuming
250 ;; here that there are only two..which is true for python.
251 (setq def-pos
252 (or (match-beginning class-paren)
253 (match-beginning def-paren)))
254
255 ; if we don't have a starting indent level, take this one
256 (or start-indent
257 (setq start-indent cur-indent))
258
259 ; if we don't have class name yet, take this one
260 (or prev-name
261 (setq prev-name def-name))
262
263 ;; what level is the next definition on?
264 ;; must be same, deeper or shallower indentation
265 (cond
266
267 ;; at the same indent level, add it to the list...
268 ((= start-indent cur-indent)
269 (push (cons def-name def-pos) index-alist))
270
271 ;; deeper indented expression, recur...
272 ((< start-indent cur-indent)
273
274 ;; the point is currently on the expression we're supposed to
275 ;; start on, so go back to the last expression. The recursive
276 ;; call will find this place again and add it to the correct
277 ;; list
278 (re-search-backward imenu-example--python-generic-regexp
279 (point-min) 'move)
280 (setq sub-method-alist (imenu-example--create-python-index-engine
281 cur-indent))
282
283 (if sub-method-alist
284 ;; we put the last element on the index-alist on the start
285 ;; of the submethod alist so the user can still get to it.
286 (let ((save-elmt (pop index-alist)))
287 (push (cons (imenu-create-submenu-name prev-name)
288 (cons save-elmt sub-method-alist))
289 index-alist))))
290
291 ;; found less indented expression, we're done.
292 (t
293 (setq looking-p nil)
294 (re-search-backward imenu-example--python-generic-regexp
295 (point-min) t)))
296 (setq prev-name def-name)
297 (and looking-p
298 (setq looking-p
299 (re-search-forward imenu-example--python-generic-regexp
300 (point-max) 'move))))
301 (nreverse index-alist)))
302
303(provide 'pyimenu)
304
305;;; PyImenu.EL ends here