master 9c2fab94ec1 3/5: Merge from origin/emacs-31
Eli Zaretskii <[email protected]> Sat, 25 Jul 2026 04:52:16 -0400 (EDT)
| Newsgroups | gmane.emacs.diffs |
|---|---|
| Message-ID | <[email protected]> |
branch: master commit 9c2fab94ec11fda068046bddcb44a89a619fbd19 Merge: 31c0007f583 2771f99c722 Author: Eli Zaretskii <[email protected]> Commit: Eli Zaretskii <[email protected]> Merge from origin/emacs-31 2771f99c722 ; * admin/MAINTAINERS: Mark Amin as maintaining some more... 19eeda16a4a ; Fix recent changes in treesit documentation 29bbd02347d Fix treesit_cursor_helper_1 for zero-width nodes (bug#81436) 6b670907fa9 Update tree-sitter manual for embed level 0f6d977a88f ; * test/src/xdisp-tests.el (xdisp-test-format-mode-line)... --- admin/MAINTAINERS | 3 ++ doc/lispref/parsing.texi | 132 +++++++++++++++++++++++++++++++++-------------- lisp/treesit.el | 2 +- src/treesit.c | 11 ++-- test/src/xdisp-tests.el | 8 +-- 5 files changed, 110 insertions(+), 46 deletions(-) diff --git a/admin/MAINTAINERS b/admin/MAINTAINERS index 3ffa21c0d45..a2e80bf2dc8 100644 --- a/admin/MAINTAINERS +++ b/admin/MAINTAINERS @@ -123,6 +123,9 @@ Amin Bandali ERC lisp/erc/* doc/misc/erc.texi + lisp/net/browse-url.el + lisp/net/eww.el + lisp/url/* Andrea Corallo Lisp native compiler diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index 4da72b512b0..dac6574a969 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi @@ -455,20 +455,20 @@ is non-@code{nil}, this function always creates a new parser. @code{nil}. Different parsers can have the same tag. @end defun -Given a parser, we can query information about it. - -@defun treesit-parser-buffer parser -This function returns the buffer associated with @var{parser}. -@end defun - -@defun treesit-parser-language parser -This function returns the language used by @var{parser}. -@end defun - -@defun treesit-parser-p object -This function checks if @var{object} is a tree-sitter parser, and -returns non-@code{nil} if it is, and @code{nil} otherwise. -@end defun +@findex treesit-parser-buffer +@findex treesit-parser-language +@findex treesit-parser-embed-level +@findex treesit-parser-p +Given a parser, we can query information about it: +@code{treesit-parser-buffer} returns the buffer associated with a +parser, and @code{treesit-parser-language} returns the language used by +the parser. A parser's tag can be retrieved by +@code{treesit-parser-tag}. Each parser also optionally carries an embed +level that can be set by @code{treesit-parser-set-embed-level} and +retrieved by @code{treesit-parser-embed-level} (see +@ref{parser-embed-level}, for more about embed level). Finally, +@code{treesit-parser-p} tells you whether an object is a tree-sitter +parser. There is no need to explicitly parse a buffer, because parsing is done automatically and lazily. A parser only parses when a Lisp program @@ -1808,10 +1808,11 @@ Again, @var{thing} can be either a symbol or a predicate. If @var{parser} is non-@code{nil}, only use that parser's parse tree. Otherwise try each parser covering point, from the most specific -(deepest-embedded) to the least specific. If there are multiple parsers with -the same embed level at @var{position}, which parser is tried first is -undefined. If @var{parser} is a language symbol, the function limits -the parsers it tries to the ones for that language. +(deepest-embedded) to the least specific. If there are multiple parsers +with the same embed level (@pxref{parser-embed-level}) at +@var{position}, which parser is tried first is undefined. If +@var{parser} is a language symbol, the function limits the parsers it +tries to the ones for that language. @end defun @defun treesit-thing-next position thing &optional parser @@ -1875,8 +1876,9 @@ position must be strictly greater than @var{position}. If @var{parser} is non-nil, only use that parser's parse tree. Otherwise try each parser covering point, from the most specific (deepest-embedded) to the least specific. If there are multiple parsers -with the same embed level at @var{position}, which parser is tried first is -undefined. @var{parser} can also be a language symbol. +with the same embed level (@pxref{parser-embed-level}) at +@var{position}, which parser is tried first is undefined. @var{parser} +can also be a language symbol. @end defun @findex treesit-beginning-of-thing @@ -2032,11 +2034,12 @@ for nodes in a region. @defun treesit-language-at pos This function returns the language of the text at buffer position @var{pos}. Under the hood it calls -@code{treesit-language-at-point-function} and returns its return -value. If @code{treesit-language-at-point-function} is @code{nil}, -this function returns the language of the deepest parser by embed level -among parsers returned by @code{treesit-parsers-at}. If there is no -parser at that buffer position, it returns @code{nil}. +@code{treesit-language-at-point-function} and returns its return value. +If @code{treesit-language-at-point-function} is @code{nil}, this +function returns the language of the deepest parser by embed level +(@pxref{parser-embed-level}) among parsers returned by +@code{treesit-parsers-at}. If there is no parser at that buffer +position, it returns @code{nil}. @end defun @heading Supporting multiple languages in major modes @@ -2153,10 +2156,38 @@ a @var{query} preceded by zero or more @var{keyword}/@var{value} pairs. Each @var{query} is a tree-sitter query in either the string, s-expression, or compiled form, or a function. +@code{treesit-update-ranges} uses @var{query} to figure out how to set +the ranges for parsers for the embedded language. It queries +@var{query} in a host language parser, computes the ranges which the +captured nodes span, and applies these ranges to embedded language +parsers. + If @var{query} is a tree-sitter query, it should be preceded by two @var{keyword}/@var{value} pairs, where the @code{:embed} keyword -specifies the embedded language, and the @code{:host} keyword -specifies the host language. +specifies the embedded language, and the @code{:host} keyword specifies +the host language. The captured nodes determine the range of embedded +parsers. The capture names don't matter, but names that starts with an +underscore are ignored, and the @code{@@language} capture name is +reserved. + +If the embedded language is dynamic, then @code{:embed} can specify a +function rathar than a static language. The function takes the node +captured with @code{@@language} capture name. Care have to be taken that +the @code{@@language} capture and the range capture are in the same +match group. The function should return either a language symbol, or nil +if no valid language can be found. + +If the query is given a @code{:offset} keyword whose value is a pair +of numbers, each captured range is offset by those numbers. For +example, an offset of @code{(1 . -1)} will update a captured range of +@code{(2 . 8)} to be @code{(3 . 7)}. This can be used to exclude things +like surrounding delimiters from being included in the range covered by +an embedded parser. + +By default, a parser sees its ranges as a continuum, rather than +treating them as separate independent segments. Therefore, if the +embedded ranges are semantically independent segments, they should be +processed by local parsers, described below. @cindex local parser If the query is given the @code{:local} keyword whose value is @@ -2164,19 +2195,44 @@ If the query is given the @code{:local} keyword whose value is otherwise the range shares a parser with other ranges for the same language. -By default, a parser sees its ranges as a continuum, rather than -treating them as separate independent segments. Therefore, if the -embedded ranges are semantically independent segments, they should be -processed by local parsers, described below. - -Local parser set to a range can be retrieved by +Local parsers set to a range can be retrieved by @code{treesit-local-parsers-at} and @code{treesit-local-parsers-on}. -@code{treesit-update-ranges} uses @var{query} to figure out how to set -the ranges for parsers for the embedded language. It queries -@var{query} in a host language parser, computes the ranges which the -captured nodes span, and applies these ranges to embedded language -parsers. +Emacs treats local embedded parsers and non-local embedded parsers quite +differently: + +@itemize @bullet +@item +Local parsers are managed by Emacs. Emacs creates new local parsers when +they are needed and deletes them when they are not. Non-local parsers +must be created by major modes, and Emacs only make use of them if one +exists. + +@item +Because local parsers can be freely created by Emacs, they are suitable +for recursively nested embedded parsers, where the embed level can be +arbitrarily deep. On the other hand, since non-local parsers created by +major modes, they can only handle a fix number of embed levels. +@end itemize + +@anchor{parser-embed-level} +@cindex embed level, tree-sitter +The embed level of the top-level parsers is 0 or omitted, the first +level of embedded parsers get embed level 1, and so on. For local +parsers, each embed level creates a new parser; for non-local parsers, +since Emacs cannot create non-local parsers freely, it tries to find a +usable parser at the right embed level; if none exists, Emacs just gives +up. + +If the query is given a @code{:range-fn} keyword whose value is a +function, Emacs uses that function to compute the ranges to use for the +embedded parser. The function takes as arguments the captured node and +the offset given by the @code{:offset} keyword, and returns a list of +ranges, where each range is a cons of the start and end position. For +local parsers, each @var{query} creates one local parser, and the ranges +set by the @code{:range-fn} are applied to that parser; for non-local +parsers, all the ranges (including ranges created by @code{:range-fn}) +in that embed level are joined together and applied to the same parser. If @var{query} is a function, it doesn't need any @var{keyword} and @var{value} pair. It should be a function that takes 2 arguments, diff --git a/lisp/treesit.el b/lisp/treesit.el index f318e08a565..8a1fb1ca86c 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -3832,7 +3832,7 @@ the current line if the beginning of the defun is indented." Return the first non-nil evaluation of BODY. \(fn (SYM VAL) &rest BODY)" - (declare (indent 1)) + (declare (indent 1) (debug ((symbolp form) body))) (let ((result-sym (gensym)) (val-sym (gensym)) (sym (car sym-val)) diff --git a/src/treesit.c b/src/treesit.c index 2b4718ec002..73094bfbbdf 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -2559,7 +2559,6 @@ embedding increments the embed level by 1. */) return XTS_PARSER (parser)->embed_level; } -/* TODO: Mention in manual, once the API stabilizes. */ DEFUN ("treesit-parser-set-embed-level", Ftreesit_parser_set_embed_level, Streesit_parser_set_embed_level, 2, 2, 0, @@ -4258,9 +4257,13 @@ treesit_cursor_helper_1 (TSTreeCursor *cursor, TSNode *target, return true; /* ts_tree_cursor_goto_first_child_for_byte is significantly faster, - so despite it having problems (see bug#60127), we try it - first. */ - if (ts_tree_cursor_goto_first_child_for_byte (cursor, start_pos) == -1 + so despite it having problems (see bug#60127), we try it first. + Also, ts_tree_cursor_goto_first_child_for_byte can't find + zero-width nodes (which exists and are legit, e.g., markdown's + block_continuation), because a zero-width node can't contain a pos + (end > pos). */ + if (start_pos != end_pos + && ts_tree_cursor_goto_first_child_for_byte (cursor, start_pos) == -1 && !ts_tree_cursor_goto_first_child (cursor)) return false; diff --git a/test/src/xdisp-tests.el b/test/src/xdisp-tests.el index f3df858e218..8eb57253972 100644 --- a/test/src/xdisp-tests.el +++ b/test/src/xdisp-tests.el @@ -205,10 +205,12 @@ int main () { (font-lock-mode 0) (insert "\n") (insert m1) - (setq s1 (window-text-pixel-size nil (line-beginning-position) (point))) + (setq s1 (buffer-text-pixel-size)) + (erase-buffer) (insert "\n") (insert m2) - (setq s2 (window-text-pixel-size nil (line-beginning-position) (point))) - (should (equal m1 m2))))) + (setq s2 (buffer-text-pixel-size)) + (should (equal m1 m2)) + (should (equal s1 s2))))) ;;; xdisp-tests.el ends here