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. '<h1>...</h1>' in html-ts-mode.
(treesit-hs-find-next-block, treesit-hs-inside-comment-p):
Use anchors for "\\`comment\\'" (bug#75609).
This commit is contained in:
Juri Linkov 2025-02-09 19:54:02 +02:00
parent 7e60ceeccb
commit e9d17e4197
5 changed files with 68 additions and 16 deletions

View file

@ -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))

View file

@ -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

View file

@ -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)

View file

@ -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))

View file

@ -3512,7 +3512,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))))
@ -3586,7 +3586,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.
@ -3621,7 +3621,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)))))