From b8021c8dd1ff33a7f0acc089997f731bf9cdd6b1 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Fri, 10 Jan 2025 09:33:49 +0200 Subject: [PATCH] Use the treesit thing 'list' with symbol property 'treesit-thing-symbol' * doc/lispref/parsing.texi (User-defined Things): Mention new functions 'treesit-forward-list', 'treesit-down-list', 'treesit-up-list', 'treesit-show-paren-data' that use the thing 'list' with the symbol property 'treesit-thing-symbol' (bug#73404). * lisp/treesit.el: Put the property 'treesit-thing-symbol' on the symbol 'list'. (treesit--forward-list-with-default, treesit-down-list) (treesit-up-list, treesit-navigate-thing) (treesit-show-paren-data--categorize, treesit-major-mode-setup): Replace 'sexp-list' with 'list'. * lisp/progmodes/c-ts-mode.el (c-ts-mode--thing-settings): * lisp/progmodes/js.el (js-ts-mode): * lisp/progmodes/ruby-ts-mode.el (ruby-ts-mode): * lisp/progmodes/typescript-ts-mode.el (typescript-ts-base-mode) (tsx-ts-mode): * lisp/textmodes/html-ts-mode.el (html-ts-mode): Replace 'sexp-list' with 'list'. * src/treesit.c (treesit_traverse_validate_predicate) (treesit_traverse_match_predicate): Check if the 'pred' symbol has the property 'Qtreesit_thing_symbol'. (syms_of_treesit): New symbol 'Qtreesit_thing_symbol'. (cherry picked from commit 42a5ac3b513ff03c64c9609fc7e79c2b7932b2a4) --- doc/lispref/parsing.texi | 12 ++++++++---- etc/NEWS | 10 +++++----- lisp/progmodes/c-ts-mode.el | 2 +- lisp/progmodes/js.el | 4 ++-- lisp/progmodes/ruby-ts-mode.el | 6 +++--- lisp/progmodes/typescript-ts-mode.el | 8 ++++---- lisp/textmodes/html-ts-mode.el | 2 +- lisp/treesit.el | 27 +++++++++++++++------------ src/treesit.c | 8 ++++++-- 9 files changed, 45 insertions(+), 34 deletions(-) diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index f6680ea589c..e16aea6a38d 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi @@ -1628,10 +1628,14 @@ exactly how C and C@t{++} modes define things. Emacs builtin functions already make use some thing definitions. Command @code{treesit-forward-sexp} uses the @code{sexp} definition if -major mode defines it; @code{treesit-forward-sentence} uses the -@code{sentence} definition. Defun movement functions like -@code{treesit-end-of-defun} uses the @code{defun} definition -(@code{defun} definition is overridden by +major mode defines it; @code{treesit-forward-list}, +@code{treesit-down-list}, @code{treesit-up-list}, +@code{treesit-show-paren-data} use the @code{list} definition (its +symbol @code{list} has the symbol property @code{treesit-thing-symbol} +to avoid ambiguity with the function that has the same name); +@code{treesit-forward-sentence} uses the @code{sentence} definition. +Defun movement functions like @code{treesit-end-of-defun} uses the +@code{defun} definition (@code{defun} definition is overridden by @var{treesit-defun-type-regexp} for backward compatibility). Major modes can also define @code{comment}, @code{string}, @code{text} (generally comments and strings). diff --git a/etc/NEWS b/etc/NEWS index 4387bb0010e..5787b0b6ad4 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -871,15 +871,15 @@ override flag by 'treesit-font-lock-setting-query', 'treesit-font-lock-setting-feature', 'treesit-font-lock-setting-enable', and 'treesit-font-lock-setting-override'. -*** New treesit thing 'sexp-list'. +*** New treesit thing 'list'. Unlike the existing thing 'sexp' that defines both lists and atoms, -'sexp-list' defines only lists to be navigated by 'forward-sexp'. -The new function 'treesit-forward-sexp-list' uses 'sexp-list' +'list' defines only lists to be navigated by 'forward-sexp'. +The new function 'treesit-forward-sexp-list' uses 'list' to move across lists. But to move across atoms inside the list it uses 'forward-sexp-default-function'. -*** New tree-sitter based functions for moving by sexp-lists. -If a major mode defines 'sexp-list' in 'treesit-thing-settings', +*** New tree-sitter based functions for moving by lists. +If a major mode defines 'list' in 'treesit-thing-settings', tree-sitter setup for these modes sets 'forward-list-function' to 'treesit-forward-list', 'up-list-function' to 'treesit-up-list', and 'down-list-function' to 'treesit-down-list'. This enables the diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index ac92032f92a..80e635fe76d 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -1151,7 +1151,7 @@ if `c-ts-mode-emacs-sources-support' is non-nil." `(;; It's more useful to include semicolons as sexp so ;; that users can move to the end of a statement. (sexp (not ,(rx (or "{" "}" "[" "]" "(" ")" ",")))) - (sexp-list + (list ,(regexp-opt '("preproc_params" "preproc_parenthesized_expression" "preproc_argument_list" diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 26622ed4bec..635f6d8200e 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3873,7 +3873,7 @@ See `treesit-thing-settings' for more information.") "Nodes that designate sexps in JavaScript. See `treesit-thing-settings' for more information.") -(defvar js--treesit-sexp-list-nodes +(defvar js--treesit-list-nodes '("export_clause" "named_imports" "statement_block" @@ -3936,7 +3936,7 @@ See `treesit-thing-settings' for more information.") (setq-local treesit-thing-settings `((javascript (sexp ,(js--regexp-opt-symbol js--treesit-sexp-nodes)) - (sexp-list ,(js--regexp-opt-symbol js--treesit-sexp-list-nodes)) + (list ,(js--regexp-opt-symbol js--treesit-list-nodes)) (sentence ,(js--regexp-opt-symbol js--treesit-sentence-nodes)) (text ,(js--regexp-opt-symbol '("comment" "string_fragment")))))) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index a776abe881e..15394f28b27 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -1132,7 +1132,7 @@ leading double colon is not added." (equal (treesit-node-type (treesit-node-child node 0)) "("))) -(defun ruby-ts--sexp-list-p (node) +(defun ruby-ts--list-p (node) ;; Distinguish between the named `unless' node and the ;; node with the same value of type. (when (treesit-node-check node 'named) @@ -1213,7 +1213,7 @@ leading double colon is not added." ) eol) #'ruby-ts--sexp-p)) - (sexp-list + (list ,(cons (rx bol (or @@ -1253,7 +1253,7 @@ leading double colon is not added." "array" "hash") eol) - #'ruby-ts--sexp-list-p)) + #'ruby-ts--list-p)) (text ,(lambda (node) (or (member (treesit-node-type node) '("comment" "string_content" "heredoc_content")) diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index 5b886b5da7f..61ae8babbce 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -482,7 +482,7 @@ See `treesit-thing-settings' for more information.") "Nodes that designate sexps in TypeScript. See `treesit-thing-settings' for more information.") -(defvar typescript-ts-mode--sexp-list-nodes +(defvar typescript-ts-mode--list-nodes '("export_clause" "named_imports" "statement_block" @@ -534,7 +534,7 @@ This mode is intended to be inherited by concrete major modes." (setq-local treesit-thing-settings `((typescript (sexp ,(regexp-opt typescript-ts-mode--sexp-nodes 'symbols)) - (sexp-list ,(regexp-opt typescript-ts-mode--sexp-list-nodes + (list ,(regexp-opt typescript-ts-mode--list-nodes 'symbols)) (sentence ,(regexp-opt typescript-ts-mode--sentence-nodes 'symbols)) @@ -619,9 +619,9 @@ at least 3 (which is the default value)." (sexp ,(regexp-opt (append typescript-ts-mode--sexp-nodes '("jsx")))) - (sexp-list ,(concat "^" + (list ,(concat "^" (regexp-opt - (append typescript-ts-mode--sexp-list-nodes + (append typescript-ts-mode--list-nodes '( "jsx_element" "jsx_self_closing_element" diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el index b29e018cfb6..dad49b7ed4c 100644 --- a/lisp/textmodes/html-ts-mode.el +++ b/lisp/textmodes/html-ts-mode.el @@ -117,7 +117,7 @@ Return nil if there is no name or if NODE is not a defun node." "text" "attribute" "value"))) - (sexp-list ,(regexp-opt '("element")) 'symbols) + (list ,(regexp-opt '("element")) 'symbols) (sentence "tag") (text ,(regexp-opt '("comment" "text")))))) diff --git a/lisp/treesit.el b/lisp/treesit.el index 888efeea239..9e967c4a8f8 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -2412,6 +2412,9 @@ delimits medium sized statements in the source code. It is, however, smaller in scope than sentences. This is used by `treesit-forward-sexp' and friends.") +;; Avoid interpreting the symbol `list' as a function. +(put 'list 'treesit-thing-symbol t) + (defun treesit--scan-error (pred arg) (when-let* ((parent (treesit-thing-at (point) pred t)) (boundary (treesit-node-child parent (if (> arg 0) -1 0)))) @@ -2435,7 +2438,7 @@ What constitutes as text and source code sexp is determined by `text' and `sexp' in `treesit-thing-settings'. There is an alternative implementation in `treesit-forward-sexp-list' -that uses `sexp-list' in `treesit-thing-settings' to move only +that uses `list' in `treesit-thing-settings' to move only across lists, whereas uses `forward-sexp-default-function' to move across atoms (such as symbols or words) inside the list." (interactive "^p") @@ -2467,7 +2470,7 @@ Fall back to DEFAULT-FUNCTION as long as it doesn't cross the boundaries of the list. ARG is described in the docstring of `forward-list'." - (let* ((pred (or treesit-sexp-type-regexp 'sexp-list)) + (let* ((pred (or treesit-sexp-type-regexp 'list)) (arg (or arg 1)) (cnt arg) (inc (if (> arg 0) 1 -1))) @@ -2507,7 +2510,7 @@ ARG is described in the docstring of `forward-list'." Whereas `treesit-forward-sexp' moves across both lists and atoms using `sexp' in `treesit-thing-settings', this function uses -`sexp-list' in `treesit-thing-settings' to move only across lists. +`list' in `treesit-thing-settings' to move only across lists. But to move across atoms (such as symbols or words) inside the list it uses `forward-sexp-default-function' as long as it doesn't go outside of the boundaries of the current list. @@ -2518,7 +2521,7 @@ ARG is described in the docstring of `forward-sexp-function'." (defun treesit-forward-list (&optional arg) "Move forward across a list. -What constitutes a list is determined by `sexp-list' in +What constitutes a list is determined by `list' in `treesit-thing-settings' that usually defines parentheses-like expressions. @@ -2535,7 +2538,7 @@ ARG is described in the docstring of `forward-list-function'." (defun treesit-down-list (&optional arg) "Move forward down one level of parentheses. What constitutes a level of parentheses is determined by -`sexp-list' in `treesit-thing-settings' that usually defines +`list' in `treesit-thing-settings' that usually defines parentheses-like expressions. This command is the tree-sitter variant of `down-list' @@ -2543,7 +2546,7 @@ redefined by the variable `down-list-function'. ARG is described in the docstring of `down-list'." (interactive "^p") - (let* ((pred 'sexp-list) + (let* ((pred 'list) (arg (or arg 1)) (cnt arg) (inc (if (> arg 0) 1 -1))) @@ -2575,7 +2578,7 @@ ARG is described in the docstring of `down-list'." (defun treesit-up-list (&optional arg escape-strings no-syntax-crossing) "Move forward out of one level of parentheses. What constitutes a level of parentheses is determined by -`sexp-list' in `treesit-thing-settings' that usually defines +`list' in `treesit-thing-settings' that usually defines parentheses-like expressions. This command is the tree-sitter variant of `up-list' @@ -2583,7 +2586,7 @@ redefined by the variable `up-list-function'. ARG is described in the docstring of `up-list'." (interactive "^p") - (let* ((pred 'sexp-list) + (let* ((pred 'list) (arg (or arg 1)) (cnt arg) (inc (if (> arg 0) 1 -1))) @@ -3068,7 +3071,7 @@ function is called recursively." (setq pos (funcall advance (cond ((and (null next) (null prev) - (not (eq thing 'sexp-list))) + (not (eq thing 'list))) parent) ((> arg 0) next) (t prev)))) @@ -3413,9 +3416,9 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in ;;; Show paren mode (defun treesit-show-paren-data--categorize (pos &optional end-p) - (let* ((pred 'sexp-list) + (let* ((pred 'list) (parent (when (treesit-thing-defined-p - 'sexp-list (treesit-language-at pos)) + pred (treesit-language-at pos)) (treesit-parent-until (treesit-node-at (if end-p (1- pos) pos)) pred))) (first (when parent (treesit-node-child parent 0))) @@ -3587,7 +3590,7 @@ before calling this function." (setq-local forward-sexp-function #'treesit-forward-sexp) (setq-local transpose-sexps-function #'treesit-transpose-sexps)) - (when (treesit-thing-defined-p 'sexp-list nil) + (when (treesit-thing-defined-p 'list nil) (setq-local forward-sexp-function #'treesit-forward-sexp-list) (setq-local forward-list-function #'treesit-forward-list) (setq-local down-list-function #'treesit-down-list) diff --git a/src/treesit.c b/src/treesit.c index b3214dad836..878c1f0b340 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -3618,7 +3618,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred, } if (STRINGP (pred)) return true; - else if (FUNCTIONP (pred)) + else if (FUNCTIONP (pred) + && !(SYMBOLP (pred) && !NILP (Fget (pred, Qtreesit_thing_symbol)))) return true; else if (SYMBOLP (pred)) { @@ -3722,7 +3723,8 @@ treesit_traverse_match_predicate (TSTreeCursor *cursor, Lisp_Object pred, const char *type = ts_node_type (node); return fast_c_string_match (pred, type, strlen (type)) >= 0; } - else if (FUNCTIONP (pred)) + else if (FUNCTIONP (pred) + && !(SYMBOLP (pred) && !NILP (Fget (pred, Qtreesit_thing_symbol)))) { Lisp_Object lisp_node = make_treesit_node (parser, node); return !NILP (CALLN (Ffuncall, pred, lisp_node)); @@ -4333,6 +4335,8 @@ syms_of_treesit (void) DEFSYM (Qtreesit_invalid_predicate, "treesit-invalid-predicate"); DEFSYM (Qtreesit_predicate_not_found, "treesit-predicate-not-found"); + DEFSYM (Qtreesit_thing_symbol, "treesit-thing-symbol"); + DEFSYM (Qor, "or"); #ifdef WINDOWSNT -- 2.39.5