aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorFabián Ezequiel Gallina2012-05-17 00:03:14 -0300
committerFabián Ezequiel Gallina2012-05-17 00:03:14 -0300
commitfc2dc7df0209283c66e21b20c2b6bafe29d6c331 (patch)
treedd9d6df45f125f75f74c823869959852e126adac /lisp/progmodes/python.el
parent2d63ad564d8ca5258bb6a94fbec63403e1ff56e5 (diff)
downloademacs-fc2dc7df0209283c66e21b20c2b6bafe29d6c331.tar.gz
emacs-fc2dc7df0209283c66e21b20c2b6bafe29d6c331.zip
Implemented imenu support.
New variables: + python-imenu-include-defun-type + python-imenu-make-tree + python-imenu-subtree-root-label + python-imenu-index-alist New Functions: + python-imenu-tree-assoc + python-imenu-make-element-tree + python-imenu-make-tree + python-imenu-create-index API changes: + python-info-current-defun now supports an optional argument called INCLUDE-TYPE.
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el147
1 files changed, 138 insertions, 9 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 884a84c382a..a582dc6db2a 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -34,7 +34,7 @@
34 34
35;; Implements Syntax highlighting, Indentation, Movement, Shell 35;; Implements Syntax highlighting, Indentation, Movement, Shell
36;; interaction, Shell completion, Pdb tracking, Symbol completion, 36;; interaction, Shell completion, Pdb tracking, Symbol completion,
37;; Skeletons, FFAP, Code Check, Eldoc. 37;; Skeletons, FFAP, Code Check, Eldoc, imenu.
38 38
39;; Syntax highlighting: Fontification of code is provided and supports 39;; Syntax highlighting: Fontification of code is provided and supports
40;; python's triple quoted strings properly. 40;; python's triple quoted strings properly.
@@ -124,6 +124,11 @@
124;; might guessed you should run `python-shell-send-buffer' from time 124;; might guessed you should run `python-shell-send-buffer' from time
125;; to time to get better results too. 125;; to time to get better results too.
126 126
127;; imenu: This mode supports imenu. It builds a plain or tree menu
128;; depending on the value of `python-imenu-make-tree'. Also you can
129;; customize if menu items should include its type using
130;; `python-imenu-include-defun-type'.
131
127;; If you used python-mode.el you probably will miss auto-indentation 132;; If you used python-mode.el you probably will miss auto-indentation
128;; when inserting newlines. To achieve the same behavior you have 133;; when inserting newlines. To achieve the same behavior you have
129;; two options: 134;; two options:
@@ -873,15 +878,15 @@ With numeric ARG, just insert that many colons. With
873(defvar python-nav-beginning-of-defun-regexp 878(defvar python-nav-beginning-of-defun-regexp
874 (python-rx line-start (* space) defun (+ space) (group symbol-name)) 879 (python-rx line-start (* space) defun (+ space) (group symbol-name))
875 "Regular expresion matching beginning of class or function. 880 "Regular expresion matching beginning of class or function.
876The name of the class or function should be in a group so it can 881The name of the defun should be grouped so it can be retrieved
877be retrieved via `match-string'.") 882via `match-string'.")
878 883
879(defun python-nav-beginning-of-defun (&optional nodecorators) 884(defun python-nav-beginning-of-defun (&optional nodecorators)
880 "Move point to `beginning-of-defun'. 885 "Move point to `beginning-of-defun'.
881When NODECORATORS is non-nil decorators are not included. This 886When NODECORATORS is non-nil decorators are not included. This
882is the main part of`python-beginning-of-defun-function' 887is the main part of`python-beginning-of-defun-function'
883implementation. Return non-nil if point is moved to the 888implementation. Return non-nil if point is moved to the
884beginning-of-defun." 889`beginning-of-defun'."
885 (let ((indent-pos (save-excursion 890 (let ((indent-pos (save-excursion
886 (back-to-indentation) 891 (back-to-indentation)
887 (point-marker))) 892 (point-marker)))
@@ -916,7 +921,7 @@ beginning-of-defun."
916With positive ARG move that number of functions forward. With 921With positive ARG move that number of functions forward. With
917negative do the same but backwards. When NODECORATORS is non-nil 922negative do the same but backwards. When NODECORATORS is non-nil
918decorators are not included. Return non-nil if point is moved to the 923decorators are not included. Return non-nil if point is moved to the
919beginning-of-defun." 924`beginning-of-defun'."
920 (when (or (null arg) (= arg 0)) (setq arg 1)) 925 (when (or (null arg) (= arg 0)) (setq arg 1))
921 (if (> arg 0) 926 (if (> arg 0)
922 (dotimes (i arg (python-nav-beginning-of-defun nodecorators))) 927 (dotimes (i arg (python-nav-beginning-of-defun nodecorators)))
@@ -1943,10 +1948,127 @@ Interactively, prompt for symbol."
1943 (help-print-return-message))))))) 1948 (help-print-return-message)))))))
1944 1949
1945 1950
1951;;; Imenu
1952
1953(defcustom python-imenu-include-defun-type t
1954 "Non-nil make imenu items to include its type."
1955 :type 'boolean
1956 :group 'python
1957 :safe 'booleanp)
1958
1959(defcustom python-imenu-make-tree nil
1960 "Non-nil make imenu to build a tree menu.
1961Set to nil for speed."
1962 :type 'boolean
1963 :group 'python
1964 :safe 'booleanp)
1965
1966(defcustom python-imenu-subtree-root-label "<Jump to %s>"
1967 "Label displayed to navigate to root from a subtree.
1968It can contain a \"%s\" which will be replaced with the root name."
1969 :type 'string
1970 :group 'python
1971 :safe 'stringp)
1972
1973(defvar python-imenu-index-alist nil
1974 "Calculated index tree for imenu.")
1975
1976(defun python-imenu-tree-assoc (keylist tree)
1977 "Using KEYLIST traverse TREE."
1978 (if keylist
1979 (python-imenu-tree-assoc (cdr keylist)
1980 (ignore-errors (assoc (car keylist) tree)))
1981 tree))
1982
1983(defun python-imenu-make-element-tree (element-list full-element plain-index)
1984 "Make a tree from plain alist of module names.
1985ELEMENT-LIST is the defun name splitted by \".\" and FULL-ELEMENT
1986is the same thing, the difference is that FULL-ELEMENT remains
1987untouched in all recursive calls.
1988Argument PLAIN-INDEX is the calculated plain index used to build the tree."
1989 (when (not (python-imenu-tree-assoc full-element python-imenu-index-alist))
1990 (when element-list
1991 (let* ((subelement-point (cdr (assoc
1992 (mapconcat #'identity full-element ".")
1993 plain-index)))
1994 (subelement-name (car element-list))
1995 (subelement-position (position subelement-name full-element))
1996 (subelement-path (when subelement-position
1997 (butlast
1998 full-element
1999 (- (length full-element)
2000 subelement-position)))))
2001 (let ((path-ref (python-imenu-tree-assoc subelement-path
2002 python-imenu-index-alist)))
2003 (if (not path-ref)
2004 (push (cons subelement-name subelement-point)
2005 python-imenu-index-alist)
2006 (when (not (listp (cdr path-ref)))
2007 ;; Modifiy root cdr to be a list
2008 (setcdr path-ref
2009 (list (cons (format python-imenu-subtree-root-label
2010 (car path-ref))
2011 (cdr (assoc
2012 (mapconcat #'identity
2013 subelement-path ".")
2014 plain-index))))))
2015 (when (not (assoc subelement-name path-ref))
2016 (push (cons subelement-name subelement-point) (cdr path-ref))))))
2017 (python-imenu-make-element-tree (cdr element-list)
2018 full-element plain-index))))
2019
2020(defun python-imenu-make-tree (index)
2021"Build the imenu alist tree from plain INDEX.
2022
2023The idea of this function is that given the alist:
2024
2025 '((\"Test\" . 100)
2026 (\"Test.__init__\" . 200)
2027 (\"Test.some_method\" . 300)
2028 (\"Test.some_method.another\" . 400)
2029 (\"Test.something_else\" . 500)
2030 (\"test\" . 600)
2031 (\"test.reprint\" . 700)
2032 (\"test.reprint\" . 800))
2033
2034This tree gets built:
2035
2036 '((\"Test\" . ((\"jump to...\" . 100)
2037 (\"__init__\" . 200)
2038 (\"some_method\" . ((\"jump to...\" . 300)
2039 (\"another\" . 400)))
2040 (\"something_else\" . 500)))
2041 (\"test\" . ((\"jump to...\" . 600)
2042 (\"reprint\" . 700)
2043 (\"reprint\" . 800))))
2044
2045Internally it uses `python-imenu-make-element-tree' to create all
2046branches for each element."
2047(setq python-imenu-index-alist nil)
2048(mapcar (lambda (element)
2049 (python-imenu-make-element-tree element element index))
2050 (mapcar (lambda (element)
2051 (split-string (car element) "\\." t)) index))
2052python-imenu-index-alist)
2053
2054(defun python-imenu-create-index ()
2055 "`imenu-create-index-function' for Python."
2056 (let ((index))
2057 (goto-char (point-max))
2058 (while (python-beginning-of-defun-function 1 t)
2059 (let ((defun-dotted-name
2060 (python-info-current-defun python-imenu-include-defun-type)))
2061 (push (cons defun-dotted-name (point)) index)))
2062 (if python-imenu-make-tree
2063 (python-imenu-make-tree index)
2064 index)))
2065
2066
1946;;; Misc helpers 2067;;; Misc helpers
1947 2068
1948(defun python-info-current-defun () 2069(defun python-info-current-defun (&optional include-type)
1949 "Return name of surrounding function with Python compatible dotty syntax. 2070 "Return name of surrounding function with Python compatible dotty syntax.
2071Optional argument INCLUDE-TYPE indicates to include the type of the defun.
1950This function is compatible to be used as 2072This function is compatible to be used as
1951`add-log-current-defun-function' since it returns nil if point is 2073`add-log-current-defun-function' since it returns nil if point is
1952not inside a defun." 2074not inside a defun."
@@ -1957,13 +2079,18 @@ not inside a defun."
1957 (save-excursion 2079 (save-excursion
1958 (goto-char (line-end-position)) 2080 (goto-char (line-end-position))
1959 (forward-comment -1) 2081 (forward-comment -1)
1960 (while (and (not (equal 0 (current-indentation))) 2082 (while (python-beginning-of-defun-function 1 t)
1961 (python-beginning-of-defun-function 1 t))
1962 (when (or (not min-indent) 2083 (when (or (not min-indent)
1963 (< (current-indentation) min-indent)) 2084 (< (current-indentation) min-indent))
1964 (setq min-indent (current-indentation)) 2085 (setq min-indent (current-indentation))
1965 (looking-at python-nav-beginning-of-defun-regexp) 2086 (looking-at python-nav-beginning-of-defun-regexp)
1966 (setq names (cons (match-string-no-properties 1) names)))))) 2087 (setq names (cons
2088 (if (not include-type)
2089 (match-string-no-properties 1)
2090 (mapconcat 'identity
2091 (split-string
2092 (match-string-no-properties 0)) " "))
2093 names))))))
1967 (when names 2094 (when names
1968 (mapconcat (lambda (string) string) names ".")))) 2095 (mapconcat (lambda (string) string) names "."))))
1969 2096
@@ -2103,6 +2230,8 @@ if that value is non-nil."
2103 (add-hook 'completion-at-point-functions 2230 (add-hook 'completion-at-point-functions
2104 'python-completion-complete-at-point nil 'local) 2231 'python-completion-complete-at-point nil 'local)
2105 2232
2233 (setq imenu-create-index-function #'python-imenu-create-index)
2234
2106 (set (make-local-variable 'add-log-current-defun-function) 2235 (set (make-local-variable 'add-log-current-defun-function)
2107 #'python-info-current-defun) 2236 #'python-info-current-defun)
2108 2237