From 0175b7209ed7a326871688d0f938cda769f98249 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 2 Nov 2025 17:38:40 +0000 Subject: [PATCH 1/6] ; * lisp/emacs-lisp/cond-star.el (cond*): Fix typo. --- lisp/emacs-lisp/cond-star.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/emacs-lisp/cond-star.el b/lisp/emacs-lisp/cond-star.el index 0969511176a..dcbc3e67829 100644 --- a/lisp/emacs-lisp/cond-star.el +++ b/lisp/emacs-lisp/cond-star.el @@ -57,7 +57,7 @@ A `cond*' construct is a series of clauses, and a clause normally has the form (CONDITION BODY...). CONDITION can be a Lisp expression, as in `cond'. -Or it can be one of`(bind* BINDINGS...)', `(match* PATTERN DATUM)', +Or it can be one of `(bind* BINDINGS...)', `(match* PATTERN DATUM)', or `(pcase* PATTERN DATUM)', `(bind* BINDINGS...)' means to bind BINDINGS (as if they were in `let*') From 68290e1a03ba4f264eab5d25960cf907b0c903fe Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Sun, 2 Nov 2025 14:03:33 -0800 Subject: [PATCH 2/6] Fix fontification for var in java-ts-mode (bug#79626) * lisp/progmodes/java-ts-mode.el: (java-ts-mode--font-lock-settings): Add font-lock rule for "var". --- lisp/progmodes/java-ts-mode.el | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index 64a41de2a43..cd97472a3cd 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -315,6 +315,12 @@ function is called. Subsequent calls return the first evaluated value." (:match "\\`[A-Z]" @font-lock-type-face)) (type_identifier) @font-lock-type-face + ;; In Java, var is not a keyword but rather a auto-determined type. + ;; But we want to fontify it as a keyword. (The override query is + ;; below the general query because :override flag is set for this + ;; rule.) + ((type_identifier) @font-lock-keyword-face + (:match "\\`var\\'" @font-lock-keyword-face)) [(boolean_type) (integral_type) From b01435306a36e4e75671fbe7bacea351f89947d5 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Sun, 2 Nov 2025 16:16:50 -0800 Subject: [PATCH 3/6] 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 +++++++++------ etc/NEWS | 12 ++++++ src/treesit.c | 90 ++++++++++++++++++++------------------- test/src/treesit-tests.el | 6 +-- 4 files changed, 83 insertions(+), 59 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: @group ( (array :anchor (_) @@first (_) @@last :anchor) - (:equal @@first @@last) + (:eq? @@first @@last) ) @end group @end example @@ -1482,24 +1482,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 @@ -1554,9 +1562,9 @@ Anchor @code{:anchor} is written as @samp{.}. @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, @@ -1565,7 +1573,7 @@ For example, @group '(( (compound_expression :anchor (_) @@first (_) :* @@rest) - (:match "love" @@first) + (:match? "love" @@first) )) @end group @end example @@ -1577,7 +1585,7 @@ is written in string form as @group "( (compound_expression . (_) @@first (_)* @@rest) - (#match \"love\" @@first) + (#match? \"love\" @@first) )" @end group @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 rather than a language, and it is now possible to select a local parser at point to explore. ++++ +*** Tree-sitter query predicate :equal, :match, and :pred are deprecated +Use :eq?, :match?, :pred? instead. The change is because newer +tree-sitter library mandates query predicates to end with question mark. +Emacs will transparently converts :equal, :match and :pred to :eq?, +:match? and :pred?, respectively, so existing queries still work fine +with latest tree-sitter library. Predicate :equal is changed to :eq? to +better follow tree-sitter’s convention. Also, the :match? predicates +can now take the regexp as either the first or second argument, so it +works with both tree-sitter convention (regexp arg second) and Emacs +convention (regexp arg first). + ** Hideshow +++ 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; 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 @@ -3471,12 +3471,12 @@ See Info node `(elisp)Pattern Matching' for detailed explanation. */) 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; @@ -3507,7 +3507,9 @@ A PATTERN in QUERY can be :* :+ :equal + :eq? :match + :match? (TYPE PATTERN...) [PATTERN...] FIELD-NAME: @@ -3670,7 +3672,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. */ @@ -3680,26 +3682,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)); + 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, @@ -3783,11 +3784,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 { @@ -5175,8 +5176,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 (QCline, ":line"); DEFSYM (QCcol, ":col"); DEFSYM (QCpos, ":pos"); @@ -5357,12 +5361,12 @@ depending on customization of `treesit-enabled-modes'. */); Vtreesit_str_star = build_string ("*"); staticpro (&Vtreesit_str_plus); Vtreesit_str_plus = build_string ("+"); - staticpro (&Vtreesit_str_pound_equal); - Vtreesit_str_pound_equal = build_string ("#equal"); - staticpro (&Vtreesit_str_pound_match); - Vtreesit_str_pound_match = build_string ("#match"); - staticpro (&Vtreesit_str_pound_pred); - Vtreesit_str_pound_pred = build_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_string ("["); staticpro (&Vtreesit_str_close_bracket); @@ -5373,12 +5377,12 @@ depending on customization of `treesit-enabled-modes'. */); Vtreesit_str_close_paren = build_string (")"); staticpro (&Vtreesit_str_space); Vtreesit_str_space = build_string (" "); - staticpro (&Vtreesit_str_equal); - Vtreesit_str_equal = build_string ("equal"); - staticpro (&Vtreesit_str_match); - Vtreesit_str_match = build_string ("match"); - staticpro (&Vtreesit_str_pred); - Vtreesit_str_pred = build_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_string (""); 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." ;; 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 f591d1c02745af9ce8cf4ed7dc4ada05ca4f822b Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 29 Oct 2025 22:43:12 -0700 Subject: [PATCH 4/6] ; Skip erc-keep-place-indicator test on Emacs 27 * test/lisp/erc/erc-scenarios-base-reuse-buffers.el (erc-scenarios-common--base-reuse-buffers-channel-buffers): Extend some timeouts. * test/lisp/erc/erc-scenarios-keep-place-indicator.el (erc-scenarios-keep-place-indicator--follow): Prefer `ert-skip' here instead of an :unstable tag because ERC's tests can run locally on some systems with "SELECTOR=t". --- test/lisp/erc/erc-scenarios-base-reuse-buffers.el | 6 +++--- test/lisp/erc/erc-scenarios-keep-place-indicator.el | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el index 860cd79be11..d9d8bdaa4de 100644 --- a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el +++ b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el @@ -107,21 +107,21 @@ Adapted from scenario clash-of-chans/uniquify described in Bug#48598: (ert-info ("#chan@foonet is exclusive and not contaminated") (with-current-buffer "#chan/127.0.0.1" - (funcall expect 1 "") + (funcall expect 10 "") (erc-d-t-absent-for 0.1 "") (funcall expect 1 "strength to climb") (should (eq erc-server-process server-process-foo)))) (ert-info ("#chan@barnet is exclusive and not contaminated") (with-current-buffer "#chan/127.0.0.1<2>" - (funcall expect 1 "") + (funcall expect 10 "") (erc-d-t-absent-for 0.1 "") (funcall expect 1 "the loudest noise") (should (eq erc-server-process server-process-bar)))) (ert-info ("Part #chan@foonet") (with-current-buffer "#chan/127.0.0.1" - (erc-d-t-search-for 1 "shake my sword") + (erc-d-t-search-for 10 "shake my sword") (erc-cmd-PART "#chan") (funcall expect 3 "You have left channel #chan") (should-not (erc-get-channel-user (erc-current-nick))) diff --git a/test/lisp/erc/erc-scenarios-keep-place-indicator.el b/test/lisp/erc/erc-scenarios-keep-place-indicator.el index 2e0babfd2ae..7ff668f0eda 100644 --- a/test/lisp/erc/erc-scenarios-keep-place-indicator.el +++ b/test/lisp/erc/erc-scenarios-keep-place-indicator.el @@ -35,7 +35,8 @@ ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) ;; ERC's tests also run in external CI that exports this variable. - (when (getenv "CI") + ;; Skip on 27 because `erc-scrolltobottom-all' currently requires 28+. + (when (or (getenv "CI") (< emacs-major-version 28)) (ert-skip "Times out intermittently")) (should-not erc-scrolltobottom-all) From d123ac14005dcabe2e83630ae17b678f2c113d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Sun, 2 Nov 2025 18:11:34 +0100 Subject: [PATCH 5/6] Don't assume that literal constants are eq-unique in tests * test/lisp/emacs-lisp/cl-lib-tests.el (cl-test-ldiff): * test/lisp/emacs-lisp/seq-tests.el (test-seq-union): These tests relied on literal constants being unique in the sense of 'eq', and would fail if they stop being that (already the case for strings). --- test/lisp/emacs-lisp/cl-lib-tests.el | 2 +- test/lisp/emacs-lisp/seq-tests.el | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/lisp/emacs-lisp/cl-lib-tests.el b/test/lisp/emacs-lisp/cl-lib-tests.el index d7c38b73432..2955e3f9c88 100644 --- a/test/lisp/emacs-lisp/cl-lib-tests.el +++ b/test/lisp/emacs-lisp/cl-lib-tests.el @@ -208,7 +208,7 @@ (should (null (cl-ldiff l l))) (should (equal l (cl-ldiff l '()))) ;; must be part of the list - (should (equal l (cl-ldiff l '(2 3)))) + (should (equal l (cl-ldiff l (list 2 3)))) (should (equal '(1) (cl-ldiff l (nthcdr 1 l)))) ;; should return a copy (should-not (eq (cl-ldiff l '()) l)))) diff --git a/test/lisp/emacs-lisp/seq-tests.el b/test/lisp/emacs-lisp/seq-tests.el index a8ed51e8e70..e84e50dda4c 100644 --- a/test/lisp/emacs-lisp/seq-tests.el +++ b/test/lisp/emacs-lisp/seq-tests.el @@ -386,8 +386,8 @@ Evaluate BODY for each created sequence. (should (same-contents-p (seq-union v1 v2) '("a" "b" "c" "f" "e")))) - (let ((v1 '("a")) - (v2 '("a")) + (let ((v1 (list (make-string 1 ?a))) + (v2 (list (make-string 1 ?a))) (testfn #'eq)) (should (same-contents-p (seq-union v1 v2 testfn) '("a" "a"))))) From f2836a85159df6551c27a47eac4c55e14c1aed75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Mon, 3 Nov 2025 10:53:27 +0100 Subject: [PATCH 6/6] ; Avoid minibuffer.el compilation warning about cl-every --- lisp/minibuffer.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 912c14a3667..e79e96bd7c7 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -4324,7 +4324,7 @@ one wildcard." (when (cdr segment) (concat (when group "\\(") - (if (cl-every (lambda (x) (eq x 'any-delim)) (cdr segment)) + (if (all (lambda (x) (eq x 'any-delim)) (cdr segment)) (concat completion-pcm--delim-wild-regex "*?") "[^z-a]*?") (when group "\\)")))))