Use the treesit thing 'list' with symbol property 'treesit-thing-symbol'

* doc/lispref/parsing.texi (User-defined Things): Mention new
functions 'treesit-forward-list', 'treesit-down-list',
'treesit-up-list', 'treesit-show-paren-data' that use the thing
'list' with the symbol property 'treesit-thing-symbol' (bug#73404).

* lisp/treesit.el: Put the property 'treesit-thing-symbol'
on the symbol 'list'.
(treesit--forward-list-with-default, treesit-down-list)
(treesit-up-list, treesit-navigate-thing)
(treesit-show-paren-data--categorize, treesit-major-mode-setup):
Replace 'sexp-list' with 'list'.

* lisp/progmodes/c-ts-mode.el (c-ts-mode--thing-settings):
* lisp/progmodes/js.el (js-ts-mode):
* lisp/progmodes/ruby-ts-mode.el (ruby-ts-mode):
* lisp/progmodes/typescript-ts-mode.el (typescript-ts-base-mode)
(tsx-ts-mode):
* lisp/textmodes/html-ts-mode.el (html-ts-mode):
Replace 'sexp-list' with 'list'.

* src/treesit.c (treesit_traverse_validate_predicate)
(treesit_traverse_match_predicate): Check if the 'pred'
symbol has the property 'Qtreesit_thing_symbol'.
(syms_of_treesit): New symbol 'Qtreesit_thing_symbol'.
This commit is contained in:
Juri Linkov 2025-01-10 09:33:49 +02:00
parent 351b6ac16d
commit 42a5ac3b51
9 changed files with 45 additions and 34 deletions

View file

@ -1628,10 +1628,14 @@ exactly how C and C@t{++} modes define things.
Emacs builtin functions already make use some thing definitions.
Command @code{treesit-forward-sexp} uses the @code{sexp} definition if
major mode defines it; @code{treesit-forward-sentence} uses the
@code{sentence} definition. Defun movement functions like
@code{treesit-end-of-defun} uses the @code{defun} definition
(@code{defun} definition is overridden by
major mode defines it; @code{treesit-forward-list},
@code{treesit-down-list}, @code{treesit-up-list},
@code{treesit-show-paren-data} use the @code{list} definition (its
symbol @code{list} has the symbol property @code{treesit-thing-symbol}
to avoid ambiguity with the function that has the same name);
@code{treesit-forward-sentence} uses the @code{sentence} definition.
Defun movement functions like @code{treesit-end-of-defun} uses the
@code{defun} definition (@code{defun} definition is overridden by
@var{treesit-defun-type-regexp} for backward compatibility). Major
modes can also define @code{comment}, @code{string}, @code{text}
(generally comments and strings).

View file

@ -1024,15 +1024,15 @@ override flag by 'treesit-font-lock-setting-query',
'treesit-font-lock-setting-feature', 'treesit-font-lock-setting-enable',
and 'treesit-font-lock-setting-override'.
*** New treesit thing 'sexp-list'.
*** New treesit thing 'list'.
Unlike the existing thing 'sexp' that defines both lists and atoms,
'sexp-list' defines only lists to be navigated by 'forward-sexp'.
The new function 'treesit-forward-sexp-list' uses 'sexp-list'
'list' defines only lists to be navigated by 'forward-sexp'.
The new function 'treesit-forward-sexp-list' uses 'list'
to move across lists. But to move across atoms inside the list
it uses 'forward-sexp-default-function'.
*** New tree-sitter based functions for moving by sexp-lists.
If a major mode defines 'sexp-list' in 'treesit-thing-settings',
*** New tree-sitter based functions for moving by lists.
If a major mode defines 'list' in 'treesit-thing-settings',
tree-sitter setup for these modes sets 'forward-list-function' to
'treesit-forward-list', 'up-list-function' to 'treesit-up-list', and
'down-list-function' to 'treesit-down-list'. This enables the

View file

@ -1151,7 +1151,7 @@ if `c-ts-mode-emacs-sources-support' is non-nil."
`(;; It's more useful to include semicolons as sexp so
;; that users can move to the end of a statement.
(sexp (not ,(rx (or "{" "}" "[" "]" "(" ")" ","))))
(sexp-list
(list
,(regexp-opt '("preproc_params"
"preproc_parenthesized_expression"
"preproc_argument_list"

View file

@ -3876,7 +3876,7 @@ See `treesit-thing-settings' for more information.")
"Nodes that designate sexps in JavaScript.
See `treesit-thing-settings' for more information.")
(defvar js--treesit-sexp-list-nodes
(defvar js--treesit-list-nodes
'("export_clause"
"named_imports"
"statement_block"
@ -3941,7 +3941,7 @@ See `treesit-thing-settings' for more information.")
(setq-local treesit-thing-settings
`((javascript
(sexp ,(js--regexp-opt-symbol js--treesit-sexp-nodes))
(sexp-list ,(js--regexp-opt-symbol js--treesit-sexp-list-nodes))
(list ,(js--regexp-opt-symbol js--treesit-list-nodes))
(sentence ,(js--regexp-opt-symbol js--treesit-sentence-nodes))
(text ,(js--regexp-opt-symbol '("comment"
"string_fragment"))))))

View file

@ -1132,7 +1132,7 @@ leading double colon is not added."
(equal (treesit-node-type (treesit-node-child node 0))
"(")))
(defun ruby-ts--sexp-list-p (node)
(defun ruby-ts--list-p (node)
;; Distinguish between the named `unless' node and the
;; node with the same value of type.
(when (treesit-node-check node 'named)
@ -1213,7 +1213,7 @@ leading double colon is not added."
)
eol)
#'ruby-ts--sexp-p))
(sexp-list
(list
,(cons (rx
bol
(or
@ -1253,7 +1253,7 @@ leading double colon is not added."
"array"
"hash")
eol)
#'ruby-ts--sexp-list-p))
#'ruby-ts--list-p))
(text ,(lambda (node)
(or (member (treesit-node-type node)
'("comment" "string_content" "heredoc_content"))

View file

@ -482,7 +482,7 @@ See `treesit-thing-settings' for more information.")
"Nodes that designate sexps in TypeScript.
See `treesit-thing-settings' for more information.")
(defvar typescript-ts-mode--sexp-list-nodes
(defvar typescript-ts-mode--list-nodes
'("export_clause"
"named_imports"
"statement_block"
@ -536,7 +536,7 @@ This mode is intended to be inherited by concrete major modes."
(setq-local treesit-thing-settings
`((typescript
(sexp ,(regexp-opt typescript-ts-mode--sexp-nodes 'symbols))
(sexp-list ,(regexp-opt typescript-ts-mode--sexp-list-nodes
(list ,(regexp-opt typescript-ts-mode--list-nodes
'symbols))
(sentence ,(regexp-opt
typescript-ts-mode--sentence-nodes 'symbols))
@ -621,9 +621,9 @@ at least 3 (which is the default value)."
(sexp ,(regexp-opt
(append typescript-ts-mode--sexp-nodes
'("jsx"))))
(sexp-list ,(concat "^"
(list ,(concat "^"
(regexp-opt
(append typescript-ts-mode--sexp-list-nodes
(append typescript-ts-mode--list-nodes
'(
"jsx_element"
"jsx_self_closing_element"

View file

@ -117,7 +117,7 @@ Return nil if there is no name or if NODE is not a defun node."
"text"
"attribute"
"value")))
(sexp-list ,(regexp-opt '("element")) 'symbols)
(list ,(regexp-opt '("element")) 'symbols)
(sentence "tag")
(text ,(regexp-opt '("comment" "text"))))))

View file

@ -2420,6 +2420,9 @@ delimits medium sized statements in the source code. It is,
however, smaller in scope than sentences. This is used by
`treesit-forward-sexp' and friends.")
;; Avoid interpreting the symbol `list' as a function.
(put 'list 'treesit-thing-symbol t)
(defun treesit--scan-error (pred arg)
(when-let* ((parent (treesit-thing-at (point) pred t))
(boundary (treesit-node-child parent (if (> arg 0) -1 0))))
@ -2443,7 +2446,7 @@ What constitutes as text and source code sexp is determined
by `text' and `sexp' in `treesit-thing-settings'.
There is an alternative implementation in `treesit-forward-sexp-list'
that uses `sexp-list' in `treesit-thing-settings' to move only
that uses `list' in `treesit-thing-settings' to move only
across lists, whereas uses `forward-sexp-default-function' to move
across atoms (such as symbols or words) inside the list."
(interactive "^p")
@ -2475,7 +2478,7 @@ Fall back to DEFAULT-FUNCTION as long as it doesn't cross
the boundaries of the list.
ARG is described in the docstring of `forward-list'."
(let* ((pred (or treesit-sexp-type-regexp 'sexp-list))
(let* ((pred (or treesit-sexp-type-regexp 'list))
(arg (or arg 1))
(cnt arg)
(inc (if (> arg 0) 1 -1)))
@ -2515,7 +2518,7 @@ ARG is described in the docstring of `forward-list'."
Whereas `treesit-forward-sexp' moves across both lists and atoms
using `sexp' in `treesit-thing-settings', this function uses
`sexp-list' in `treesit-thing-settings' to move only across lists.
`list' in `treesit-thing-settings' to move only across lists.
But to move across atoms (such as symbols or words) inside the list
it uses `forward-sexp-default-function' as long as it doesn't go
outside of the boundaries of the current list.
@ -2526,7 +2529,7 @@ ARG is described in the docstring of `forward-sexp-function'."
(defun treesit-forward-list (&optional arg)
"Move forward across a list.
What constitutes a list is determined by `sexp-list' in
What constitutes a list is determined by `list' in
`treesit-thing-settings' that usually defines
parentheses-like expressions.
@ -2543,7 +2546,7 @@ ARG is described in the docstring of `forward-list-function'."
(defun treesit-down-list (&optional arg)
"Move forward down one level of parentheses.
What constitutes a level of parentheses is determined by
`sexp-list' in `treesit-thing-settings' that usually defines
`list' in `treesit-thing-settings' that usually defines
parentheses-like expressions.
This command is the tree-sitter variant of `down-list'
@ -2551,7 +2554,7 @@ redefined by the variable `down-list-function'.
ARG is described in the docstring of `down-list'."
(interactive "^p")
(let* ((pred 'sexp-list)
(let* ((pred 'list)
(arg (or arg 1))
(cnt arg)
(inc (if (> arg 0) 1 -1)))
@ -2583,7 +2586,7 @@ ARG is described in the docstring of `down-list'."
(defun treesit-up-list (&optional arg escape-strings no-syntax-crossing)
"Move forward out of one level of parentheses.
What constitutes a level of parentheses is determined by
`sexp-list' in `treesit-thing-settings' that usually defines
`list' in `treesit-thing-settings' that usually defines
parentheses-like expressions.
This command is the tree-sitter variant of `up-list'
@ -2591,7 +2594,7 @@ redefined by the variable `up-list-function'.
ARG is described in the docstring of `up-list'."
(interactive "^p")
(let* ((pred 'sexp-list)
(let* ((pred 'list)
(arg (or arg 1))
(cnt arg)
(inc (if (> arg 0) 1 -1)))
@ -3076,7 +3079,7 @@ function is called recursively."
(setq pos (funcall
advance
(cond ((and (null next) (null prev)
(not (eq thing 'sexp-list)))
(not (eq thing 'list)))
parent)
((> arg 0) next)
(t prev))))
@ -3420,9 +3423,9 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in
;;; Show paren mode
(defun treesit-show-paren-data--categorize (pos &optional end-p)
(let* ((pred 'sexp-list)
(let* ((pred 'list)
(parent (when (treesit-thing-defined-p
'sexp-list (treesit-language-at pos))
pred (treesit-language-at pos))
(treesit-parent-until
(treesit-node-at (if end-p (1- pos) pos)) pred)))
(first (when parent (treesit-node-child parent 0)))
@ -3594,7 +3597,7 @@ before calling this function."
(setq-local forward-sexp-function #'treesit-forward-sexp)
(setq-local transpose-sexps-function #'treesit-transpose-sexps))
(when (treesit-thing-defined-p 'sexp-list nil)
(when (treesit-thing-defined-p 'list nil)
(setq-local forward-sexp-function #'treesit-forward-sexp-list)
(setq-local forward-list-function #'treesit-forward-list)
(setq-local down-list-function #'treesit-down-list)

View file

@ -3618,7 +3618,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
}
if (STRINGP (pred))
return true;
else if (FUNCTIONP (pred))
else if (FUNCTIONP (pred)
&& !(SYMBOLP (pred) && !NILP (Fget (pred, Qtreesit_thing_symbol))))
return true;
else if (SYMBOLP (pred))
{
@ -3722,7 +3723,8 @@ treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred,
const char *type = ts_node_type (node);
return fast_c_string_match (pred, type, strlen (type)) >= 0;
}
else if (FUNCTIONP (pred))
else if (FUNCTIONP (pred)
&& !(SYMBOLP (pred) && !NILP (Fget (pred, Qtreesit_thing_symbol))))
{
Lisp_Object lisp_node = make_treesit_node (parser, node);
return !NILP (CALLN (Ffuncall, pred, lisp_node));
@ -4333,6 +4335,8 @@ syms_of_treesit (void)
DEFSYM (Qtreesit_invalid_predicate, "treesit-invalid-predicate");
DEFSYM (Qtreesit_predicate_not_found, "treesit-predicate-not-found");
DEFSYM (Qtreesit_thing_symbol, "treesit-thing-symbol");
DEFSYM (Qor, "or");
#ifdef WINDOWSNT