mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-05 11:45:45 +00:00
Add tree-sitter helper functions for Imenu
We didn't add an integration for Imenu because we aren't sure what should it look like. Now we have a pretty good idea. All the major modes copy-paste the two Imenu functions and tweaks them in a standard way. With the addition of treesit-defun-type-regexp and treesit-defun-name-function, now is a good time to standardize Imenu integration. In the next commit we update all the major modes to use this integration. * doc/lispref/modes.texi (Imenu): Add manual. * doc/lispref/parsing.texi (Tree-sitter major modes): Update manual. * lisp/treesit.el (treesit-simple-imenu-settings): New varaible. (treesit--simple-imenu-1) (treesit-simple-imenu): New functions. (treesit-major-mode-setup): Setup Imenu.
This commit is contained in:
parent
ba1ddea9da
commit
b39dc7ab27
@ -2841,6 +2841,35 @@ function uses @code{imenu-generic-expression} instead.
|
||||
Setting this variable makes it buffer-local in the current buffer.
|
||||
@end defvar
|
||||
|
||||
If built with tree-sitter, Emacs can automatically generate an Imenu
|
||||
index if the major mode sets relevant variables.
|
||||
|
||||
@defvar treesit-simple-imenu-settings
|
||||
This variable instructs Emacs how to generate Imenu indexes. It
|
||||
should be a list of @w{(@var{category} @var{regexp} @var{pred}
|
||||
@var{name-fn})}.
|
||||
|
||||
@var{category} should be the name of a category, like "Function",
|
||||
"Class", etc. @var{regexp} should be a regexp matching the type of
|
||||
nodes that belong to @var{category}. @var{pred} should be either
|
||||
@code{nil} or a function that takes a node as the argument. It should
|
||||
return non-@code{nil} if the node is a valid node for @var{category},
|
||||
or @code{nil} if not.
|
||||
|
||||
@var{category} could also be @code{nil}. In which case the entries
|
||||
matched by @var{regexp} and @var{pred} are not grouped under
|
||||
@var{category}.
|
||||
|
||||
@var{name-fn} should be either @var{nil} or a function that takes a
|
||||
defun node and returns the name of that defun, e.g., the function name
|
||||
for a function definition. If @var{name-fn} is @var{nil},
|
||||
@code{treesit-defun-name} (@pxref{Tree-sitter major modes}) is used
|
||||
instead.
|
||||
|
||||
@code{treesit-major-mode-setup} (@pxref{Tree-sitter major modes})
|
||||
automatically sets up Imenu if this variable is non-@code{nil}.
|
||||
@end defvar
|
||||
|
||||
@node Font Lock Mode
|
||||
@section Font Lock Mode
|
||||
@cindex Font Lock mode
|
||||
|
@ -1738,6 +1738,11 @@ navigation functions for @code{beginning-of-defun} and
|
||||
If @code{treesit-defun-name-function} is non-@code{nil}, it sets up
|
||||
add-log functions used by @code{add-log-current-defun}.
|
||||
@end itemize
|
||||
|
||||
@item
|
||||
If @code{treesit-simple-imenu-settings} (@pxref{Imenu}) is
|
||||
non-@code{nil}, it sets up Imenu.
|
||||
@end itemize
|
||||
@end defun
|
||||
|
||||
For more information of these built-in tree-sitter features,
|
||||
|
@ -2009,6 +2009,91 @@ The delimiter between nested defun names is controlled by
|
||||
(setq node (treesit-node-parent node)))
|
||||
name))
|
||||
|
||||
;;; Imenu
|
||||
|
||||
(defvar treesit-simple-imenu-settings nil
|
||||
"Settings that configure `treesit-simple-imenu'.
|
||||
|
||||
It should be a list of (CATEGORY REGEXP PRED NAME-FN).
|
||||
|
||||
CATEGORY is the name of a category, like \"Function\", \"Class\",
|
||||
etc. REGEXP should be a regexp matching the type of nodes that
|
||||
belong to CATEGORY. PRED should be either nil or a function
|
||||
that takes a node an the argument. It should return non-nil if
|
||||
the node is a valid node for CATEGORY, or nil if not.
|
||||
|
||||
CATEGORY could also be nil. In that case the entries matched by
|
||||
REGEXP and PRED are not grouped under CATEGORY.
|
||||
|
||||
NAME-FN should be either nil or a function that takes a defun
|
||||
node and returns the name of that defun node. If NAME-FN is nil,
|
||||
`treesit-defun-name' is used.
|
||||
|
||||
`treesit-major-mode-setup' automatically sets up Imenu if this
|
||||
variable is non-nil.")
|
||||
|
||||
(defun treesit--simple-imenu-1 (node pred name-fn)
|
||||
"Given a sparse tree, create an Imenu index.
|
||||
|
||||
NODE is a node in the tree returned by
|
||||
`treesit-induce-sparse-tree' (not a tree-sitter node, its car is
|
||||
a tree-sitter node). Walk that tree and return an Imenu index.
|
||||
|
||||
Return a list of ENTRYs where
|
||||
|
||||
ENTRY := (NAME . MARKER)
|
||||
| (NAME . ((\" \" . MARKER)
|
||||
ENTRY
|
||||
...)
|
||||
|
||||
PRED and NAME-FN are the same as described in
|
||||
`treesit-simple-imenu-settings'. NAME-FN computes NAME in an
|
||||
ENTRY. MARKER marks the start of each tree-sitter node."
|
||||
(let* ((ts-node (car node))
|
||||
(children (cdr node))
|
||||
(subtrees (mapcan (lambda (node)
|
||||
(treesit--simple-imenu-1 node pred name-fn))
|
||||
children))
|
||||
;; The root of the tree could have a nil ts-node.
|
||||
(name (when ts-node
|
||||
(or (if name-fn
|
||||
(funcall name-fn ts-node)
|
||||
(treesit-defun-name ts-node))
|
||||
"Anonymous")))
|
||||
(marker (when ts-node
|
||||
(set-marker (make-marker)
|
||||
(treesit-node-start ts-node)))))
|
||||
(cond
|
||||
;; The tree-sitter node in the root node of the tree returned by
|
||||
;; `treesit-induce-sparse-tree' is often nil.
|
||||
((null ts-node)
|
||||
subtrees)
|
||||
;; This tree-sitter node is not a valid entry, skip it.
|
||||
((and pred (not (funcall pred ts-node)))
|
||||
subtrees)
|
||||
;; Non-leaf node, return a (list of) subgroup.
|
||||
(subtrees
|
||||
`((,name
|
||||
,(cons " " marker)
|
||||
,@subtrees)))
|
||||
;; Leaf node, return a (list of) plain index entry.
|
||||
(t (list (cons name marker))))))
|
||||
|
||||
(defun treesit-simple-imenu ()
|
||||
"Return an Imenu index for the current buffer."
|
||||
(let ((root (treesit-buffer-root-node)))
|
||||
(mapcan (lambda (setting)
|
||||
(pcase-let ((`(,category ,regexp ,pred ,name-fn)
|
||||
setting))
|
||||
(when-let* ((tree (treesit-induce-sparse-tree
|
||||
root regexp))
|
||||
(index (treesit--simple-imenu-1
|
||||
tree pred name-fn)))
|
||||
(if category
|
||||
(list (cons category index))
|
||||
index))))
|
||||
treesit-simple-imenu-settings)))
|
||||
|
||||
;;; Activating tree-sitter
|
||||
|
||||
(defun treesit-ready-p (language &optional quiet)
|
||||
@ -2066,6 +2151,11 @@ If `treesit-simple-indent-rules' is non-nil, setup indentation.
|
||||
If `treesit-defun-type-regexp' is non-nil, setup
|
||||
`beginning/end-of-defun' functions.
|
||||
|
||||
If `treesit-defun-name-function' is non-nil, setup
|
||||
`add-log-current-defun'.
|
||||
|
||||
If `treesit-simple-imenu-settings' is non-nil, setup Imenu.
|
||||
|
||||
Make sure necessary parsers are created for the current buffer
|
||||
before calling this function."
|
||||
;; Font-lock.
|
||||
@ -2106,7 +2196,11 @@ before calling this function."
|
||||
;; Defun name.
|
||||
(when treesit-defun-name-function
|
||||
(setq-local add-log-current-defun-function
|
||||
#'treesit-add-log-current-defun)))
|
||||
#'treesit-add-log-current-defun))
|
||||
;; Imenu.
|
||||
(when treesit-simple-imenu-settings
|
||||
(setq-local imenu-create-index-function
|
||||
#'treesit-simple-imenu)))
|
||||
|
||||
;;; Debugging
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user