diff options
| -rw-r--r-- | lisp/progmodes/python.el | 147 |
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. |
| 876 | The name of the class or function should be in a group so it can | 881 | The name of the defun should be grouped so it can be retrieved |
| 877 | be retrieved via `match-string'.") | 882 | via `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'. |
| 881 | When NODECORATORS is non-nil decorators are not included. This | 886 | When NODECORATORS is non-nil decorators are not included. This |
| 882 | is the main part of`python-beginning-of-defun-function' | 887 | is the main part of`python-beginning-of-defun-function' |
| 883 | implementation. Return non-nil if point is moved to the | 888 | implementation. Return non-nil if point is moved to the |
| 884 | beginning-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." | |||
| 916 | With positive ARG move that number of functions forward. With | 921 | With positive ARG move that number of functions forward. With |
| 917 | negative do the same but backwards. When NODECORATORS is non-nil | 922 | negative do the same but backwards. When NODECORATORS is non-nil |
| 918 | decorators are not included. Return non-nil if point is moved to the | 923 | decorators are not included. Return non-nil if point is moved to the |
| 919 | beginning-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. | ||
| 1961 | Set 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. | ||
| 1968 | It 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. | ||
| 1985 | ELEMENT-LIST is the defun name splitted by \".\" and FULL-ELEMENT | ||
| 1986 | is the same thing, the difference is that FULL-ELEMENT remains | ||
| 1987 | untouched in all recursive calls. | ||
| 1988 | Argument 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 | |||
| 2023 | The 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 | |||
| 2034 | This 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 | |||
| 2045 | Internally it uses `python-imenu-make-element-tree' to create all | ||
| 2046 | branches 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)) | ||
| 2052 | python-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. |
| 2071 | Optional argument INCLUDE-TYPE indicates to include the type of the defun. | ||
| 1950 | This function is compatible to be used as | 2072 | This 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 |
| 1952 | not inside a defun." | 2074 | not 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 | ||