aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuan Fu2025-11-02 16:16:50 -0800
committerYuan Fu2025-11-02 17:11:55 -0800
commitb01435306a36e4e75671fbe7bacea351f89947d5 (patch)
tree702393061f1a049f87c892637365a0582170b136
parent68290e1a03ba4f264eab5d25960cf907b0c903fe (diff)
downloademacs-b01435306a36e4e75671fbe7bacea351f89947d5.tar.gz
emacs-b01435306a36e4e75671fbe7bacea351f89947d5.zip
Change tree-sitter query predicate names (bug#79687)
Latest tree-sitter library throws a syntax error if the predicate names in a query don't end with question mark. So we made the following change: :equal changed to :eq? :match changed to :match? :pred changed to :pred? Old names are transparently converted to new names when expanding patterns. :match predicate can now take the regexp and the node in any order: it'll figure out which is which automatically. This way it works with current Emacs convention (regexp first), as well as tree-sitter's match convention (regexp second). * doc/lispref/parsing.texi (Pattern Matching): Update manuel to use new predicate names. * src/treesit.c: (Ftreesit_pattern_expand): (Ftreesit_query_expand): (treesit_predicate_match): (treesit_eval_predicates): (syms_of_treesit): Use new predicate names. * test/src/treesit-tests.el (treesit-query-api): Update test.
-rw-r--r--doc/lispref/parsing.texi34
-rw-r--r--etc/NEWS12
-rw-r--r--src/treesit.c92
-rw-r--r--test/src/treesit-tests.el6
4 files changed, 84 insertions, 60 deletions
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
index 5734fcf8094..08f5c310a24 100644
--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi
@@ -1473,7 +1473,7 @@ example, with the following pattern:
1473@group 1473@group
1474( 1474(
1475 (array :anchor (_) @@first (_) @@last :anchor) 1475 (array :anchor (_) @@first (_) @@last :anchor)
1476 (:equal @@first @@last) 1476 (:eq? @@first @@last)
1477) 1477)
1478@end group 1478@end group
1479@end example 1479@end example
@@ -1482,24 +1482,32 @@ example, with the following pattern:
1482tree-sitter only matches arrays where the first element is equal to 1482tree-sitter only matches arrays where the first element is equal to
1483the last element. To attach a predicate to a pattern, we need to 1483the last element. To attach a predicate to a pattern, we need to
1484group them together. Currently there are three predicates: 1484group them together. Currently there are three predicates:
1485@code{:equal}, @code{:match}, and @code{:pred}. 1485@code{:eq?}, @code{:match?}, and @code{:pred?}.
1486 1486
1487@deffn Predicate :equal arg1 arg2 1487@deffn Predicate :eq? arg1 arg2
1488Matches if @var{arg1} is equal to @var{arg2}. Arguments can be either 1488Matches if @var{arg1} is equal to @var{arg2}. Arguments can be either
1489strings or capture names. Capture names represent the text that the 1489strings or capture names. Capture names represent the text that the
1490captured node spans in the buffer. 1490captured node spans in the buffer. Note that this is more like
1491@code{equal} in Elisp, but @code{eq?} is the convention used by
1492tree-sitter. Previously we supported the @code{:equal} predicate but
1493it's now considered deprecated.
1491@end deffn 1494@end deffn
1492 1495
1493@deffn Predicate :match regexp capture-name 1496@deffn Predicate :match? capture-name regexp
1494Matches if the text that @var{capture-name}'s node spans in the buffer 1497Matches if the text that @var{capture-name}'s node spans in the buffer
1495matches regular expression @var{regexp}, given as a string literal. 1498matches regular expression @var{regexp}, given as a string literal.
1496Matching is case-sensitive. 1499Matching is case-sensitive. The ordering of the arguments doesn't
1500matter. Previously we supported the @code{:match} predicate but it's
1501now considered deprecated.
1497@end deffn 1502@end deffn
1498 1503
1499@deffn Predicate :pred fn &rest nodes 1504@deffn Predicate :pred? fn &rest nodes
1500Matches if function @var{fn} returns non-@code{nil} when passed each 1505Matches if function @var{fn} returns non-@code{nil} when passed each
1501node in @var{nodes} as arguments. The function runs with the current 1506node in @var{nodes} as arguments. The function runs with the current
1502buffer set to the buffer of node being queried. 1507buffer set to the buffer of node being queried. Be very careful when
1508using this predicate, since it can be expensive when used in a tight
1509loop. Previously we supported the @code{:pred} predicate but it's now
1510considered deprecated.
1503@end deffn 1511@end deffn
1504 1512
1505Note that a predicate can only refer to capture names that appear in 1513Note that a predicate can only refer to capture names that appear in
@@ -1554,9 +1562,9 @@ Anchor @code{:anchor} is written as @samp{.}.
1554@item 1562@item
1555@samp{:+} is written as @samp{+}. 1563@samp{:+} is written as @samp{+}.
1556@item 1564@item
1557@code{:equal}, @code{:match} and @code{:pred} are written as 1565@code{:eq?}, @code{:match?} and @code{:pred?} are written as
1558@code{#equal}, @code{#match} and @code{#pred}, respectively. 1566@code{#eq?}, @code{#match?} and @code{#pred?}, respectively. In
1559In general, predicates change their @samp{:} to @samp{#}. 1567general, predicates change the @samp{:} to @samp{#}.
1560@end itemize 1568@end itemize
1561 1569
1562For example, 1570For example,
@@ -1565,7 +1573,7 @@ For example,
1565@group 1573@group
1566'(( 1574'((
1567 (compound_expression :anchor (_) @@first (_) :* @@rest) 1575 (compound_expression :anchor (_) @@first (_) :* @@rest)
1568 (:match "love" @@first) 1576 (:match? "love" @@first)
1569 )) 1577 ))
1570@end group 1578@end group
1571@end example 1579@end example
@@ -1577,7 +1585,7 @@ is written in string form as
1577@group 1585@group
1578"( 1586"(
1579 (compound_expression . (_) @@first (_)* @@rest) 1587 (compound_expression . (_) @@first (_)* @@rest)
1580 (#match \"love\" @@first) 1588 (#match? \"love\" @@first)
1581 )" 1589 )"
1582@end group 1590@end group
1583@end example 1591@end example
diff --git a/etc/NEWS b/etc/NEWS
index 63ea0b5a11f..d3eff6991dd 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1049,6 +1049,18 @@ Now 'treesit-explore-mode' (or 'treesit-explore') prompts for a parser
1049rather than a language, and it is now possible to select a local parser 1049rather than a language, and it is now possible to select a local parser
1050at point to explore. 1050at point to explore.
1051 1051
1052+++
1053*** Tree-sitter query predicate :equal, :match, and :pred are deprecated
1054Use :eq?, :match?, :pred? instead. The change is because newer
1055tree-sitter library mandates query predicates to end with question mark.
1056Emacs will transparently converts :equal, :match and :pred to :eq?,
1057:match? and :pred?, respectively, so existing queries still work fine
1058with latest tree-sitter library. Predicate :equal is changed to :eq? to
1059better follow tree-sitter’s convention. Also, the :match? predicates
1060can now take the regexp as either the first or second argument, so it
1061works with both tree-sitter convention (regexp arg second) and Emacs
1062convention (regexp arg first).
1063
1052** Hideshow 1064** Hideshow
1053 1065
1054+++ 1066+++
diff --git a/src/treesit.c b/src/treesit.c
index 69751b5ea10..3230d0a50a1 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -490,17 +490,17 @@ static Lisp_Object Vtreesit_str_dot;
490static Lisp_Object Vtreesit_str_question_mark; 490static Lisp_Object Vtreesit_str_question_mark;
491static Lisp_Object Vtreesit_str_star; 491static Lisp_Object Vtreesit_str_star;
492static Lisp_Object Vtreesit_str_plus; 492static Lisp_Object Vtreesit_str_plus;
493static Lisp_Object Vtreesit_str_pound_equal; 493static Lisp_Object Vtreesit_str_pound_eq_question_mark;
494static Lisp_Object Vtreesit_str_pound_match; 494static Lisp_Object Vtreesit_str_pound_match_question_mark;
495static Lisp_Object Vtreesit_str_pound_pred; 495static Lisp_Object Vtreesit_str_pound_pred_question_mark;
496static Lisp_Object Vtreesit_str_open_bracket; 496static Lisp_Object Vtreesit_str_open_bracket;
497static Lisp_Object Vtreesit_str_close_bracket; 497static Lisp_Object Vtreesit_str_close_bracket;
498static Lisp_Object Vtreesit_str_open_paren; 498static Lisp_Object Vtreesit_str_open_paren;
499static Lisp_Object Vtreesit_str_close_paren; 499static Lisp_Object Vtreesit_str_close_paren;
500static Lisp_Object Vtreesit_str_space; 500static Lisp_Object Vtreesit_str_space;
501static Lisp_Object Vtreesit_str_equal; 501static Lisp_Object Vtreesit_str_eq_question_mark;
502static Lisp_Object Vtreesit_str_match; 502static Lisp_Object Vtreesit_str_match_question_mark;
503static Lisp_Object Vtreesit_str_pred; 503static Lisp_Object Vtreesit_str_pred_question_mark;
504static Lisp_Object Vtreesit_str_empty; 504static Lisp_Object Vtreesit_str_empty;
505 505
506/* This is the limit on recursion levels for some tree-sitter 506/* This is the limit on recursion levels for some tree-sitter
@@ -3471,12 +3471,12 @@ See Info node `(elisp)Pattern Matching' for detailed explanation. */)
3471 return Vtreesit_str_star; 3471 return Vtreesit_str_star;
3472 if (BASE_EQ (pattern, QCplus)) 3472 if (BASE_EQ (pattern, QCplus))
3473 return Vtreesit_str_plus; 3473 return Vtreesit_str_plus;
3474 if (BASE_EQ (pattern, QCequal)) 3474 if (BASE_EQ (pattern, QCequal) || BASE_EQ (pattern, QCeq_q))
3475 return Vtreesit_str_pound_equal; 3475 return Vtreesit_str_pound_eq_question_mark;
3476 if (BASE_EQ (pattern, QCmatch)) 3476 if (BASE_EQ (pattern, QCmatch) || BASE_EQ (pattern, QCmatch_q))
3477 return Vtreesit_str_pound_match; 3477 return Vtreesit_str_pound_match_question_mark;
3478 if (BASE_EQ (pattern, QCpred)) 3478 if (BASE_EQ (pattern, QCpred) || BASE_EQ (pattern, QCpred_q))
3479 return Vtreesit_str_pound_pred; 3479 return Vtreesit_str_pound_pred_question_mark;
3480 Lisp_Object opening_delimeter 3480 Lisp_Object opening_delimeter
3481 = VECTORP (pattern) 3481 = VECTORP (pattern)
3482 ? Vtreesit_str_open_bracket : Vtreesit_str_open_paren; 3482 ? Vtreesit_str_open_bracket : Vtreesit_str_open_paren;
@@ -3507,7 +3507,9 @@ A PATTERN in QUERY can be
3507 :* 3507 :*
3508 :+ 3508 :+
3509 :equal 3509 :equal
3510 :eq?
3510 :match 3511 :match
3512 :match?
3511 (TYPE PATTERN...) 3513 (TYPE PATTERN...)
3512 [PATTERN...] 3514 [PATTERN...]
3513 FIELD-NAME: 3515 FIELD-NAME:
@@ -3670,7 +3672,7 @@ treesit_predicate_equal (Lisp_Object args, struct capture_range captures,
3670 return !NILP (Fstring_equal (text1, text2)); 3672 return !NILP (Fstring_equal (text1, text2));
3671} 3673}
3672 3674
3673/* Handles predicate (#match "regexp" @node). Return true if "regexp" 3675/* Handles predicate (#match? "regexp" @node). Return true if "regexp"
3674 matches the text spanned by @node; return false otherwise. 3676 matches the text spanned by @node; return false otherwise.
3675 Matching is case-sensitive. If everything goes fine, don't touch 3677 Matching is case-sensitive. If everything goes fine, don't touch
3676 SIGNAL_DATA; if error occurs, set it to a suitable signal data. */ 3678 SIGNAL_DATA; if error occurs, set it to a suitable signal data. */
@@ -3680,26 +3682,25 @@ treesit_predicate_match (Lisp_Object args, struct capture_range captures,
3680{ 3682{
3681 if (list_length (args) != 2) 3683 if (list_length (args) != 2)
3682 { 3684 {
3683 *signal_data = list2 (build_string ("Predicate `match' requires two " 3685 *signal_data = list2 (build_string ("Predicate `match?' requires two "
3684 "arguments but got"), 3686 "arguments but got"),
3685 Flength (args)); 3687 Flength (args));
3686 return false; 3688 return false;
3687 } 3689 }
3688 Lisp_Object regexp = XCAR (args); 3690 Lisp_Object arg1 = XCAR (args);
3689 Lisp_Object capture_name = XCAR (XCDR (args)); 3691 Lisp_Object arg2 = XCAR (XCDR (args));
3690 3692 Lisp_Object regexp = SYMBOLP (arg2) ? arg1 : arg2;
3691 /* It's probably common to get the argument order backwards. Catch 3693 Lisp_Object capture_name = SYMBOLP (arg2) ? arg2 : arg1;
3692 this mistake early and show helpful explanation, because Emacs 3694
3693 loves you. (We put the regexp first because that's what 3695 if (!STRINGP (regexp) || !SYMBOLP (capture_name))
3694 string-match does.) */ 3696 {
3695 if (!STRINGP (regexp)) 3697 *signal_data = list2 (build_string ("Predicate `match?' takes a regexp "
3696 xsignal1 (Qtreesit_query_error, 3698 "and a node capture (order doesn't "
3697 build_string ("The first argument to `match' should " 3699 "matter), but got"),
3698 "be a regexp string, not a capture name")); 3700 Flength (args));
3699 if (!SYMBOLP (capture_name)) 3701 return false;
3700 xsignal1 (Qtreesit_query_error, 3702 }
3701 build_string ("The second argument to `match' should " 3703
3702 "be a capture name, not a string"));
3703 3704
3704 Lisp_Object node = Qnil; 3705 Lisp_Object node = Qnil;
3705 if (!treesit_predicate_capture_name_to_node (capture_name, captures, &node, 3706 if (!treesit_predicate_capture_name_to_node (capture_name, captures, &node,
@@ -3783,11 +3784,11 @@ treesit_eval_predicates (struct capture_range captures, Lisp_Object predicates,
3783 Lisp_Object predicate = XCAR (tail); 3784 Lisp_Object predicate = XCAR (tail);
3784 Lisp_Object fn = XCAR (predicate); 3785 Lisp_Object fn = XCAR (predicate);
3785 Lisp_Object args = XCDR (predicate); 3786 Lisp_Object args = XCDR (predicate);
3786 if (!NILP (Fstring_equal (fn, Vtreesit_str_equal))) 3787 if (!NILP (Fstring_equal (fn, Vtreesit_str_eq_question_mark)))
3787 pass &= treesit_predicate_equal (args, captures, signal_data); 3788 pass &= treesit_predicate_equal (args, captures, signal_data);
3788 else if (!NILP (Fstring_equal (fn, Vtreesit_str_match))) 3789 else if (!NILP (Fstring_equal (fn, Vtreesit_str_match_question_mark)))
3789 pass &= treesit_predicate_match (args, captures, signal_data); 3790 pass &= treesit_predicate_match (args, captures, signal_data);
3790 else if (!NILP (Fstring_equal (fn, Vtreesit_str_pred))) 3791 else if (!NILP (Fstring_equal (fn, Vtreesit_str_pred_question_mark)))
3791 pass &= treesit_predicate_pred (args, captures, signal_data); 3792 pass &= treesit_predicate_pred (args, captures, signal_data);
3792 else 3793 else
3793 { 3794 {
@@ -5175,8 +5176,11 @@ syms_of_treesit (void)
5175 DEFSYM (QCstar, ":*"); 5176 DEFSYM (QCstar, ":*");
5176 DEFSYM (QCplus, ":+"); 5177 DEFSYM (QCplus, ":+");
5177 DEFSYM (QCequal, ":equal"); 5178 DEFSYM (QCequal, ":equal");
5179 DEFSYM (QCeq_q, ":eq?");
5178 DEFSYM (QCmatch, ":match"); 5180 DEFSYM (QCmatch, ":match");
5181 DEFSYM (QCmatch_q, ":match?");
5179 DEFSYM (QCpred, ":pred"); 5182 DEFSYM (QCpred, ":pred");
5183 DEFSYM (QCpred_q, ":pred?");
5180 DEFSYM (QCline, ":line"); 5184 DEFSYM (QCline, ":line");
5181 DEFSYM (QCcol, ":col"); 5185 DEFSYM (QCcol, ":col");
5182 DEFSYM (QCpos, ":pos"); 5186 DEFSYM (QCpos, ":pos");
@@ -5357,12 +5361,12 @@ depending on customization of `treesit-enabled-modes'. */);
5357 Vtreesit_str_star = build_string ("*"); 5361 Vtreesit_str_star = build_string ("*");
5358 staticpro (&Vtreesit_str_plus); 5362 staticpro (&Vtreesit_str_plus);
5359 Vtreesit_str_plus = build_string ("+"); 5363 Vtreesit_str_plus = build_string ("+");
5360 staticpro (&Vtreesit_str_pound_equal); 5364 staticpro (&Vtreesit_str_pound_eq_question_mark);
5361 Vtreesit_str_pound_equal = build_string ("#equal"); 5365 Vtreesit_str_pound_eq_question_mark = build_string ("#eq?");
5362 staticpro (&Vtreesit_str_pound_match); 5366 staticpro (&Vtreesit_str_pound_match_question_mark);
5363 Vtreesit_str_pound_match = build_string ("#match"); 5367 Vtreesit_str_pound_match_question_mark = build_string ("#match?");
5364 staticpro (&Vtreesit_str_pound_pred); 5368 staticpro (&Vtreesit_str_pound_pred_question_mark);
5365 Vtreesit_str_pound_pred = build_string ("#pred"); 5369 Vtreesit_str_pound_pred_question_mark = build_string ("#pred?");
5366 staticpro (&Vtreesit_str_open_bracket); 5370 staticpro (&Vtreesit_str_open_bracket);
5367 Vtreesit_str_open_bracket = build_string ("["); 5371 Vtreesit_str_open_bracket = build_string ("[");
5368 staticpro (&Vtreesit_str_close_bracket); 5372 staticpro (&Vtreesit_str_close_bracket);
@@ -5373,12 +5377,12 @@ depending on customization of `treesit-enabled-modes'. */);
5373 Vtreesit_str_close_paren = build_string (")"); 5377 Vtreesit_str_close_paren = build_string (")");
5374 staticpro (&Vtreesit_str_space); 5378 staticpro (&Vtreesit_str_space);
5375 Vtreesit_str_space = build_string (" "); 5379 Vtreesit_str_space = build_string (" ");
5376 staticpro (&Vtreesit_str_equal); 5380 staticpro (&Vtreesit_str_eq_question_mark);
5377 Vtreesit_str_equal = build_string ("equal"); 5381 Vtreesit_str_eq_question_mark = build_string ("eq?");
5378 staticpro (&Vtreesit_str_match); 5382 staticpro (&Vtreesit_str_match_question_mark);
5379 Vtreesit_str_match = build_string ("match"); 5383 Vtreesit_str_match_question_mark = build_string ("match?");
5380 staticpro (&Vtreesit_str_pred); 5384 staticpro (&Vtreesit_str_pred_question_mark);
5381 Vtreesit_str_pred = build_string ("pred"); 5385 Vtreesit_str_pred_question_mark = build_string ("pred?");
5382 staticpro (&Vtreesit_str_empty); 5386 staticpro (&Vtreesit_str_empty);
5383 Vtreesit_str_empty = build_string (""); 5387 Vtreesit_str_empty = build_string ("");
5384 5388
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
index b5ea63a53f3..89303114735 100644
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el
@@ -547,10 +547,10 @@ BODY is the test body."
547 ;; String query. 547 ;; String query.
548 '("(string) @string 548 '("(string) @string
549(pair key: (_) @keyword) 549(pair key: (_) @keyword)
550((_) @bob (#match \"\\\\`B.b\\\\'\" @bob)) 550((_) @bob (#match? \"\\\\`B.b\\\\'\" @bob))
551(number) @number 551(number) @number
552((number) @n3 (#equal \"3\" @n3)) 552((number) @n3 (#eq? \"3\" @n3))
553((number) @n3p (#pred treesit--ert-pred-last-sibling @n3p))" 553((number) @n3p (#pred? treesit--ert-pred-last-sibling @n3p))"
554 ;; Sexp query. 554 ;; Sexp query.
555 ((string) @string 555 ((string) @string
556 (pair key: (_) @keyword) 556 (pair key: (_) @keyword)