| ;;; PYIMENU.EL ---  | 
 |  | 
 | ;; Copyright (C) 1995 Perry A. Stoll | 
 |  | 
 | ;; Author: Perry A. Stoll <stoll@atr-sw.atr.co.jp> | 
 | ;; Created: 12 May 1995 | 
 | ;; Version: 1.0 | 
 | ;; Keywords: tools python imenu | 
 |  | 
 | ;; 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, 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. | 
 |  | 
 | ;; A copy of the GNU General Public License can be obtained from the | 
 | ;; Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, | 
 | ;; USA. | 
 |  | 
 | ;;;; COMMENTS | 
 |  | 
 | ;; I use the imenu package a lot for looking at Lisp and C/C++ | 
 | ;; code. When I started using Python, I was dismayed that I couldn't | 
 | ;; use it to look at Python source. So here's a rough go at it. | 
 |  | 
 | ;;;; USAGE | 
 |  | 
 | ;; This program is used in conjunction with the imenu package. When | 
 | ;; you call imenu in python-mode in a python buffer, a list of | 
 | ;; functions and classes is built. The top level menu has a list of | 
 | ;; all functions and classes defined at the top indentation | 
 | ;; level. Classes which have methods defined in them have a submenu | 
 | ;; which contains all definitions contained in them. Selecting any | 
 | ;; item will bring you to that point in the file. | 
 |  | 
 | ;;;; INSTALLATION | 
 |  | 
 | ;; You know the routine: | 
 | ;; 1) Save this file as pyimenu.el, | 
 | ;; 2) Place that file somewhere in your emacs load path (maybe ~/emacs | 
 | ;;    or ~/emacs/lisp), | 
 | ;; 3) Byte compile it (M-x byte-compile-file), | 
 | ;; 4) Add the following (between "cut here" and "end here") to your | 
 | ;;    ~/.emacs file, | 
 | ;; 5) Reboot. (joke: DON'T do that, although you'll probably have to | 
 | ;;    either reload your ~/.emacs file or start a new emacs) | 
 |  | 
 | ;;--------------cut here------------------------------------------- | 
 | ;;;; Load the pyimenu index function | 
 | ;;(autoload 'imenu "imenu" nil t) | 
 | ;;(autoload 'imenu-example--create-python-index "pyimenu") | 
 | ;;;; Add the index creation function to the python-mode-hook  | 
 | ;;(add-hook 'python-mode-hook | 
 | ;;	  (function | 
 | ;;	   (lambda () | 
 | ;;	     (setq imenu-create-index-function | 
 | ;;		   (function imenu-example--create-python-index))))) | 
 | ;;----------------end here-------------------------------------------- | 
 | ;; | 
 | ;; That is all you need. Of course, the following provides a more | 
 | ;; useful interface. i.e. this is how I have it set up ;-) | 
 | ;; | 
 | ;;----------------optionally cut here---------------------------------- | 
 | ;;(autoload 'imenu-add-to-menubar "imenu" nil t) | 
 | ;;(defun my-imenu-install-hook () | 
 | ;;  (imenu-add-to-menubar (format "%s-%s" "IM" mode-name))) | 
 | ;;(add-hook 'python-mode-hook (function my-imenu-install-hook)) | 
 | ;;;; Bind imenu to some convenient (?) mouse key. This really lets you | 
 | ;;;; fly around the buffer. Here it is set to Meta-Shift-Mouse3Click. | 
 | ;;(global-set-key [M-S-down-mouse-3] (function imenu)) | 
 | ;;-----------------optionaly end here----------------------------------- | 
 |  | 
 | ;;;; CAVEATS/NOTES | 
 |  | 
 | ;; 0) I'm not a professional elisp programmer and it shows in the code | 
 | ;;    below. If anyone there has suggestions/changes, I'd love to | 
 | ;;    hear them. I've tried the code out on a bunch of python files | 
 | ;;    from the python-1.1.1 Demo distribution and it worked with | 
 | ;;    them - your mileage may very. | 
 | ;; | 
 | ;; 1) You must have the imenu package to use this file. This file | 
 | ;;    works with imenu version 1.11 (the version included with emacs | 
 | ;;    19.28) and imenu version 1.14; if you have a later version, this | 
 | ;;    may not work with it. | 
 | ;; | 
 | ;; 2) This setup assumes you also have python-mode.el, so that it can | 
 | ;;    use the python-mode-hook. It comes with the python distribution. | 
 | ;; | 
 | ;; 3) I don't have the Python 1.2 distribution yet, so again, this may | 
 | ;;    not work with that. | 
 | ;; | 
 |  | 
 | (require 'imenu) | 
 |  | 
 | ;;; | 
 | ;;; VARIABLES: customizable in your .emacs file. | 
 | ;;; | 
 |  | 
 | (defvar imenu-example--python-show-method-args-p nil  | 
 |   "*When using imenu package with python-mode, whether the arguments of | 
 | the function/methods should be printed in the imenu buffer in addition | 
 | to the function/method name. If non-nil, args are printed.") | 
 |  | 
 | ;;; | 
 | ;;; VARIABLES: internal use. | 
 | ;;; | 
 | (defvar imenu-example--python-class-regexp | 
 |   (concat                              ; <<classes>> | 
 |    "\\("                               ; | 
 |    "^[ \t]*"                           ; newline and maybe whitespace | 
 |    "\\(class[ \t]+[a-zA-Z0-9_]+\\)"    ; class name | 
 |                                        ; possibly multiple superclasses | 
 |    "\\([ \t]*\\((\\([a-zA-Z0-9_, \t\n]\\)*)\\)?\\)" | 
 |    "[ \t]*:"                           ; and the final : | 
 |    "\\)"                               ; >>classes<< | 
 |    ) | 
 |   "Regexp for Python classes for use with the imenu package." | 
 | ) | 
 |  | 
 | (defvar imenu-example--python-method-regexp | 
 |   (concat                               ; <<methods and functions>> | 
 |    "\\("                                ;  | 
 |    "^[ \t]*"                            ; new line and maybe whitespace | 
 |    "\\(def[ \t]+"                       ; function definitions start with def | 
 |    "\\([a-zA-Z0-9_]+\\)"                ;   name is here | 
 | 					;   function arguments... | 
 |    "[ \t]*(\\([a-zA-Z0-9_=,\* \t\n]*\\))" | 
 |    "\\)"                                ; end of def | 
 |    "[ \t]*:"                            ; and then the : | 
 |    "\\)"                                ; >>methods and functions<< | 
 |    ) | 
 |   "Regexp for Python methods/functions for use with the imenu package." | 
 |   ) | 
 |  | 
 | (defvar imenu-example--python-method-no-arg-parens '(2 8) | 
 |   "Indicies into the parenthesis list of the regular expression for | 
 | python for use with imenu. Using these values will result in smaller | 
 | imenu lists, as arguments to functions are not listed. | 
 |  | 
 | See the variable imenu-example--python-show-method-args-p to for | 
 | information") | 
 |  | 
 | (defvar imenu-example--python-method-arg-parens '(2 7) | 
 |   "Indicies into the parenthesis list of the regular expression for | 
 | python for use with imenu. Using these values will result in large | 
 | imenu lists, as arguments to functions are listed. | 
 |  | 
 | See the variable imenu-example--python-show-method-args-p to for | 
 | information") | 
 |  | 
 | ;; Note that in this format, this variable can still be used with the | 
 | ;; imenu--generic-function. Otherwise, there is no real reason to have | 
 | ;; it. | 
 | (defvar imenu-example--generic-python-expression | 
 |   (cons | 
 |    (concat  | 
 |     imenu-example--python-class-regexp | 
 |     "\\|"  ; or... | 
 |     imenu-example--python-method-regexp | 
 |     ) | 
 |    imenu-example--python-method-no-arg-parens) | 
 |   "Generic Python expression which may be used directly with imenu by | 
 | setting the variable imenu-generic-expression to this value. Also, see | 
 | the function \\[imenu-example--create-python-index] for an alternate | 
 | way of finding the index.") | 
 |  | 
 | ;; These next two variables are used when searching for the python | 
 | ;; class/definitions. Just saving some time in accessing the | 
 | ;; generic-python-expression, really. | 
 | (defvar imenu-example--python-generic-regexp) | 
 | (defvar imenu-example--python-generic-parens) | 
 |  | 
 | ;;; | 
 | ;;; CODE: | 
 | ;;; | 
 |  | 
 | ;; Note: | 
 | ;; At first, I tried using some of the functions supplied by | 
 | ;; python-mode to navigate through functions and classes, but after a | 
 | ;; while, I decided dump it. This file is relatively self contained | 
 | ;; and I liked it that. | 
 |  | 
 | ;;;###autoload | 
 | (defun imenu-example--create-python-index () | 
 |   "Interface function for imenu package to find all python classes and | 
 | functions/methods. Calls function | 
 | \\[imenu-example--create-python-index-engine]. See that function for | 
 | the details of how this works." | 
 |   (setq imenu-example--python-generic-regexp | 
 | 	(car imenu-example--generic-python-expression)) | 
 |   (setq imenu-example--python-generic-parens | 
 | 	(if imenu-example--python-show-method-args-p | 
 | 	    imenu-example--python-method-arg-parens | 
 | 	  imenu-example--python-method-no-arg-parens)) | 
 |   (goto-char (point-min)) | 
 |   (imenu-example--create-python-index-engine nil)) | 
 |  | 
 | (defun imenu-example--create-python-index-engine (&optional start-indent) | 
 | "Function for finding all definitions (classes, methods, or functions) | 
 | in a python file for the imenu package.  | 
 |  | 
 | Retuns a possibly nested alist of the form \(INDEX-NAME | 
 |  INDEX-POSITION). The second element of the alist may be an alist, | 
 | producing a nested list as in \(INDEX-NAME . INDEX-ALIST). | 
 |  | 
 | This function should not be called directly, as it calls itself | 
 | recursively and requires some setup. Rather this is the engine for the | 
 | function \\[imenu-example--create-python-index]. | 
 |  | 
 | It works recursively by looking for all definitions at the current | 
 | indention level. When it finds one, it adds it to the alist. If it | 
 | finds a definition at a greater indentation level, it removes the | 
 | previous definition from the alist. In it's place it adds all | 
 | definitions found at the next indentation level. When it finds a | 
 | definition that is less indented then the current level, it retuns the | 
 | alist it has created thus far. | 
 |  | 
 |  The optional argument START-INDENT indicates the starting indentation | 
 | at which to continue looking for python classes, methods, or | 
 | functions. If this is not supplied, the function uses the indentation | 
 | of the first definition found. " | 
 |   (let ((index-alist '()) | 
 | 	(sub-method-alist '()) | 
 | 	looking-p | 
 | 	def-name prev-name | 
 | 	cur-indent def-pos | 
 | 	(class-paren (first  imenu-example--python-generic-parens))  | 
 | 	(def-paren   (second imenu-example--python-generic-parens))) | 
 |     (setq looking-p | 
 | 	  (re-search-forward imenu-example--python-generic-regexp | 
 | 			     (point-max) t)) | 
 |     (while looking-p | 
 |       (save-excursion | 
 | 	;; used to set def-name to this value but generic-extract-name is | 
 | 	;; new to imenu-1.14. this way it still works with imenu-1.11 | 
 | 	;;(imenu--generic-extract-name imenu-example--python-generic-parens)) | 
 | 	(let ((cur-paren (if (match-beginning class-paren) | 
 | 			     class-paren def-paren))) | 
 | 	  (setq def-name | 
 | 		(buffer-substring (match-beginning cur-paren) | 
 | 				  (match-end  cur-paren)))) | 
 | 	(beginning-of-line) | 
 | 	(setq cur-indent (current-indentation))) | 
 |  | 
 |       ;; HACK: want to go to the correct definition location. Assuming | 
 |       ;; here that there are only two..which is true for python. | 
 |       (setq def-pos | 
 | 	    (or  (match-beginning class-paren) | 
 | 		 (match-beginning def-paren))) | 
 |  | 
 |       ; if we don't have a starting indent level, take this one | 
 |       (or start-indent | 
 | 	  (setq start-indent cur-indent)) | 
 |  | 
 |       ; if we don't have class name yet, take this one | 
 |       (or prev-name | 
 | 	  (setq prev-name def-name)) | 
 |  | 
 |       ;; what level is the next definition on?  | 
 |       ;; must be same, deeper or shallower indentation | 
 |       (cond | 
 |  | 
 |        ;; at the same indent level, add it to the list... | 
 |        ((= start-indent cur-indent) | 
 | 	(push (cons def-name def-pos) index-alist)) | 
 |  | 
 |        ;; deeper indented expression, recur... | 
 |        ((< start-indent cur-indent) | 
 |  | 
 | 	;; the point is currently on the expression we're supposed to | 
 | 	;; start on, so go back to the last expression. The recursive | 
 | 	;; call will find this place again and add it to the correct | 
 | 	;; list | 
 | 	(re-search-backward imenu-example--python-generic-regexp | 
 | 			    (point-min) 'move) | 
 | 	(setq sub-method-alist (imenu-example--create-python-index-engine | 
 | 				cur-indent)) | 
 |  | 
 | 	(if sub-method-alist | 
 | 	    ;; we put the last element on the index-alist on the start | 
 | 	    ;; of the submethod alist so the user can still get to it. | 
 | 	    (let ((save-elmt (pop index-alist))) | 
 | 	      (push (cons (imenu-create-submenu-name prev-name) | 
 | 			  (cons save-elmt sub-method-alist)) | 
 | 		    index-alist)))) | 
 |  | 
 |        ;; found less indented expression, we're done. | 
 |        (t  | 
 | 	(setq looking-p nil) | 
 | 	(re-search-backward imenu-example--python-generic-regexp  | 
 | 			    (point-min) t))) | 
 |       (setq prev-name def-name) | 
 |       (and looking-p | 
 | 	   (setq looking-p | 
 | 		 (re-search-forward imenu-example--python-generic-regexp | 
 | 				    (point-max) 'move)))) | 
 |     (nreverse index-alist))) | 
 |  | 
 | (provide 'pyimenu) | 
 |  | 
 | ;;; PyImenu.EL ends here |