Subject: treesit-thing-settings for elixir-ts-mode

From: Juri Linkov <[email-masked]>
Date: Thu, 06 Mar 2025 20:12:12 +0200

Hi Wilhelm,

Currently I'm adding the new 'list' thing to all ts-modes
and noticed that elixir-ts-mode defines the function
'elixir-ts--forward-sexp'.

This function can be superseded by the 'list' definition
in treesit-thing-settings that supports such commands
as 'forward-sexp', 'forward-list', 'up-list', 'down-list',
and modes 'show-paren-mode', 'hs-minor-mode'.

I have tested all cases, and this patch covers all list nodes.
The only doubtful case is the commented out definition

  ;; (and "\\`call\\'" ,#'elixir-ts--defun-p)

When enabled, it causes too much trouble
with e.g. 'show-paren-mode', etc.
OTOH, this definition is not much needed since
'C-M-a' (beginning-of-defun) and 'C-M-e' (end-of-defun)
already cover this need instead of using 'C-M-f/b',
so 'C-M-a/e' could be used to navigate between functions.
And 'C-M-f/b' navigate in all other cases such as
between 'do' and 'end', parens, etc.
diff --git a/lisp/progmodes/elixir-ts-mode.el b/lisp/progmodes/elixir-ts-mode.el
index d50692d87c0..e5a6448fc8f 100644
--- a/lisp/progmodes/elixir-ts-mode.el
+++ b/lisp/progmodes/elixir-ts-mode.el
@@ -111,13 +111,6 @@ elixir-ts-attribute
   "Face used for attributes in Elixir files."
   :group 'elixir-ts)
 
-(defconst elixir-ts--sexp-regexp
-  (rx bol
-      (or "call" "stab_clause" "binary_operator" "list" "tuple" "map" "pair"
-          "sigil" "string" "atom" "alias" "arguments" "identifier"
-          "boolean" "quoted_content" "bitstring")
-      eol))
-
 (defconst elixir-ts--test-definition-keywords
   '("describe" "test"))
 
@@ -574,21 +567,10 @@ elixir-ts--treesit-range-rules
               (:match "^[HF]$" @_name)
               (quoted_content) @heex)))))
 
-(defvar heex-ts--sexp-regexp)
+(defvar heex-ts--thing-settings)
 (defvar heex-ts--indent-rules)
 (defvar heex-ts--font-lock-settings)
 
-(defun elixir-ts--forward-sexp (&optional arg)
-  "Move forward across one balanced expression (sexp).
-With ARG, do it many times.  Negative ARG means move backward."
-  (or arg (setq arg 1))
-  (funcall
-   (if (> arg 0) #'treesit-end-of-thing #'treesit-beginning-of-thing)
-   (if (eq (treesit-language-at (point)) 'heex)
-       heex-ts--sexp-regexp
-     elixir-ts--sexp-regexp)
-   (abs arg)))
-
 (defun elixir-ts--treesit-anchor-grand-parent-bol (_n parent &rest _)
   "Return the beginning of non-space characters for the parent node of PARENT."
   (save-excursion
@@ -636,6 +618,14 @@ elixir-ts--defun-name
                 (_ nil))))
     (_ nil)))
 
+(defun elixir-ts--with-parens-0-p (node)
+  (equal (treesit-node-type (treesit-node-child node 0))
+         "("))
+
+(defun elixir-ts--with-parens-1-p (node)
+  (equal (treesit-node-type (treesit-node-child node 1))
+         "("))
+
 (defvar elixir-ts--syntax-propertize-query
   (when (treesit-available-p)
     (treesit-query-compile
@@ -722,7 +712,30 @@ elixir-ts-mode
     (setq-local treesit-simple-indent-rules elixir-ts--indent-rules)
 
     ;; Navigation.
-    (setq-local forward-sexp-function #'elixir-ts--forward-sexp)
+    (setq-local treesit-thing-settings
+                `((elixir
+                   (list
+                    (or ;; (and "\\`call\\'" ,#'elixir-ts--defun-p)
+                        (and "\\`arguments\\'" ,#'elixir-ts--with-parens-0-p)
+                        (and "\\`unary_operator\\'" ,#'elixir-ts--with-parens-1-p)
+                        ,(rx bos (or "block"
+                                     "quoted_atom"
+                                     "string"
+                                     "interpolation"
+                                     "sigil"
+                                     "quoted_keyword"
+                                     "list"
+                                     "tuple"
+                                     "bitstring"
+                                     "map"
+                                     "do_block"
+                                     "anonymous_function")
+                             eos)))
+                   (sentence
+                    ,(rx bos (or "call") eos))
+                   (text
+                    ,(rx bos (or "string" "sigil" "comment") eos)))
+                  (heex ,@heex-ts--thing-settings)))
     (setq-local treesit-defun-type-regexp
                 '("call" . elixir-ts--defun-p))
 
diff --git a/lisp/progmodes/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el
index 65aaa0d488d..88f58041c1b 100644
--- a/lisp/progmodes/heex-ts-mode.el
+++ b/lisp/progmodes/heex-ts-mode.el
@@ -56,12 +56,6 @@ heex-ts-indent-offset
   :safe 'integerp
   :group 'heex-ts)
 
-(defconst heex-ts--sexp-regexp
-  (rx bol
-      (or "directive" "tag" "component" "slot"
-          "attribute" "attribute_value" "quoted_attribute_value" "expression")
-      eol))
-
 ;; There seems to be no parent directive block for tree-sitter-heex,
 ;; so we ignore them for now until we learn how to query them.
 ;; https://github.com/phoenixframework/tree-sitter-heex/issues/28
@@ -139,14 +133,24 @@ heex-ts--defun-name
        (treesit-node-child (treesit-node-child node 0) 1) nil)))
     (_ nil)))
 
-(defun heex-ts--forward-sexp (&optional arg)
-  "Move forward across one balanced expression (sexp).
-With ARG, do it many times.  Negative ARG means move backward."
-  (or arg (setq arg 1))
-  (funcall
-   (if (> arg 0) #'treesit-end-of-thing #'treesit-beginning-of-thing)
-   heex-ts--sexp-regexp
-   (abs arg)))
+(defvar heex-ts--thing-settings
+  `((list
+     ,(rx bos (or "doctype"
+                  "tag"
+                  "component"
+                  "slot"
+                  "expression"
+                  "directive"
+                  "comment")
+          eos))
+    (sentence
+     ,(rx bos (or "tag_name"
+                  "component_name"
+                  "attribute")
+          eos))
+    (text
+     ,(rx bos (or "comment" "text") eos)))
+  "`treesit-thing-settings' for HEEx.")
 
 ;;;###autoload
 (define-derived-mode heex-ts-mode html-mode "HEEx"
@@ -158,10 +162,7 @@ heex-ts-mode
 
     ;; Comments
     (setq-local treesit-thing-settings
-                `((heex
-                   (text ,(regexp-opt '("comment" "text"))))))
-
-    (setq-local forward-sexp-function #'heex-ts--forward-sexp)
+                `((heex ,@heex-ts--thing-settings)))
 
     ;; Navigation.
     (setq-local treesit-defun-type-regexp