From 2a75693f24a631b0a2a366bdce1ca5cb0c38d215 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 27 Aug 2024 22:31:42 -0700
Subject: [PATCH] Add Ftreesit_grammar_location

* src/treesit.c (treesit_loaded_lang): New struct.
(treesit_load_language): Return a struct instead of just the language
object.  The struct contains both the language object and the path to
the shared library.
(Ftreesit_language_available_p, Ftreesit_language_abi_version)
(treesit_ensure_query_compiled, Ftreesit_parser_create): Update
call of treesit_load_language.
(Ftreesit_grammar_location): New function.
---
 src/treesit.c | 71 +++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 58 insertions(+), 13 deletions(-)

diff --git a/src/treesit.c b/src/treesit.c
index 0ba6c733d64..8c586109b2d 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -22,6 +22,7 @@ along with GNU Emacs.  If not, see <http
 #include <config.h>
 #include "lisp.h"
 #include "buffer.h"
+#include "coding.h"
 
 #include "treesit.h"
 
@@ -538,6 +539,15 @@ treesit_debug_print_parser_list (char *m
 
 /*** Loading language library  */
 
+struct treesit_loaded_lang
+{
+  /* The language object, or NULL if the language failed to load.  */
+  TSLanguage *lang;
+  /* The absolute file name of the shared library, or NULL if access
+     failed.  */
+  const char *filename;
+};
+
 /* Translate a symbol treesit-<lang> to a C name treesit_<lang>.  */
 static void
 treesit_symbol_to_c_name (char *symbol_name)
@@ -637,7 +647,7 @@ treesit_load_language_push_for_each_suff
 
    If error occurs, return NULL and fill SIGNAL_SYMBOL and SIGNAL_DATA
    with values suitable for xsignal.  */
-static TSLanguage *
+static struct treesit_loaded_lang
 treesit_load_language (Lisp_Object language_symbol,
 		       Lisp_Object *signal_symbol, Lisp_Object *signal_data)
 {
@@ -688,6 +698,7 @@ treesit_load_language (Lisp_Object langu
   dynlib_handle_ptr handle;
   const char *error;
   Lisp_Object error_list = Qnil;
+  struct treesit_loaded_lang loaded_lang = { NULL, NULL };
 
   tail = path_candidates;
   error = NULL;
@@ -712,7 +723,7 @@ treesit_load_language (Lisp_Object langu
          mismatch.  */
       *signal_symbol = Qtreesit_load_language_error;
       *signal_data = Fcons (Qnot_found, Fnreverse (error_list));
-      return NULL;
+      return loaded_lang;
     }
 
   /* Load TSLanguage.  */
@@ -734,7 +745,7 @@ treesit_load_language (Lisp_Object langu
     {
       *signal_symbol = Qtreesit_load_language_error;
       *signal_data = list2 (Qsymbol_error, build_string (error));
-      return NULL;
+      return loaded_lang;
     }
   TSLanguage *lang = (*langfn) ();
 
@@ -747,9 +758,14 @@ treesit_load_language (Lisp_Object langu
       *signal_symbol = Qtreesit_load_language_error;
       *signal_data = list2 (Qversion_mismatch,
 			    make_fixnum (ts_language_version (lang)));
-      return NULL;
+      return loaded_lang;
     }
-  return lang;
+
+  const char *sym;
+  dynlib_addr ((void (*)) langfn, &loaded_lang.filename, &sym);
+
+  loaded_lang.lang = lang;
+  return loaded_lang;
 }
 
 DEFUN ("treesit-language-available-p", Ftreesit_language_available_p,
@@ -766,7 +782,9 @@ If DETAIL is non-nil, return (t . nil) w
   treesit_initialize ();
   Lisp_Object signal_symbol = Qnil;
   Lisp_Object signal_data = Qnil;
-  if (treesit_load_language (language, &signal_symbol, &signal_data) == NULL)
+  struct treesit_loaded_lang loaded_lang
+    = treesit_load_language (language, &signal_symbol, &signal_data);
+  if (loaded_lang.lang == NULL)
     {
       if (NILP (detail))
 	return Qnil;
@@ -812,9 +830,9 @@ Return nil if a grammar library for LANG
     {
       Lisp_Object signal_symbol = Qnil;
       Lisp_Object signal_data = Qnil;
-      TSLanguage *ts_language = treesit_load_language (language,
-						       &signal_symbol,
-						       &signal_data);
+      struct treesit_loaded_lang lang
+	= treesit_load_language (language, &signal_symbol, &signal_data);
+      TSLanguage *ts_language = lang.lang;
       if (ts_language == NULL)
 	return Qnil;
       uint32_t version =  ts_language_version (ts_language);
@@ -822,6 +840,30 @@ Return nil if a grammar library for LANG
     }
 }
 
+/* This function isn't documented in the manual since it's mainly for
+   debugging.  */
+DEFUN ("treesit-grammar-location", Ftreesit_grammar_location,
+       Streesit_grammar_location,
+       1, 1, 0,
+       doc: /* Return the absolute file name of the grammar file for LANGUAGE.
+
+If LANGUAGE isn't loaded yet, load it first.  If the language can't be
+loaded or the file name couldn't be determined, return nil.  */)
+  (Lisp_Object language)
+{
+  CHECK_SYMBOL (language);
+
+  Lisp_Object signal_symbol = Qnil;
+  Lisp_Object signal_data = Qnil;
+  struct treesit_loaded_lang lang
+    = treesit_load_language (language, &signal_symbol, &signal_data);
+
+  if (!lang.lang || !lang.filename) return Qnil;
+
+  return DECODE_FILE (make_unibyte_string (lang.filename,
+					   strlen (lang.filename)));
+}
+
 
 /*** Parsing functions  */
 
@@ -1449,8 +1491,9 @@ treesit_ensure_query_compiled (Lisp_Obje
   Lisp_Object language = XTS_COMPILED_QUERY (query)->language;
   /* This is the main reason why we compile query lazily: to avoid
      loading languages early.  */
-  TSLanguage *treesit_lang = treesit_load_language (language, signal_symbol,
-						    signal_data);
+  struct treesit_loaded_lang lang
+    = treesit_load_language (language, signal_symbol, signal_data);
+  TSLanguage *treesit_lang = lang.lang;
   if (treesit_lang == NULL)
     return NULL;
 
@@ -1604,8 +1647,9 @@ an indirect buffer.  */)
   Lisp_Object signal_symbol = Qnil;
   Lisp_Object signal_data = Qnil;
   TSParser *parser = ts_parser_new ();
-  TSLanguage *lang = treesit_load_language (language, &signal_symbol,
-					    &signal_data);
+  struct treesit_loaded_lang loaded_lang
+    = treesit_load_language (language, &signal_symbol, &signal_data);
+  TSLanguage *lang = loaded_lang.lang;
   if (lang == NULL)
     xsignal (signal_symbol, signal_data);
   /* We check language version when loading a language, so this should
@@ -4352,6 +4396,7 @@ the symbol of that THING.  For example,
   defsubr (&Streesit_language_available_p);
   defsubr (&Streesit_library_abi_version);
   defsubr (&Streesit_language_abi_version);
+  defsubr (&Streesit_grammar_location);
 
   defsubr (&Streesit_parser_p);
   defsubr (&Streesit_node_p);
-- 
2.51.0

From 38789e9a2878846cadb72fef58cc24872fe46ae4 Mon Sep 17 00:00:00 2001
From: Eli Zaretskii <eliz@gnu.org>
Date: Sun, 18 May 2025 09:05:07 +0300
Subject: [PATCH] Improve reporting of language-grammar library ABI version
 mismatch

* lisp/treesit.el (treesit-ready-p): More accurate wording of
message when grammar library fails to load.

* src/treesit.c (treesit_load_language): Improve message when
reporting library ABI version mismatch.  Suggested by Soham
Gumaste <sohamg2@gmail.com>.
---
 lisp/treesit.el |  2 +-
 src/treesit.c   | 18 ++++++++++++++----
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/lisp/treesit.el b/lisp/treesit.el
index a353bc942d3..68de843ce4d 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -3059,7 +3059,7 @@ instead of emitting a warning."
         (pcase-let ((`(,available . ,err)
                      (treesit-language-available-p lang t)))
           (when (not available)
-            (setq msg (format "language grammar for %s is unavailable (%s): %s"
+            (setq msg (format "language grammar for %s failed to load (%s): %s"
                               lang (nth 0 err)
                               (string-join
                                (mapcar (lambda (x) (format "%s" x))
diff --git a/src/treesit.c b/src/treesit.c
index 3a19e0cb282..de74e41c89a 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -704,6 +704,7 @@ treesit_load_language (Lisp_Object language_symbol,
   error = NULL;
   handle = NULL;
 
+  Lisp_Object loaded_lib = Qnil;
   FOR_EACH_TAIL (tail)
     {
       char *library_name = SSDATA (XCAR (tail));
@@ -711,7 +712,10 @@ treesit_load_language (Lisp_Object language_symbol,
       handle = dynlib_open (library_name);
       error = dynlib_error ();
       if (error == NULL)
-	break;
+	{
+	  loaded_lib = XCAR (tail);
+	  break;
+	}
       else
 	error_list = Fcons (build_string (error), error_list);
     }
@@ -755,9 +759,15 @@ treesit_load_language (Lisp_Object language_symbol,
   ts_parser_delete (parser);
   if (!success)
     {
+      Lisp_Object fmt =
+	build_string ("%s's ABI version is %d, but supported versions are %d-%d");
+      Lisp_Object formatted_msg =
+	CALLN (Fformat_message, fmt, loaded_lib,
+	       make_fixnum (ts_language_version (lang)),
+	       make_fixnum (TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION),
+	       make_fixnum (TREE_SITTER_LANGUAGE_VERSION));
       *signal_symbol = Qtreesit_load_language_error;
-      *signal_data = list2 (Qversion_mismatch,
-			    make_fixnum (ts_language_version (lang)));
+      *signal_data = list2 (Qlang_version_mismatch, formatted_msg);
       return loaded_lang;
     }
 
@@ -4241,7 +4251,7 @@ syms_of_treesit (void)
 
   DEFSYM (Qnot_found, "not-found");
   DEFSYM (Qsymbol_error, "symbol-error");
-  DEFSYM (Qversion_mismatch, "version-mismatch");
+  DEFSYM (Qlang_version_mismatch, "language-grammar-version-mismatch");
 
   DEFSYM (Qtreesit_error, "treesit-error");
   DEFSYM (Qtreesit_query_error, "treesit-query-error");
-- 
2.51.0

From d587ce8c65a0e22ab0a63ef2873a3dfcfbeba166 Mon Sep 17 00:00:00 2001
From: Eli Zaretskii <eliz@gnu.org>
Date: Fri, 17 Oct 2025 14:15:41 +0300
Subject: [PATCH] Support Tree-sitter version 0.26 and later

* src/treesit.c (init_treesit_functions)
[TREE_SITTER_LANGUAGE_VERSION >= 15]: Define prototype for, and
load 'ts_language_abi_version' instead of the deprecated (and
removed in tree-sitter 0.26) 'ts_language_version'.
(ts_language_abi_version) [TREE_SITTER_LANGUAGE_VERSION >= 15]:
Define on WINDOWSNT, instead of 'ts_language_version'.
(treesit_language_abi_version): New compatibility function.
(treesit_load_language, Ftreesit_language_abi_version): Use
'treesit_language_abi_version' instead of 'ts_language_version'.
(Bug#79627)
---
 src/treesit.c | 36 ++++++++++++++++++++++++++++++++++--
 1 file changed, 34 insertions(+), 2 deletions(-)

diff --git a/src/treesit.c b/src/treesit.c
index bf982de580b..69751b5ea10 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -35,7 +35,11 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 # include "w32common.h"
 
 /* In alphabetical order.  */
+#if TREE_SITTER_LANGUAGE_VERSION >= 15
+#undef ts_language_abi_version
+#else
 #undef ts_language_version
+#endif
 #undef ts_node_child
 #undef ts_node_child_by_field_name
 #undef ts_node_child_count
@@ -90,7 +94,11 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #undef ts_tree_get_changed_ranges
 #undef ts_tree_root_node
 
+#if TREE_SITTER_LANGUAGE_VERSION >= 15
+DEF_DLL_FN (uint32_t, ts_language_abi_version, (const TSLanguage *));
+#else
 DEF_DLL_FN (uint32_t, ts_language_version, (const TSLanguage *));
+#endif
 DEF_DLL_FN (TSNode, ts_node_child, (TSNode, uint32_t));
 DEF_DLL_FN (TSNode, ts_node_child_by_field_name,
 	    (TSNode, const char *, uint32_t));
@@ -167,7 +175,11 @@ init_treesit_functions (void)
   if (!library)
     return false;
 
+#if TREE_SITTER_LANGUAGE_VERSION >= 15
+  LOAD_DLL_FN (library, ts_language_abi_version);
+#else
   LOAD_DLL_FN (library, ts_language_version);
+#endif
   LOAD_DLL_FN (library, ts_node_child);
   LOAD_DLL_FN (library, ts_node_child_by_field_name);
   LOAD_DLL_FN (library, ts_node_child_count);
@@ -225,7 +237,11 @@ init_treesit_functions (void)
   return true;
 }
 
+#if TREE_SITTER_LANGUAGE_VERSION >= 15
+#define ts_language_abi_version fn_ts_language_abi_version
+#else
 #define ts_language_version fn_ts_language_version
+#endif
 #define ts_node_child fn_ts_node_child
 #define ts_node_child_by_field_name fn_ts_node_child_by_field_name
 #define ts_node_child_count fn_ts_node_child_count
@@ -642,6 +658,22 @@ treesit_load_language_push_for_each_suffix (Lisp_Object lib_base_name,
     }
 }
 
+/* This function is a compatibility shim.  Tree-sitter 0.25 introduced
+   ts_language_abi_version as a replacement for ts_language_version, and
+   tree-sitter 0.26 removed ts_language_version.  Here we use the fact
+   that 0.25 bumped TREE_SITTER_LANGUAGE_VERSION to 15, to use the new
+   function instead of the old one, when Emacs is compiled against
+   tree-sitter version 0.25 or newer.  */
+static uint32_t
+treesit_language_abi_version (const TSLanguage *ts_lang)
+{
+#if TREE_SITTER_LANGUAGE_VERSION >= 15
+  return ts_language_abi_version (ts_lang);
+#else
+  return ts_language_version (ts_lang);
+#endif
+}
+
 /* Load the dynamic library of LANGUAGE_SYMBOL and return the pointer
    to the language definition.
 
@@ -763,7 +795,7 @@ treesit_load_language (Lisp_Object language_symbol,
 	build_string ("%s's ABI version is %d, but supported versions are %d-%d");
       Lisp_Object formatted_msg =
 	CALLN (Fformat_message, fmt, loaded_lib,
-	       make_fixnum (ts_language_version (lang)),
+	       make_fixnum (treesit_language_abi_version (lang)),
 	       make_fixnum (TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION),
 	       make_fixnum (TREE_SITTER_LANGUAGE_VERSION));
       *signal_symbol = Qtreesit_load_language_error;
@@ -845,7 +877,7 @@ Return nil if a grammar library for LANGUAGE is not available.  */)
       TSLanguage *ts_language = lang.lang;
       if (ts_language == NULL)
 	return Qnil;
-      uint32_t version =  ts_language_version (ts_language);
+      uint32_t version =  treesit_language_abi_version (ts_language);
       return make_fixnum((ptrdiff_t) version);
     }
 }
-- 
2.51.0

From b01435306a36e4e75671fbe7bacea351f89947d5 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
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.
---
 doc/lispref/parsing.texi  |   34 ++++++++++-------
 src/treesit.c             |   90 ++++++++++++++++++++++++----------------------
 test/src/treesit-tests.el |    6 +--
 3 files changed, 71 insertions(+), 59 deletions(-)

--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi	2026-04-21 11:28:33.986819462 +0000
@@ -1375,7 +1375,7 @@ example, with the following pattern:
 @group
 (
  (array :anchor (_) @@first (_) @@last :anchor)
- (:equal @@first @@last)
+ (:eq? @@first @@last)
 )
 @end group
 @end example
@@ -1384,24 +1384,32 @@ example, with the following pattern:
 tree-sitter only matches arrays where the first element is equal to
 the last element.  To attach a predicate to a pattern, we need to
 group them together.  Currently there are three predicates:
-@code{:equal}, @code{:match}, and @code{:pred}.
+@code{:eq?}, @code{:match?}, and @code{:pred?}.
 
-@deffn Predicate :equal arg1 arg2
+@deffn Predicate :eq? arg1 arg2
 Matches if @var{arg1} is equal to @var{arg2}.  Arguments can be either
 strings or capture names.  Capture names represent the text that the
-captured node spans in the buffer.
+captured node spans in the buffer.  Note that this is more like
+@code{equal} in Elisp, but @code{eq?} is the convention used by
+tree-sitter.  Previously we supported the @code{:equal} predicate but
+it's now considered deprecated.
 @end deffn
 
-@deffn Predicate :match regexp capture-name
+@deffn Predicate :match? capture-name regexp
 Matches if the text that @var{capture-name}'s node spans in the buffer
 matches regular expression @var{regexp}, given as a string literal.
-Matching is case-sensitive.
+Matching is case-sensitive.  The ordering of the arguments doesn't
+matter.  Previously we supported the @code{:match} predicate but it's
+now considered deprecated.
 @end deffn
 
-@deffn Predicate :pred fn &rest nodes
+@deffn Predicate :pred? fn &rest nodes
 Matches if function @var{fn} returns non-@code{nil} when passed each
 node in @var{nodes} as arguments.  The function runs with the current
-buffer set to the buffer of node being queried.
+buffer set to the buffer of node being queried.  Be very careful when
+using this predicate, since it can be expensive when used in a tight
+loop.  Previously we supported the @code{:pred} predicate but it's now
+considered deprecated.
 @end deffn
 
 Note that a predicate can only refer to capture names that appear in
@@ -1456,9 +1464,9 @@ Anchor @code{:anchor} is written as @sam
 @item
 @samp{:+} is written as @samp{+}.
 @item
-@code{:equal}, @code{:match} and @code{:pred} are written as
-@code{#equal}, @code{#match} and @code{#pred}, respectively.
-In general, predicates change their @samp{:} to @samp{#}.
+@code{:eq?}, @code{:match?} and @code{:pred?} are written as
+@code{#eq?}, @code{#match?} and @code{#pred?}, respectively.  In
+general, predicates change the @samp{:} to @samp{#}.
 @end itemize
 
 For example,
@@ -1467,7 +1475,7 @@ For example,
 @group
 '((
    (compound_expression :anchor (_) @@first (_) :* @@rest)
-   (:match "love" @@first)
+   (:match? "love" @@first)
    ))
 @end group
 @end example
@@ -1479,7 +1487,7 @@ is written in string form as
 @group
 "(
   (compound_expression . (_) @@first (_)* @@rest)
-  (#match \"love\" @@first)
+  (#match? \"love\" @@first)
   )"
 @end group
 @end example
--- a/src/treesit.c
+++ b/src/treesit.c	2026-04-21 11:21:26.262613893 +0000
@@ -432,17 +432,17 @@ static Lisp_Object Vtreesit_str_dot;
 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
@@ -2690,12 +2690,12 @@ See Info node `(elisp)Pattern Matching'
     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;
@@ -2726,7 +2726,9 @@ A PATTERN in QUERY can be
     :*
     :+
     :equal
+    :eq?
     :match
+    :match?
     (TYPE PATTERN...)
     [PATTERN...]
     FIELD-NAME:
@@ -2889,7 +2891,7 @@ treesit_predicate_equal (Lisp_Object arg
   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.  */
@@ -2899,26 +2901,25 @@ treesit_predicate_match (Lisp_Object arg
 {
   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));
+  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;
+    }
 
-  /* 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 node = Qnil;
   if (!treesit_predicate_capture_name_to_node (capture_name, captures, &node,
@@ -3002,11 +3003,11 @@ treesit_eval_predicates (struct capture_
       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
 	{
@@ -4278,8 +4279,11 @@ 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 (Qnot_found, "not-found");
   DEFSYM (Qsymbol_error, "symbol-error");
@@ -4410,12 +4414,12 @@ the symbol of that THING.  For example,
   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");
+  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);
@@ -4426,12 +4430,12 @@ the symbol of that THING.  For example,
   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");
+  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 ("");
 
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el	2026-04-21 11:28:33.986819462 +0000
@@ -434,10 +434,10 @@ BODY is the test body."
                ;; 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)
From: werner@suse.de
Date: Fri, 24 Apr 2026 13:13:32 +0200
Subject: [FIX] Let treesit find libraries below %{_libdir}/tree-sitter/

---
 src/treesit.c |    9 +++++++++
 1 file changed, 9 insertions(+)

--- emacs-30.2/src/treesit.c
+++ emacs-30.2/src/treesit.c	2026-04-24 12:06:16.936246505 +0000
@@ -712,6 +712,15 @@ treesit_load_language (Lisp_Object langu
     = Fexpand_file_name (concat2 (build_string ("tree-sitter/"), lib_base_name),
 			 Fsymbol_value (Quser_emacs_directory));
   treesit_load_language_push_for_each_suffix (lib_name, &path_candidates);
+
+  /* --- START SUSE SYSTEM TREE-SITTER LOAD PATH --- */
+#ifdef TREESIT_SYSTEM_GRAMMAR_DIR
+  Lisp_Object sys_prefix = concat2 (build_string (TREESIT_SYSTEM_GRAMMAR_DIR),
+			   lib_base_name);
+  treesit_load_language_push_for_each_suffix (sys_prefix, &path_candidates);
+#endif
+  /* --- END SUSE SYSTEM TREE-SITTER LOAD PATH --- */
+
   /* Then push paths from treesit-extra-load-path.  */
   Lisp_Object tail;
 
