aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/python.el
diff options
context:
space:
mode:
authorEli Zaretskii2013-04-19 11:39:24 +0300
committerEli Zaretskii2013-04-19 11:39:24 +0300
commit4cf86a2fa737296d26e8a02338e2bf6f26e1133a (patch)
tree094502cb2926db798570e62d32c680b2ce72b6c2 /lisp/progmodes/python.el
parentf60f289656d815827a492150f2e564dd081967f8 (diff)
parent7d6883367a54b4eee342aa861ef73e4191151ef0 (diff)
downloademacs-4cf86a2fa737296d26e8a02338e2bf6f26e1133a.tar.gz
emacs-4cf86a2fa737296d26e8a02338e2bf6f26e1133a.zip
Merge from trunk.
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r--lisp/progmodes/python.el234
1 files changed, 213 insertions, 21 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 53aff94ae0e..06d07d6ee3c 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -177,12 +177,14 @@
177;; might guessed you should run `python-shell-send-buffer' from time 177;; might guessed you should run `python-shell-send-buffer' from time
178;; to time to get better results too. 178;; to time to get better results too.
179 179
180;; Imenu: This mode supports Imenu in its most basic form, letting it 180;; Imenu: There are two index building functions to be used as
181;; build the necessary alist via `imenu-default-create-index-function' 181;; `imenu-create-index-function': `python-imenu-create-index' (the
182;; by having set `imenu-extract-index-name-function' to 182;; default one, builds the alist in form of a tree) and
183;; `python-info-current-defun' and 183;; `python-imenu-create-flat-index'. See also
184;; `imenu-prev-index-position-function' to 184;; `python-imenu-format-item-label-function',
185;; `python-imenu-prev-index-position'. 185;; `python-imenu-format-parent-item-label-function',
186;; `python-imenu-format-parent-item-jump-label-function' variables for
187;; changing the way labels are formatted in the tree version.
186 188
187;; If you used python-mode.el you probably will miss auto-indentation 189;; If you used python-mode.el you probably will miss auto-indentation
188;; when inserting newlines. To achieve the same behavior you have 190;; when inserting newlines. To achieve the same behavior you have
@@ -1194,7 +1196,7 @@ Returns nil if point is not in a def or class."
1194 1196
1195(defun python-nav--syntactically (fn poscompfn &optional contextfn) 1197(defun python-nav--syntactically (fn poscompfn &optional contextfn)
1196 "Move point using FN avoiding places with specific context. 1198 "Move point using FN avoiding places with specific context.
1197FN must take no arguments. POSCOMPFN is a two arguments function 1199FN must take no arguments. POSCOMPFN is a two arguments function
1198used to compare current and previous point after it is moved 1200used to compare current and previous point after it is moved
1199using FN, this is normally a less-than or greater-than 1201using FN, this is normally a less-than or greater-than
1200comparison. Optional argument CONTEXTFN defaults to 1202comparison. Optional argument CONTEXTFN defaults to
@@ -3008,15 +3010,192 @@ Interactively, prompt for symbol."
3008 3010
3009;;; Imenu 3011;;; Imenu
3010 3012
3011(defun python-imenu-prev-index-position () 3013(defvar python-imenu-format-item-label-function
3012 "Python mode's `imenu-prev-index-position-function'." 3014 'python-imenu-format-item-label
3013 (let ((found)) 3015 "Imenu function used to format an item label.
3014 (while (and (setq found 3016It must be a function with two arguments: TYPE and NAME.")
3015 (re-search-backward python-nav-beginning-of-defun-regexp nil t)) 3017
3016 (not (python-info-looking-at-beginning-of-defun)))) 3018(defvar python-imenu-format-parent-item-label-function
3017 (and found 3019 'python-imenu-format-parent-item-label
3018 (python-info-looking-at-beginning-of-defun) 3020 "Imenu function used to format a parent item label.
3019 (python-info-current-defun)))) 3021It must be a function with two arguments: TYPE and NAME.")
3022
3023(defvar python-imenu-format-parent-item-jump-label-function
3024 'python-imenu-format-parent-item-jump-label
3025 "Imenu function used to format a parent jump item label.
3026It must be a function with two arguments: TYPE and NAME.")
3027
3028(defun python-imenu-format-item-label (type name)
3029 "Return imenu label for single node using TYPE and NAME."
3030 (format "%s (%s)" name type))
3031
3032(defun python-imenu-format-parent-item-label (type name)
3033 "Return imenu label for parent node using TYPE and NAME."
3034 (format "%s..." (python-imenu-format-item-label type name)))
3035
3036(defun python-imenu-format-parent-item-jump-label (type name)
3037 "Return imenu label for parent node jump using TYPE and NAME."
3038 (if (string= type "class")
3039 "*class definition*"
3040 "*function definition*"))
3041
3042(defun python-imenu--put-parent (type name pos num-children tree &optional root)
3043 "Add the parent with TYPE, NAME, POS and NUM-CHILDREN to TREE.
3044Optional Argument ROOT must be non-nil when the node being
3045processed is the root of the TREE."
3046 (let ((label
3047 (funcall python-imenu-format-item-label-function type name))
3048 (jump-label
3049 (funcall python-imenu-format-parent-item-jump-label-function type name)))
3050 (if root
3051 ;; This is the root, everything is a children.
3052 (cons label (cons (cons jump-label pos) tree))
3053 ;; This is node a which may contain some children.
3054 (cons
3055 (cons label (cons (cons jump-label pos)
3056 ;; Append all the children
3057 (python-util-popn tree num-children)))
3058 ;; All previous non-children nodes.
3059 (nthcdr num-children tree)))))
3060
3061(defun python-imenu--build-tree (&optional min-indent prev-indent num-children tree)
3062 "Recursively build the tree of nested definitions of a node.
3063Arguments MIN-INDENT PREV-INDENT NUM-CHILDREN and TREE are
3064internal and should not be passed explicitly unless you know what
3065you are doing."
3066 (setq num-children (or num-children 0)
3067 min-indent (or min-indent 0))
3068 (let* ((pos (python-nav-backward-defun))
3069 (type)
3070 (name (when (and pos (looking-at python-nav-beginning-of-defun-regexp))
3071 (let ((split (split-string (match-string-no-properties 0))))
3072 (setq type (car split))
3073 (cadr split))))
3074 (label (when name
3075 (funcall python-imenu-format-item-label-function type name)))
3076 (indent (current-indentation)))
3077 (cond ((not pos)
3078 ;; No defun found, nothing to add.
3079 tree)
3080 ((equal indent 0)
3081 (if (> num-children 0)
3082 ;; Append it as the parent of everything collected to
3083 ;; this point.
3084 (python-imenu--put-parent type name pos num-children tree t)
3085 ;; There are no children, this is a lonely defun.
3086 (cons label pos)))
3087 ((equal min-indent indent)
3088 ;; Stop collecting nodes after moving to a position with
3089 ;; indentation equaling min-indent. This is specially
3090 ;; useful for navigating nested definitions recursively.
3091 tree)
3092 (t
3093 (python-imenu--build-tree
3094 min-indent
3095 indent
3096 ;; Add another children, either when this is the
3097 ;; first call or when indentation is
3098 ;; less-or-equal than previous. And do not
3099 ;; discard the number of children, because the
3100 ;; way code is scanned, all children are
3101 ;; collected until a root node yet to be found
3102 ;; appears.
3103 (if (or (not prev-indent)
3104 (and
3105 (> indent min-indent)
3106 (<= indent prev-indent)))
3107 (1+ num-children)
3108 num-children)
3109 (cond ((not prev-indent)
3110 ;; First call to the function: append this
3111 ;; defun to the index.
3112 (list (cons label pos)))
3113 ((= indent prev-indent)
3114 ;; Add another defun with the same depth
3115 ;; as the previous.
3116 (cons (cons label pos) tree))
3117 ((and (< indent prev-indent)
3118 (< 0 num-children))
3119 ;; There are children to be appended and
3120 ;; the previous defun had more
3121 ;; indentation, the current one must be a
3122 ;; parent.
3123 (python-imenu--put-parent type name pos num-children tree))
3124 ((> indent prev-indent)
3125 ;; There are children defuns deeper than
3126 ;; current depth. Fear not, we already
3127 ;; know how to treat them.
3128 (cons
3129 (prog1
3130 (python-imenu--build-tree
3131 prev-indent indent 1 (list (cons label pos)))
3132 ;; Adjustment: after scanning backwards
3133 ;; for all deeper children, we need to
3134 ;; continue our scan for a parent from
3135 ;; the current defun we are looking at.
3136 (python-nav-forward-defun))
3137 tree))))))))
3138
3139(defun python-imenu-create-index ()
3140 "Return tree Imenu alist for the current python buffer.
3141Change `python-imenu-format-item-label-function',
3142`python-imenu-format-parent-item-label-function',
3143`python-imenu-format-parent-item-jump-label-function' to
3144customize how labels are formatted."
3145 (goto-char (point-max))
3146 (let ((index)
3147 (tree))
3148 (while (setq tree (python-imenu--build-tree))
3149 (setq index (cons tree index)))
3150 index))
3151
3152(defun python-imenu-create-flat-index (&optional alist prefix)
3153 "Return flat outline of the current python buffer for Imenu.
3154Optional Argument ALIST is the tree to be flattened, when nil
3155`python-imenu-build-index' is used with
3156`python-imenu-format-parent-item-jump-label-function'
3157`python-imenu-format-parent-item-label-function'
3158`python-imenu-format-item-label-function' set to (lambda (type
3159name) name). Optional Argument PREFIX is used in recursive calls
3160and should not be passed explicitly.
3161
3162Converts this:
3163
3164 \((\"Foo\" . 103)
3165 (\"Bar\" . 138)
3166 (\"decorator\"
3167 (\"decorator\" . 173)
3168 (\"wrap\"
3169 (\"wrap\" . 353)
3170 (\"wrapped_f\" . 393))))
3171
3172To this:
3173
3174 \((\"Foo\" . 103)
3175 (\"Bar\" . 138)
3176 (\"decorator\" . 173)
3177 (\"decorator.wrap\" . 353)
3178 (\"decorator.wrapped_f\" . 393))"
3179 (apply
3180 'nconc
3181 (mapcar
3182 (lambda (item)
3183 (let ((name (if prefix
3184 (concat prefix "." (car item))
3185 (car item)))
3186 (pos (cdr item)))
3187 (cond ((or (numberp pos) (markerp pos))
3188 (list (cons name pos)))
3189 ((listp pos)
3190 (message "%S" item)
3191 (cons
3192 (cons name (cdar pos))
3193 (python-imenu-create-flat-index (cddr item) name))))))
3194 (or alist
3195 (let ((python-imenu-format-item-label-function (lambda (type name) name))
3196 (python-imenu-format-parent-item-label-function (lambda (type name) name))
3197 (python-imenu-format-parent-item-jump-label-function (lambda (type name) name)))
3198 (python-imenu-create-index))))))
3020 3199
3021 3200
3022;;; Misc helpers 3201;;; Misc helpers
@@ -3337,6 +3516,22 @@ Optional argument DIRECTION defines the direction to move to."
3337 (goto-char comment-start)) 3516 (goto-char comment-start))
3338 (forward-comment factor))) 3517 (forward-comment factor)))
3339 3518
3519(defun python-util-popn (lst n)
3520 "Return LST first N elements.
3521N should be an integer, when it's a natural negative number its
3522opposite is used. When N is bigger than the length of LST, the
3523list is returned as is."
3524 (let* ((n (min (abs n)))
3525 (len (length lst))
3526 (acc))
3527 (if (> n len)
3528 lst
3529 (while (< 0 n)
3530 (setq acc (cons (car lst) acc)
3531 lst (cdr lst)
3532 n (1- n)))
3533 (reverse acc))))
3534
3340 3535
3341;;;###autoload 3536;;;###autoload
3342(define-derived-mode python-mode prog-mode "Python" 3537(define-derived-mode python-mode prog-mode "Python"
@@ -3382,11 +3577,8 @@ if that value is non-nil."
3382 (add-hook 'post-self-insert-hook 3577 (add-hook 'post-self-insert-hook
3383 'python-indent-post-self-insert-function nil 'local) 3578 'python-indent-post-self-insert-function nil 'local)
3384 3579
3385 (set (make-local-variable 'imenu-extract-index-name-function) 3580 (set (make-local-variable 'imenu-create-index-function)
3386 #'python-info-current-defun) 3581 #'python-imenu-create-index)
3387
3388 (set (make-local-variable 'imenu-prev-index-position-function)
3389 #'python-imenu-prev-index-position)
3390 3582
3391 (set (make-local-variable 'add-log-current-defun-function) 3583 (set (make-local-variable 'add-log-current-defun-function)
3392 #'python-info-current-defun) 3584 #'python-info-current-defun)