diff options
| author | Yuan Fu | 2022-09-24 19:41:17 -0700 |
|---|---|---|
| committer | Yuan Fu | 2022-09-24 21:11:30 -0700 |
| commit | 795e01ac248d389a581589b13a02465a2f99202f (patch) | |
| tree | 8ba12d103784a17b37df98bd41f7d71483f5d008 /lisp/progmodes/python.el | |
| parent | f071e61d106e6f3c17b660e3aa1a5b7890ea5d41 (diff) | |
| download | emacs-795e01ac248d389a581589b13a02465a2f99202f.tar.gz emacs-795e01ac248d389a581589b13a02465a2f99202f.zip | |
Update and enable treesit-imenu function in python.el
* lisp/progmodes/python.el (python--treesit-settings): Add docstring.
(python--imenu-treesit-create-index-1): Rewrite with
treesit-induce-sparse-tree.
(python-imenu-treesit-create-index): Move main body to
python--imenu-treesit-create-index-1.
(python-imenu-treesit-create-flat-index): Fix typo.
(python-mode): Enable treesit-imenu. Also fix indentation for
which-func code.
Diffstat (limited to 'lisp/progmodes/python.el')
| -rw-r--r-- | lisp/progmodes/python.el | 114 |
1 files changed, 69 insertions, 45 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 8368f4da513..fb91d00053a 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -1035,7 +1035,9 @@ Do not fontify the initial f for f-strings." | |||
| 1035 | `(seq bol (or ,@python--treesit-exceptions) | 1035 | `(seq bol (or ,@python--treesit-exceptions) |
| 1036 | eol)) | 1036 | eol)) |
| 1037 | @font-lock-type-face)) | 1037 | @font-lock-type-face)) |
| 1038 | (type (identifier) @font-lock-type-face)))) | 1038 | (type (identifier) @font-lock-type-face))) |
| 1039 | "Tree-sitter font-lock settings.") | ||
| 1040 | |||
| 1039 | 1041 | ||
| 1040 | ;;; Indentation | 1042 | ;;; Indentation |
| 1041 | 1043 | ||
| @@ -5244,61 +5246,79 @@ To this: | |||
| 5244 | (python-imenu-create-index)))))) | 5246 | (python-imenu-create-index)))))) |
| 5245 | 5247 | ||
| 5246 | ;;; Tree-sitter imenu | 5248 | ;;; Tree-sitter imenu |
| 5247 | ;; | ||
| 5248 | ;; This works, but is slower than the native functions, presumably | ||
| 5249 | ;; because traversing the parser tree is slower than scanning the | ||
| 5250 | ;; text. Also I'm sure this consumes more memory as we allocate | ||
| 5251 | ;; memory for every node in the tree. | ||
| 5252 | 5249 | ||
| 5253 | (defun python--imenu-treesit-create-index (&optional node) | 5250 | (defun python--imenu-treesit-create-index-1 (node) |
| 5254 | "Return tree Imenu alist for the current Python buffer. | 5251 | "Given a sparse tree, create an imenu alist. |
| 5255 | 5252 | ||
| 5256 | Change `python-imenu-format-item-label-function', | 5253 | NODE is the root node of the tree returned by |
| 5257 | `python-imenu-format-parent-item-label-function', | 5254 | `treesit-induce-sparse-tree' (not a tree-sitter node, its car is |
| 5258 | `python-imenu-format-parent-item-jump-label-function' to | 5255 | a tree-sitter node). Walk that tree and return an imenu alist. |
| 5259 | customize how labels are formatted. | ||
| 5260 | 5256 | ||
| 5261 | NODE is the root node of the subtree you want to build an index | 5257 | Return a list of ENTRY where |
| 5262 | of. If nil, use the root node of the whole parse tree. | ||
| 5263 | 5258 | ||
| 5264 | Similar to `python-imenu-create-index' but use tree-sitter." | 5259 | ENTRY := (NAME . MARKER) |
| 5265 | (let* ((node (or node (treesit-buffer-root-node 'python))) | 5260 | | (NAME . ((JUMP-LABEL . MARKER) |
| 5266 | (children (treesit-node-children node t)) | 5261 | ENTRY |
| 5267 | (subtrees (mapcan #'python--imenu-treesit-create-index | 5262 | ...) |
| 5263 | |||
| 5264 | NAME is the function/class's name, JUMP-LABEL is like \"*function | ||
| 5265 | definition*\"." | ||
| 5266 | (let* ((ts-node (car node)) | ||
| 5267 | (children (cdr node)) | ||
| 5268 | (subtrees (mapcan #'python--imenu-treesit-create-index-1 | ||
| 5268 | children)) | 5269 | children)) |
| 5269 | (type (pcase (treesit-node-type node) | 5270 | (type (pcase (treesit-node-type ts-node) |
| 5270 | ("function_definition" 'def) | 5271 | ("function_definition" 'def) |
| 5271 | ("class_definition" 'class) | 5272 | ("class_definition" 'class))) |
| 5272 | (_ nil))) | 5273 | ;; The root of the tree could have a nil ts-node. |
| 5273 | (name (when type | 5274 | (name (when ts-node |
| 5274 | (treesit-node-text | 5275 | (treesit-node-text |
| 5275 | (treesit-node-child-by-field-name | 5276 | (treesit-node-child-by-field-name |
| 5276 | node "name") t)))) | 5277 | ts-node "name") t))) |
| 5278 | (marker (when ts-node | ||
| 5279 | (set-marker (make-marker) | ||
| 5280 | (treesit-node-start ts-node))))) | ||
| 5277 | (cond | 5281 | (cond |
| 5278 | ;; 1. This node is a function/class and doesn't have children. | 5282 | ((null ts-node) |
| 5279 | ((and type (not subtrees)) | 5283 | subtrees) |
| 5280 | (let ((label | 5284 | (subtrees |
| 5281 | (funcall python-imenu-format-item-label-function | ||
| 5282 | type name))) | ||
| 5283 | (list (cons label | ||
| 5284 | (set-marker (make-marker) | ||
| 5285 | (treesit-node-start node)))))) | ||
| 5286 | ;; 2. This node is a function/class and has children. | ||
| 5287 | ((and type subtrees) | ||
| 5288 | (let ((parent-label | 5285 | (let ((parent-label |
| 5289 | (funcall python-imenu-format-parent-item-label-function | 5286 | (funcall python-imenu-format-parent-item-label-function |
| 5290 | type name)) | 5287 | type name)) |
| 5291 | (jump-label | 5288 | (jump-label |
| 5292 | (funcall python-imenu-format-parent-item-jump-label-function | 5289 | (funcall |
| 5293 | type name))) | 5290 | python-imenu-format-parent-item-jump-label-function |
| 5291 | type name))) | ||
| 5294 | `((,parent-label | 5292 | `((,parent-label |
| 5295 | ,(cons jump-label (set-marker (make-marker) | 5293 | ,(cons jump-label marker) |
| 5296 | (treesit-node-start node))) | ||
| 5297 | ,@subtrees)))) | 5294 | ,@subtrees)))) |
| 5298 | ;; 3. This node is not a function/class. | 5295 | (t (let ((label |
| 5299 | ((not type) subtrees)))) | 5296 | (funcall python-imenu-format-item-label-function |
| 5297 | type name))) | ||
| 5298 | (list (cons label marker))))))) | ||
| 5299 | |||
| 5300 | (defun python-imenu-treesit-create-index (&optional node) | ||
| 5301 | "Return tree Imenu alist for the current Python buffer. | ||
| 5300 | 5302 | ||
| 5301 | (defun python--imenu-treesit-create-flat-index () | 5303 | Change `python-imenu-format-item-label-function', |
| 5304 | `python-imenu-format-parent-item-label-function', | ||
| 5305 | `python-imenu-format-parent-item-jump-label-function' to | ||
| 5306 | customize how labels are formatted. | ||
| 5307 | |||
| 5308 | NODE is the root node of the subtree you want to build an index | ||
| 5309 | of. If nil, use the root node of the whole parse tree. | ||
| 5310 | |||
| 5311 | Similar to `python-imenu-create-index' but use tree-sitter." | ||
| 5312 | (let* ((node (or node (treesit-buffer-root-node 'python))) | ||
| 5313 | (tree (treesit-induce-sparse-tree | ||
| 5314 | node | ||
| 5315 | (rx (seq bol | ||
| 5316 | (or "function" "class") | ||
| 5317 | "_definition" | ||
| 5318 | eol))))) | ||
| 5319 | (python--imenu-treesit-create-index-1 tree))) | ||
| 5320 | |||
| 5321 | (defun python-imenu-treesit-create-flat-index () | ||
| 5302 | "Return flat outline of the current Python buffer for Imenu. | 5322 | "Return flat outline of the current Python buffer for Imenu. |
| 5303 | 5323 | ||
| 5304 | Change `python-imenu-format-item-label-function', | 5324 | Change `python-imenu-format-item-label-function', |
| @@ -5309,7 +5329,7 @@ customize how labels are formatted. | |||
| 5309 | Similar to `python-imenu-create-flat-index' but use | 5329 | Similar to `python-imenu-create-flat-index' but use |
| 5310 | tree-sitter." | 5330 | tree-sitter." |
| 5311 | (python-imenu-create-flat-index | 5331 | (python-imenu-create-flat-index |
| 5312 | (python--imenu-treesit-create-index))) | 5332 | (python-imenu-treesit-create-index))) |
| 5313 | 5333 | ||
| 5314 | ;;; Misc helpers | 5334 | ;;; Misc helpers |
| 5315 | 5335 | ||
| @@ -6120,14 +6140,18 @@ REPORT-FN is Flymake's callback function." | |||
| 6120 | (add-hook 'post-self-insert-hook | 6140 | (add-hook 'post-self-insert-hook |
| 6121 | #'python-indent-post-self-insert-function 'append 'local) | 6141 | #'python-indent-post-self-insert-function 'append 'local) |
| 6122 | 6142 | ||
| 6123 | (setq-local imenu-create-index-function | 6143 | (if (and python-use-tree-sitter |
| 6124 | #'python-imenu-create-index) | 6144 | (treesit-can-enable-p)) |
| 6145 | (setq-local imenu-create-index-function | ||
| 6146 | #'python-treesit-imenu-create-index) | ||
| 6147 | (setq-local imenu-create-index-function | ||
| 6148 | #'python-imenu-create-index)) | ||
| 6125 | 6149 | ||
| 6126 | (setq-local add-log-current-defun-function | 6150 | (setq-local add-log-current-defun-function |
| 6127 | #'python-info-current-defun) | 6151 | #'python-info-current-defun) |
| 6128 | 6152 | ||
| 6129 | (if (and python-use-tree-sitter | 6153 | (if (and python-use-tree-sitter |
| 6130 | (treesit-can-enable-p)) | 6154 | (treesit-can-enable-p)) |
| 6131 | (add-hook 'which-func-functions | 6155 | (add-hook 'which-func-functions |
| 6132 | #'python-info-treesit-current-defun nil t) | 6156 | #'python-info-treesit-current-defun nil t) |
| 6133 | (add-hook 'which-func-functions #'python-info-current-defun nil t)) | 6157 | (add-hook 'which-func-functions #'python-info-current-defun nil t)) |