Add 'and', 'named', and 'anonymous' predicate for tree-sitter

* doc/lispref/parsing.texi (User-defined Things): Mention the
new predicate.
* src/treesit.c (treesit_traverse_validate_predicate): Recognize
named, anonymous, and and predicates.
(treesit_traverse_match_predicate): Handle named, anonymous, and
and predicates.
This commit is contained in:
Yuan Fu 2025-01-12 23:41:47 -08:00
parent 4687fff4f0
commit f0e63558bd
No known key found for this signature in database
GPG key ID: 56E19BC57664A442
2 changed files with 38 additions and 8 deletions

View file

@ -1596,6 +1596,8 @@ the thing.
@var{pred} can also be recursively defined. It can be @w{@code{(or
@var{pred}@dots{})}}, meaning that satisfying any one of the @var{pred}s
qualifies the node as the thing. It can be @w{@code{(and
@var{pred}@dots{})}}, meaning that satisfying all of the @var{pred}s
qualifies the node as the thing. It can be @w{@code{(not @var{pred})}},
meaning that not satisfying @var{pred} qualifies the node.
@ -1604,6 +1606,10 @@ list. For example, @w{@code{(or sexp sentence)}} defines something
that's either a @code{sexp} thing or a @code{sentence} thing, as defined
by some other rule in the alist.
There are two pre-defined predicates: @code{named} and @code{anonymous},
that qualifies named and anonymous nodes, respectively. They can be
combined with @code{and} to narrow down the match.
Here's an example @code{treesit-thing-settings} for C and C++:
@example
@ -1662,7 +1668,6 @@ signals @code{treesit-invalid-predicate} error. If @var{ignore-missing}
is @code{t}, this function doesn't signal the error when @var{thing} is
undefined and just returns @code{nil}; but it still signals the error if
@var{thing} is a malformed predicate.
@end defun
@defun treesit-thing-prev position thing
@ -2179,8 +2184,6 @@ navigation commands that move, respectively, by sexps and sentences by
defining variables such as @code{forward-sexp-function} and
@code{forward-sentence-function}.
@end itemize
@c TODO: Add treesit-thing-settings stuff once we finalize it.
@end defun
For more information on these built-in tree-sitter features,

View file

@ -3623,6 +3623,9 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
return true;
else if (SYMBOLP (pred))
{
if (BASE_EQ (pred, Qnamed) || BASE_EQ (pred, Qanonymous))
return true;
Lisp_Object definition = treesit_traverse_get_predicate (pred,
language);
if (NILP (definition))
@ -3667,13 +3670,13 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
signal_data,
recursion_level + 1);
}
else if (BASE_EQ (car, Qor))
else if (BASE_EQ (car, Qor) || BASE_EQ (car, Qand))
{
if (!CONSP (cdr) || NILP (cdr))
{
*signal_data = list3 (Qtreesit_invalid_predicate,
build_string ("`or' must have a list "
"of patterns as "
build_string ("`or' or `and' must have "
"a list of patterns as "
"arguments "),
pred);
return false;
@ -3729,6 +3732,14 @@ treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred,
Lisp_Object lisp_node = make_treesit_node (parser, node);
return !NILP (CALLN (Ffuncall, pred, lisp_node));
}
else if (SYMBOLP (pred) && BASE_EQ (pred, Qnamed))
{
return ts_node_is_named (node);
}
else if (SYMBOLP (pred) && BASE_EQ (pred, Qanonymous))
{
return !ts_node_is_named (node);
}
else if (SYMBOLP (pred))
{
Lisp_Object language = XTS_PARSER (parser)->language_symbol;
@ -3755,6 +3766,16 @@ treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred,
}
return false;
}
else if (BASE_EQ (car, Qand))
{
FOR_EACH_TAIL (cdr)
{
if (!treesit_traverse_match_predicate (cursor, XCAR (cdr),
parser, named))
return false;
}
return true;
}
else if (STRINGP (car) && FUNCTIONP (cdr))
{
/* A bit of code duplication here, but should be fine. */
@ -4297,6 +4318,7 @@ syms_of_treesit (void)
DEFSYM (Qtreesit_compiled_query_p, "treesit-compiled-query-p");
DEFSYM (Qtreesit_query_p, "treesit-query-p");
DEFSYM (Qnamed, "named");
DEFSYM (Qanonymous, "anonymous");
DEFSYM (Qmissing, "missing");
DEFSYM (Qextra, "extra");
DEFSYM (Qoutdated, "outdated");
@ -4338,6 +4360,7 @@ syms_of_treesit (void)
DEFSYM (Qtreesit_thing_symbol, "treesit-thing-symbol");
DEFSYM (Qor, "or");
DEFSYM (Qand, "and");
#ifdef WINDOWSNT
DEFSYM (Qtree_sitter, "tree-sitter");
@ -4420,8 +4443,12 @@ cons (REGEXP . FN), which is a combination of a regexp and a predicate
function, and the node has to match both to qualify as the thing.
PRED can also be recursively defined. It can be (or PRED...), meaning
satisfying anyone of the inner PREDs qualifies the node; or (not
PRED), meaning not satisfying the inner PRED qualifies the node.
satisfying anyone of the inner PREDs qualifies the node; or (and
PRED...) meaning satisfying all of the inner PREDs qualifies the node;
or (not PRED), meaning not satisfying the inner PRED qualifies the node.
There are two pre-defined predicates, `named' and `anonymous`. They
match named nodes and anonymous nodes, respectively.
Finally, PRED can refer to other THINGs defined in this list by using
the symbol of that THING. For example, (or sexp sentence). */);