1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-27 07:37:33 +00:00

Add treesit-defun-name and friends

1. We now have treesit-defun-name, powered by
treesit-defun-name-function.
2. We now have treesit-add-log-current-defun, which powers
add-log-current-defun.
3. c-ts-mode updates its code to take advantage of these new features.
4. Manual updates.

* doc/lispref/parsing.texi (Tree-sitter major modes): Add manual for
new functions.
* lisp/progmodes/c-ts-mode.el (c-ts-mode--defun-name): New function.
(c-ts-mode--imenu-1): Extract out into c-ts-mode--defun-name.
(c-ts-base-mode): Setup treesit-defun-name-function.
* lisp/treesit.el (treesit-defun-name-function)
(treesit-add-log-defun-delimiter): New variables.
(treesit-defun-at-point)
(treesit-defun-name): New functions.
(treesit-major-mode-setup): Setup add-log-current-defun-function.
This commit is contained in:
Yuan Fu 2022-12-24 16:33:35 -08:00
parent 35c2ca2ca6
commit f8e219ebfa
No known key found for this signature in database
GPG Key ID: 56E19BC57664A442
3 changed files with 103 additions and 17 deletions

View File

@ -1727,6 +1727,9 @@ indentation.
If @code{treesit-defun-type-regexp} is non-@code{nil}, it sets up
navigation functions for @code{beginning-of-defun} and
@code{end-of-defun}.
@item
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
@end defun
@ -1737,6 +1740,41 @@ For more information of these built-in tree-sitter features,
For supporting mixing of multiple languages in a major mode,
@pxref{Multiple Languages}.
Besides @code{beginning-of-defun} and @code{end-of-defun}, Emacs
provides some additional functions for working with defuns:
@code{treesit-defun-at-point} returns the defun node at point, and
@code{treesit-defun-name} returns the name of a defun node.
@defun treesit-defun-at-point
This function returns the defun node at point, or @code{nil} if none
is found. It respects @code{treesit-defun-tactic}: it returns the
top-level defun if the value is @code{top-level}, and returns the
immediate enclosing defun if the value is @code{nested}.
This function requires @code{treesit-defun-type-regexp} to work. If
it is @code{nil}, this function simply returns @code{nil}.
@end defun
@defun treesit-defun-name node
This function returns the defun name of @var{node}. It returns
@code{nil} if there is no defun name for @var{node}, or if @var{node}
is not a defun node, or if @var{node} is @code{nil}.
The defun name is names like function name, class name, struct name,
etc.
If @code{treesit-defun-name-function} is @code{nil}, this function
always returns @code{nil}.
@end defun
@defvar treesit-defun-name-function
If non-@code{nil}, this variable should store a function that is
called with a node and returns the defun name of it. The function
should have the same semantic as @code{treesit-defun-name}: if the
node is not a defun node, or the node is a defun node but doesn't have
a name, or the node is @code{nil}, return @code{nil}.
@end defvar
@node Tree-sitter C API
@section Tree-sitter C API Correspondence

View File

@ -481,6 +481,25 @@ For NODE, OVERRIDE, START, and END, see
;;; Imenu
(defun c-ts-mode--defun-name (node)
"Return the name of the defun NODE.
Return nil if NODE is not a defun node, return an empty string if
NODE doesn't have a name."
(treesit-node-text
(pcase (treesit-node-type node)
("function_definition"
(treesit-node-child-by-field-name
(treesit-node-child-by-field-name node "declarator")
"declarator"))
("declaration"
(let ((child (treesit-node-child node -1 t)))
(pcase (treesit-node-type child)
("identifier" child)
(_ (treesit-node-child-by-field-name child "declarator")))))
("struct_specifier"
(treesit-node-child-by-field-name node "name")))
t))
(defun c-ts-mode--imenu-1 (node)
"Helper for `c-ts-mode--imenu'.
Find string representation for NODE and set marker, then recurse
@ -488,22 +507,7 @@ the subtrees."
(let* ((ts-node (car node))
(subtrees (mapcan #'c-ts-mode--imenu-1 (cdr node)))
(name (when ts-node
(treesit-node-text
(pcase (treesit-node-type ts-node)
("function_definition"
(treesit-node-child-by-field-name
(treesit-node-child-by-field-name
ts-node "declarator")
"declarator"))
("declaration"
(let ((child (treesit-node-child ts-node -1 t)))
(pcase (treesit-node-type child)
("identifier" child)
(_ (treesit-node-child-by-field-name
child "declarator")))))
("struct_specifier"
(treesit-node-child-by-field-name
ts-node "name"))))))
(treesit-defun-name ts-node)))
(marker (when ts-node
(set-marker (make-marker)
(treesit-node-start ts-node)))))
@ -682,6 +686,7 @@ ARG is passed to `fill-paragraph'."
"class_specifier"))
#'c-ts-mode--defun-valid-p))
(setq-local treesit-defun-skipper #'c-ts-mode--defun-skipper)
(setq-local treesit-defun-name-function #'c-ts-mode--defun-name)
;; Nodes like struct/enum/union_specifier can appear in
;; function_definitions, so we need to find the top-level node.

View File

@ -1612,6 +1612,17 @@ newline after a defun, or the beginning of a defun.
If the value is nil, no skipping is performed.")
(defvar-local treesit-defun-name-function nil
"A function called with a node and returns the name of it.
If the node is a defun node, return the defun name. E.g., the
function name of a function. If the node is not a defun node, or
the defun node doesn't have a name, or the node is nil, return
nil.")
(defvar-local treesit-add-log-defun-delimiter "."
"The delimiter used to connect several defun names.
This is used in `treesit-add-log-current-defun'.")
(defun treesit-beginning-of-defun (&optional arg)
"Move backward to the beginning of a defun.
@ -1885,6 +1896,34 @@ is `top-level', return the immediate parent defun if it is
(if (eq treesit-defun-tactic 'top-level)
(treesit--top-level-defun node regexp pred)
node)))
(defun treesit-defun-name (node)
"Return the defun name of NODE.
Return nil if there is no name, or if NODE is not a defun node,
or if NODE is nil.
If `treesit-defun-name-function' is nil, always return nil."
(when treesit-defun-name-function
(funcall treesit-defun-name-function node)))
(defun treesit-add-log-current-defun ()
"Return the name of the defun at point.
Used for `add-log-current-defun-function'.
The delimiter between nested defun names is controlled by
`treesit-add-log-defun-delimiter'."
(let ((node (treesit-defun-at-point))
(name nil))
(while node
(when-let ((new-name (treesit-defun-name node)))
(if name
(setq name (concat new-name
treesit-add-log-defun-delimiter
name))
(setq name new-name)))
(setq node (treesit-node-parent node)))
name))
;;; Activating tree-sitter
@ -1979,7 +2018,11 @@ before calling this function."
;; the variables. In future we should update `end-of-defun' to
;; work with nested defuns.
(setq-local beginning-of-defun-function #'treesit-beginning-of-defun)
(setq-local end-of-defun-function #'treesit-end-of-defun)))
(setq-local end-of-defun-function #'treesit-end-of-defun))
;; Defun name.
(when treesit-defun-name-function
(setq-local add-log-current-defun-function
#'treesit-add-log-current-defun)))
;;; Debugging