Package: emacs
Version: 1:30.2+1-6
Severity: important

The tree-sitter patch included in 1:30.2+1-4 fixed building, but the
tree-sitter query supported in Emacs 30.2 is incompatible with
tree-sitter 0.26+. More details in [1].

I have backported the suggested patch from upstream[2], and adapted to
the Debian version. Tested in a docker image that the syntax
high-lighting is working now (tried source code in C, C++, Python, and
Java). Please find in the attachment.

(Note: I'm using important severity to avoid blocking Emacs migration,
which may further delay the tree-sitter transition. Please feel free to
adjust.)

[1] https://github.com/doomemacs/core/issues/8746#issuecomment-4233133006
[2] 
https://gitlab.archlinux.org/archlinux/packaging/packages/emacs/-/blob/30.2-3/02_all_ts-query-pred.patch?ref_type=tags

-- System Information:
Debian Release: forky/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing'), (200, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 7.0.13+deb14-amd64 (SMP w/2 CPU threads; PREEMPT)
Locale: LANG=C.UTF-8, LC_CTYPE=C.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages emacs depends on:
ii  emacs-gtk  1:30.2+1-6

emacs recommends no packages.

emacs suggests no packages.

-- no debconf information

-- 
Regards,
Xiyue Deng
From 7c7e3af01d57bdb869d469dc102cb957eaf7b322 Mon Sep 17 00:00:00 2001
From: Yuan Fu <[email protected]>
Date: Sun, 2 Nov 2025 16:16:50 -0800
Subject: [PATCH] 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.
---
 src/treesit.c             | 101 +++++++++++++++++++++-----------------
 test/src/treesit-tests.el |   6 +--
 2 files changed, 58 insertions(+), 49 deletions(-)

diff --git a/src/treesit.c b/src/treesit.c
index f87f4654209..bf212639078 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -431,17 +431,17 @@ #define ts_tree_root_node fn_ts_tree_root_node
 static Lisp_Object Vtreesit_str_question_mark;
 static Lisp_Object Vtreesit_str_star;
 static Lisp_Object Vtreesit_str_plus;
-static Lisp_Object Vtreesit_str_pound_equal;
-static Lisp_Object Vtreesit_str_pound_match;
-static Lisp_Object Vtreesit_str_pound_pred;
+static Lisp_Object Vtreesit_str_pound_eq_question_mark;
+static Lisp_Object Vtreesit_str_pound_match_question_mark;
+static Lisp_Object Vtreesit_str_pound_pred_question_mark;
 static Lisp_Object Vtreesit_str_open_bracket;
 static Lisp_Object Vtreesit_str_close_bracket;
 static Lisp_Object Vtreesit_str_open_paren;
 static Lisp_Object Vtreesit_str_close_paren;
 static Lisp_Object Vtreesit_str_space;
-static Lisp_Object Vtreesit_str_equal;
-static Lisp_Object Vtreesit_str_match;
-static Lisp_Object Vtreesit_str_pred;
+static Lisp_Object Vtreesit_str_eq_question_mark;
+static Lisp_Object Vtreesit_str_match_question_mark;
+static Lisp_Object Vtreesit_str_pred_question_mark;
 static Lisp_Object Vtreesit_str_empty;
 
 /* This is the limit on recursion levels for some tree-sitter
@@ -2636,12 +2636,12 @@ DEFUN ("treesit-pattern-expand",
     return Vtreesit_str_star;
   if (BASE_EQ (pattern, QCplus))
     return Vtreesit_str_plus;
-  if (BASE_EQ (pattern, QCequal))
-    return Vtreesit_str_pound_equal;
-  if (BASE_EQ (pattern, QCmatch))
-    return Vtreesit_str_pound_match;
-  if (BASE_EQ (pattern, QCpred))
-    return Vtreesit_str_pound_pred;
+  if (BASE_EQ (pattern, QCequal) || BASE_EQ (pattern, QCeq_q))
+    return Vtreesit_str_pound_eq_question_mark;
+  if (BASE_EQ (pattern, QCmatch) || BASE_EQ (pattern, QCmatch_q))
+    return Vtreesit_str_pound_match_question_mark;
+  if (BASE_EQ (pattern, QCpred) || BASE_EQ (pattern, QCpred_q))
+    return Vtreesit_str_pound_pred_question_mark;
   Lisp_Object opening_delimeter
     = VECTORP (pattern)
       ? Vtreesit_str_open_bracket : Vtreesit_str_open_paren;
@@ -2672,7 +2672,9 @@ DEFUN ("treesit-query-expand",
     :*
     :+
     :equal
+    :eq?
     :match
+    :match?
     (TYPE PATTERN...)
     [PATTERN...]
     FIELD-NAME:
@@ -2835,7 +2837,7 @@ treesit_predicate_equal (Lisp_Object args, struct capture_range captures,
   return !NILP (Fstring_equal (text1, text2));
 }
 
-/* Handles predicate (#match "regexp" @node).  Return true if "regexp"
+/* Handles predicate (#match? "regexp" @node).  Return true if "regexp"
    matches the text spanned by @node; return false otherwise.
    Matching is case-sensitive.  If everything goes fine, don't touch
    SIGNAL_DATA; if error occurs, set it to a suitable signal data.  */
@@ -2845,26 +2847,25 @@ treesit_predicate_match (Lisp_Object args, struct capture_range captures,
 {
   if (list_length (args) != 2)
     {
-      *signal_data = list2 (build_string ("Predicate `match' requires two "
+      *signal_data = list2 (build_string ("Predicate `match?' requires two "
 					  "arguments but got"),
 			    Flength (args));
       return false;
     }
-  Lisp_Object regexp = XCAR (args);
-  Lisp_Object capture_name = XCAR (XCDR (args));
-
-  /* It's probably common to get the argument order backwards.  Catch
-     this mistake early and show helpful explanation, because Emacs
-     loves you.  (We put the regexp first because that's what
-     string-match does.)  */
-  if (!STRINGP (regexp))
-    xsignal1 (Qtreesit_query_error,
-	      build_string ("The first argument to `match' should "
-		            "be a regexp string, not a capture name"));
-  if (!SYMBOLP (capture_name))
-    xsignal1 (Qtreesit_query_error,
-	      build_string ("The second argument to `match' should "
-		            "be a capture name, not a string"));
+  Lisp_Object arg1 = XCAR (args);
+  Lisp_Object arg2 = XCAR (XCDR (args));
+  Lisp_Object regexp = SYMBOLP (arg2) ? arg1 : arg2;
+  Lisp_Object capture_name = SYMBOLP (arg2) ? arg2 : arg1;
+
+  if (!STRINGP (regexp) || !SYMBOLP (capture_name))
+    {
+      *signal_data = list2 (build_string ("Predicate `match?' takes a regexp "
+	                                  "and a node capture (order doesn't "
+					  "matter), but got"),
+			    Flength (args));
+      return false;
+    }
+
 
   Lisp_Object node = Qnil;
   if (!treesit_predicate_capture_name_to_node (capture_name, captures, &node,
@@ -2948,11 +2949,11 @@ treesit_eval_predicates (struct capture_range captures, Lisp_Object predicates,
       Lisp_Object predicate = XCAR (tail);
       Lisp_Object fn = XCAR (predicate);
       Lisp_Object args = XCDR (predicate);
-      if (!NILP (Fstring_equal (fn, Vtreesit_str_equal)))
+      if (!NILP (Fstring_equal (fn, Vtreesit_str_eq_question_mark)))
 	pass &= treesit_predicate_equal (args, captures, signal_data);
-      else if (!NILP (Fstring_equal (fn, Vtreesit_str_match)))
+      else if (!NILP (Fstring_equal (fn, Vtreesit_str_match_question_mark)))
 	pass &= treesit_predicate_match (args, captures, signal_data);
-      else if (!NILP (Fstring_equal (fn, Vtreesit_str_pred)))
+      else if (!NILP (Fstring_equal (fn, Vtreesit_str_pred_question_mark)))
 	pass &= treesit_predicate_pred (args, captures, signal_data);
       else
 	{
@@ -4224,8 +4225,16 @@ syms_of_treesit (void)
   DEFSYM (QCstar, ":*");
   DEFSYM (QCplus, ":+");
   DEFSYM (QCequal, ":equal");
+  DEFSYM (QCeq_q, ":eq?");
   DEFSYM (QCmatch, ":match");
+  DEFSYM (QCmatch_q, ":match?");
   DEFSYM (QCpred, ":pred");
+  DEFSYM (QCpred_q, ":pred?");
+  DEFSYM (QCline, ":line");
+  DEFSYM (QCcol, ":col");
+  DEFSYM (QCpos, ":pos");
+  DEFSYM (QCbytepos, ":bytepos");
+
 
   DEFSYM (Qnot_found, "not-found");
   DEFSYM (Qsymbol_error, "symbol-error");
@@ -4355,13 +4364,13 @@ cons (REGEXP . FN), which is a combination of a regexp and a predicate
   staticpro (&Vtreesit_str_star);
   Vtreesit_str_star = build_pure_c_string ("*");
   staticpro (&Vtreesit_str_plus);
-  Vtreesit_str_plus = build_pure_c_string ("+");
-  staticpro (&Vtreesit_str_pound_equal);
-  Vtreesit_str_pound_equal = build_pure_c_string ("#equal");
-  staticpro (&Vtreesit_str_pound_match);
-  Vtreesit_str_pound_match = build_pure_c_string ("#match");
-  staticpro (&Vtreesit_str_pound_pred);
-  Vtreesit_str_pound_pred = build_pure_c_string ("#pred");
+  Vtreesit_str_plus = build_string ("+");
+  staticpro (&Vtreesit_str_pound_eq_question_mark);
+  Vtreesit_str_pound_eq_question_mark = build_string ("#eq?");
+  staticpro (&Vtreesit_str_pound_match_question_mark);
+  Vtreesit_str_pound_match_question_mark = build_string ("#match?");
+  staticpro (&Vtreesit_str_pound_pred_question_mark);
+  Vtreesit_str_pound_pred_question_mark = build_string ("#pred?");
   staticpro (&Vtreesit_str_open_bracket);
   Vtreesit_str_open_bracket = build_pure_c_string ("[");
   staticpro (&Vtreesit_str_close_bracket);
@@ -4371,13 +4380,13 @@ cons (REGEXP . FN), which is a combination of a regexp and a predicate
   staticpro (&Vtreesit_str_close_paren);
   Vtreesit_str_close_paren = build_pure_c_string (")");
   staticpro (&Vtreesit_str_space);
-  Vtreesit_str_space = build_pure_c_string (" ");
-  staticpro (&Vtreesit_str_equal);
-  Vtreesit_str_equal = build_pure_c_string ("equal");
-  staticpro (&Vtreesit_str_match);
-  Vtreesit_str_match = build_pure_c_string ("match");
-  staticpro (&Vtreesit_str_pred);
-  Vtreesit_str_pred = build_pure_c_string ("pred");
+  Vtreesit_str_space = build_string (" ");
+  staticpro (&Vtreesit_str_eq_question_mark);
+  Vtreesit_str_eq_question_mark = build_string ("eq?");
+  staticpro (&Vtreesit_str_match_question_mark);
+  Vtreesit_str_match_question_mark = build_string ("match?");
+  staticpro (&Vtreesit_str_pred_question_mark);
+  Vtreesit_str_pred_question_mark = build_string ("pred?");
   staticpro (&Vtreesit_str_empty);
   Vtreesit_str_empty = build_pure_c_string ("");
 
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
index 7cbe516fc42..8fa8f098113 100644
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el
@@ -434,10 +434,10 @@ treesit-query-api
                ;; String query.
                '("(string) @string
 (pair key: (_) @keyword)
-((_) @bob (#match \"\\\\`B.b\\\\'\" @bob))
+((_) @bob (#match? \"\\\\`B.b\\\\'\" @bob))
 (number) @number
-((number) @n3 (#equal \"3\" @n3))
-((number) @n3p (#pred treesit--ert-pred-last-sibling @n3p))"
+((number) @n3 (#eq? \"3\" @n3))
+((number) @n3p (#pred? treesit--ert-pred-last-sibling @n3p))"
                  ;; Sexp query.
                  ((string) @string
                   (pair key: (_) @keyword)
-- 
2.53.0

Attachment: signature.asc
Description: PGP signature

Reply via email to