From 4a6bb2f53572b39b1dfc13d75a443c08b5513bc0 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Sun, 9 Feb 2025 19:54:02 +0200 Subject: [PATCH] Improve treesit settings for {json,html,toml,yaml}-ts-mode (bug#73404) * lisp/progmodes/json-ts-mode.el (json-ts-mode): Add 'list' thing to 'treesit-thing-settings'. (json-ts-mode): Disable outlines. * lisp/textmodes/html-ts-mode.el (html-ts-mode--defun-name): Get a grandchild 'tag_name' from 'element' that was already defined by 'treesit-defun-type-regexp'. (html-ts-mode--outline-predicate): New function. (html-ts-mode): Add "comment" to the 'list' thing in 'treesit-thing-settings'. Use "tag_name" and "attribute" in 'sentence' to conform to sentence navigating arguments in other ts-modes. Remove unnecessary heading from 'treesit-simple-imenu-settings' and use "element" supported by 'html-ts-mode--defun-name'. Set 'treesit-outline-predicate' to 'html-ts-mode--outline-predicate'. * lisp/textmodes/toml-ts-mode.el (toml-ts-mode): Add 'treesit-thing-settings'. * lisp/textmodes/yaml-ts-mode.el (yaml-ts-mode--defun-name) (yaml-ts-mode--outline-predicate): New functions. (yaml-ts-mode): Set 'treesit-defun-type-regexp', 'treesit-defun-name-function', 'treesit-defun-tactic'. Add 'sentence' to 'treesit-thing-settings'. Set 'treesit-simple-imenu-settings' and 'treesit-outline-predicate'. Use 'kill-local-variable' for 'forward-sexp-function' and 'show-paren-data-function' instead of resetting their value. * lisp/treesit.el (treesit-outline-search): Check for the thing before the end of the line to support such case when the thing fits on the current line and ends before the end of the line such as e.g. '

...

' in html-ts-mode. (treesit-hs-find-next-block, treesit-hs-inside-comment-p): Use anchors for "\\`comment\\'" (bug#75609). (cherry picked from commit e9d17e41971c887675d52e1dcee21978d4247130) --- lisp/progmodes/json-ts-mode.el | 8 +++++++- lisp/textmodes/html-ts-mode.el | 29 ++++++++++++++++++++++------- lisp/textmodes/toml-ts-mode.el | 8 ++++++++ lisp/textmodes/yaml-ts-mode.el | 33 ++++++++++++++++++++++++++++----- lisp/treesit.el | 6 +++--- 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el index f26ba9e8d63..1b8f033e97a 100644 --- a/lisp/progmodes/json-ts-mode.el +++ b/lisp/progmodes/json-ts-mode.el @@ -152,6 +152,7 @@ Return nil if there is no name or if NODE is not a defun node." (setq-local treesit-thing-settings `((json + (list ,(rx (or "object" "array"))) (sentence "pair")))) ;; Font-lock. @@ -165,7 +166,12 @@ Return nil if there is no name or if NODE is not a defun node." (setq-local treesit-simple-imenu-settings '((nil "\\`pair\\'" nil nil))) - (treesit-major-mode-setup)) + (treesit-major-mode-setup) + + ;; Disable outlines since they are created for 'pair' from + ;; 'treesit-simple-imenu-settings' almost on every line: + (kill-local-variable 'outline-search-function) + (kill-local-variable 'outline-level)) (derived-mode-add-parents 'json-ts-mode '(json-mode)) diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el index dad49b7ed4c..7e6c3e0b7d1 100644 --- a/lisp/textmodes/html-ts-mode.el +++ b/lisp/textmodes/html-ts-mode.el @@ -90,8 +90,20 @@ (defun html-ts-mode--defun-name (node) "Return the defun name of NODE. Return nil if there is no name or if NODE is not a defun node." - (when (equal (treesit-node-type node) "tag_name") - (treesit-node-text node t))) + (when (string-match-p "element" (treesit-node-type node)) + (treesit-node-text + (treesit-search-subtree node "\\`tag_name\\'" nil nil 2) + t))) + +(defun html-ts-mode--outline-predicate (node) + "Limit outlines to a few most meaningful elements." + (let ((name (html-ts-mode--defun-name node))) + (and name (string-match-p + (rx bos (or "html" "head" "script" "style" + "body" (and "h" (any "1-6")) + "ol" "ul" "table") + eos) + name)))) ;;;###autoload (define-derived-mode html-ts-mode html-mode "HTML" @@ -108,7 +120,6 @@ Return nil if there is no name or if NODE is not a defun node." ;; Navigation. (setq-local treesit-defun-type-regexp "element") - (setq-local treesit-defun-name-function #'html-ts-mode--defun-name) (setq-local treesit-thing-settings @@ -117,8 +128,12 @@ Return nil if there is no name or if NODE is not a defun node." "text" "attribute" "value"))) - (list ,(regexp-opt '("element")) 'symbols) - (sentence "tag") + (list ,(rx (or + ;; Also match script_element and style_element + "element" + ;; HTML comments have the element syntax + "comment"))) + (sentence ,(rx (and bos (or "tag_name" "attribute") eos))) (text ,(regexp-opt '("comment" "text")))))) ;; Font-lock. @@ -130,10 +145,10 @@ Return nil if there is no name or if NODE is not a defun node." ;; Imenu. (setq-local treesit-simple-imenu-settings - '(("Element" "\\`tag_name\\'" nil nil))) + '((nil "element" nil nil))) ;; Outline minor mode. - (setq-local treesit-outline-predicate "\\`element\\'") + (setq-local treesit-outline-predicate #'html-ts-mode--outline-predicate) ;; `html-ts-mode' inherits from `html-mode' that sets ;; regexp-based outline variables. So need to restore ;; the default values of outline variables to be able diff --git a/lisp/textmodes/toml-ts-mode.el b/lisp/textmodes/toml-ts-mode.el index 759995590f6..3b55fe7706a 100644 --- a/lisp/textmodes/toml-ts-mode.el +++ b/lisp/textmodes/toml-ts-mode.el @@ -137,6 +137,14 @@ Return nil if there is no name or if NODE is not a defun node." (setq-local treesit-defun-type-regexp (rx (or "table" "table_array_element"))) (setq-local treesit-defun-name-function #'toml-ts-mode--defun-name) + (setq-local treesit-thing-settings + `((toml + (list + ,(rx bos (or "array" "inline_table") eos)) + (sentence + ,(rx bos (or "pair") eos)) + (text + ,(rx bos (or "comment") eos))))) ;; Font-lock. (setq-local treesit-font-lock-settings toml-ts-mode--font-lock-settings) diff --git a/lisp/textmodes/yaml-ts-mode.el b/lisp/textmodes/yaml-ts-mode.el index defef096aa6..7a5132634ca 100644 --- a/lisp/textmodes/yaml-ts-mode.el +++ b/lisp/textmodes/yaml-ts-mode.el @@ -141,6 +141,19 @@ boundaries. JUSTIFY is passed to `fill-paragraph'." (fill-region start-marker end justify)) t)))) +(defun yaml-ts-mode--defun-name (node) + "Return the defun name of NODE. +Return nil if there is no name or if NODE is not a defun node." + (when (equal (treesit-node-type node) "block_mapping_pair") + (treesit-node-text (treesit-node-child-by-field-name + node "key") + t))) + +(defun yaml-ts-mode--outline-predicate (node) + "Limit outlines to top-level mappings." + (when (equal (treesit-node-type node) "block_mapping_pair") + (not (treesit-parent-until node treesit-outline-predicate)))) + ;;;###autoload (define-derived-mode yaml-ts-mode text-mode "YAML" "Major mode for editing YAML, powered by tree-sitter." @@ -168,11 +181,21 @@ boundaries. JUSTIFY is passed to `fill-paragraph'." (setq-local fill-paragraph-function #'yaml-ts-mode--fill-paragraph) ;; Navigation. + (setq-local treesit-defun-type-regexp "block_mapping_pair") + (setq-local treesit-defun-name-function #'yaml-ts-mode--defun-name) + (setq-local treesit-defun-tactic 'top-level) + (setq-local treesit-thing-settings `((yaml - (list ,(regexp-opt '("block_mapping_pair" - "flow_sequence")) - 'symbols)))) + (list ,(rx (or "block_mapping_pair" "flow_sequence"))) + (sentence ,"block_mapping_pair")))) + + ;; Imenu. + (setq-local treesit-simple-imenu-settings + '((nil "\\`block_mapping_pair\\'" nil nil))) + + ;; Outline minor mode. + (setq-local treesit-outline-predicate #'yaml-ts-mode--outline-predicate) (treesit-major-mode-setup) @@ -181,8 +204,8 @@ boundaries. JUSTIFY is passed to `fill-paragraph'." ;; with `C-M-f', `C-M-b' neither adapt to 'show-paren-mode' ;; that is problematic in languages without explicit ;; opening/closing nodes. - (setq-local forward-sexp-function nil) - (setq-local show-paren-data-function 'show-paren--default))) + (kill-local-variable 'forward-sexp-function) + (kill-local-variable 'show-paren-data-function))) (derived-mode-add-parents 'yaml-ts-mode '(yaml-mode)) diff --git a/lisp/treesit.el b/lisp/treesit.el index d4ffb7188d8..e29f757d36a 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -3505,7 +3505,7 @@ when a major mode sets it.") For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in `outline-search-function'." (if looking-at - (when-let* ((node (or (treesit-thing-at (pos-eol) treesit-outline-predicate) + (when-let* ((node (or (treesit-thing-at (1- (pos-eol)) treesit-outline-predicate) (treesit-thing-at (pos-bol) treesit-outline-predicate))) (start (treesit-node-start node))) (eq (pos-bol) (save-excursion (goto-char start) (pos-bol)))) @@ -3579,7 +3579,7 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in (let* ((comment-pred (when comments (if (treesit-thing-defined-p 'comment (treesit-language-at (point))) - 'comment "comment"))) + 'comment "\\`comment\\'"))) (pred (if comment-pred (append '(or list) (list comment-pred)) 'list)) ;; `treesit-navigate-thing' can't find a thing at bobp, ;; so use `treesit-thing-at' to match at bobp. @@ -3614,7 +3614,7 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in "Tree-sitter implementation of `hs-inside-comment-p-func'." (let* ((comment-pred (if (treesit-thing-defined-p 'comment (treesit-language-at (point))) - 'comment "comment")) + 'comment "\\`comment\\'")) (thing (or (treesit-thing-at (point) comment-pred) (unless (bobp) (treesit-thing-at (1- (point)) comment-pred))))) -- 2.39.5