Add new tactic to treesit-navigate-thing (bug#78703)

* doc/emacs/programs.texi (Moving by Defuns):
* doc/lispref/parsing.texi (User-defined Things): Describe the
new tactic.
* lisp/treesit.el (treesit-navigate-thing): Add new tactic.
This commit is contained in:
Yuan Fu 2025-06-09 21:41:17 -07:00
parent 1009e3d1fd
commit 0a629abfbb
No known key found for this signature in database
GPG key ID: 56E19BC57664A442
3 changed files with 36 additions and 25 deletions

View file

@ -267,13 +267,17 @@ bindings for that purpose.
@cindex nested defuns
@vindex treesit-defun-tactic
Some programming languages supported @dfn{nested defuns}, whereby a
defun (such as a function or a method or a class) can be defined
inside (i.e., as part of the body) of another defun. The commands
described above by default find the beginning and the end of the
@emph{innermost} defun around point. Major modes based on the
tree-sitter library provide control of this behavior: if the variable
@code{treesit-defun-tactic} is set to the value @code{top-level}, the
defun commands will find the @emph{outermost} defuns instead.
defun (such as a function or a method or a class) can be defined inside
(i.e., as part of the body) of another defun. The commands described
above by default find the beginning and the end of the @emph{innermost}
defun around point. Major modes based on the tree-sitter library
provide control of this behavior: by default, the value of
@code{treesit-defun-tactic} is set to @code{nested}; if it's set
to@code{top-level}, the defun commands will find the @emph{outermost}
defuns instead; if the value is set to @code{parent-first}, the defun
command always tries to move out of the current enclosing defun rather
than moving to the previous or next defun around point; if there's no
enclosing defun, it moves to the previous or next defun.
@node Moving by Sentences
@subsection Moving by Sentences

View file

@ -1787,15 +1787,17 @@ Like in @code{treesit-thing-prev}, @var{thing} can be a thing symbol
defined in @code{treesit-thing-settings}, or a predicate.
@var{tactic} determines how this function moves between things. It can
be @code{nested}, @code{top-level}, @code{restricted}, or @code{nil}.
@code{nested} or @code{nil} means normal nested navigation: first try to
move across siblings; if there aren't any siblings left in the current
level, move to the parent, then its siblings, and so on.
@code{top-level} means only navigate across top-level things and ignore
nested things. @code{restricted} means movement is restricted within
the thing that encloses @var{position}, if there is such a thing. This
tactic is useful for commands that want to stop at the current nesting
level and not move up.
be @code{nested}, @code{top-level}, @code{restricted},
@code{parent-first}, or @code{nil}. @code{nested} or @code{nil} means
normal nested navigation: first try to move across siblings; if there
aren't any siblings left in the current level, move to the parent, then
its siblings, and so on. @code{top-level} means only navigate across
top-level things and ignore nested things. @code{restricted} means
movement is restricted within the thing that encloses @var{position}, if
there is such a thing. This tactic is useful for commands that want to
stop at the current nesting level and not move up. @var{parent-first}
means move to the parent if there is one; and move to siblings if
there's no parent.
@end defun
@defun treesit-thing-at position thing &optional strict

View file

@ -3695,15 +3695,15 @@ across, return nil.
THING can be a regexp, a predicate function, and more. See
`treesit-thing-settings' for details.
TACTIC determines how does this function move between things. It
can be `nested', `top-level', `restricted', or nil. `nested'
means normal nested navigation: try to move to siblings first,
and if there aren't enough siblings, move to the parent and its
siblings. `top-level' means only consider top-level things, and
nested things are ignored. `restricted' means movement is
restricted inside the thing that encloses POS (i.e., parent),
should there be one. If omitted, TACTIC is considered to be
`nested'.
TACTIC determines how does this function move between things. It can be
`nested', `top-level', `restricted', `parent-first' or nil. `nested'
means normal nested navigation: try to move to siblings first, and if
there aren't enough siblings, move to the parent and its siblings.
`top-level' means only consider top-level things, and nested things are
ignored. `restricted' means movement is restricted inside the thing
that encloses POS (i.e., parent), should there be one. `parent' means
move to the parent if there is one; and move to siblings if there's no
parent. If omitted, TACTIC is considered to be `nested'.
RECURSING is an internal parameter, if non-nil, it means this
function is called recursively."
@ -3737,6 +3737,11 @@ function is called recursively."
(setq parent (treesit-node-top-level parent thing t)
prev nil
next nil))
;; When PARENT is nil, `nested' and `parent-first' are the
;; same, if there is a PARENT, pretend there is no nested PREV
;; and NEXT so the following code moves to the parent.
(when (and (eq tactic 'parent-first) parent)
(setq prev nil next nil))
;; If TACTIC is `restricted', the implementation is simple.
;; In principle we don't go to parent's beg/end for
;; `restricted' tactic, but if the parent is a "leaf thing"