Subject: Re: bug#76676: 30.1.50; C-M-b in c-ts-mode moves too far

From: Juri Linkov <[email-masked]>
Date: Tue, 18 Mar 2025 09:20:59 +0200

>   (defun treesit-toggle-named-thing ()
>     (interactive)
>     (setq-local treesit-sexp-type-regexp
>       (unless treesit-sexp-type-regexp #'treesit-node-named)))
>
> For the example above it toggles between moving to the
> first character of 'if' and moving to the opening brace.

Actually, it's better to toggle between the traditional behavior
and navigation by the 'sexp' thing:

  (defun treesit-toggle-sexp-mode ()
    (interactive)
    (setq-local treesit-sexp-type-regexp
                (unless treesit-sexp-type-regexp
                  (if (treesit-thing-defined-p 'sexp nil)
                      'sexp
                    #'treesit-node-named))
                forward-sexp-function
                (if (eq treesit-sexp-type-regexp 'sexp)
                    #'treesit-forward-sexp
                  #'treesit-forward-sexp-list)))

The only ts-mode where the 'sexp' thing is well defined is c-ts-mode with

  (sexp (not ,(rx (or "{" "}" "[" "]" "(" ")" ","))))

All other ts-modes contain desperate attempts to emulate the
traditional behavior to navigate symbols with the 'sexp' thing.
But symbol navigation is now implemented as the default behavior.

So these ts-modes should be fixed based on the pattern in c-ts-mode.
Then after toggling to the sexp mode they will navigate all ts nodes
except nodes that define boundaries.

Here is an example how such fix will look for ruby-ts-mode:

diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el
index 1594f301641..39113d381c7 100644
--- a/lisp/progmodes/ruby-ts-mode.el
+++ b/lisp/progmodes/ruby-ts-mode.el
@@ -1167,52 +1167,15 @@ ruby-ts-mode
 
   (setq-local treesit-thing-settings
               `((ruby
-                 (sexp ,(cons (rx
-                               bos
-                               (or
-                                "class"
-                                "singleton_class"
-                                "module"
-                                "method"
-                                "singleton_method"
-                                "array"
-                                "hash"
-                                "parenthesized_statements"
-                                "method_parameters"
-                                "array_pattern"
-                                "hash_pattern"
-                                "if"
-                                "else"
-                                "then"
-                                "unless"
-                                "case"
-                                "case_match"
-                                "when"
-                                "while"
-                                "until"
-                                "for"
-                                "block"
-                                "do_block"
-                                "begin"
-                                "integer"
-                                "identifier"
-                                "self"
-                                "super"
-                                "constant"
-                                "simple_symbol"
-                                "hash_key_symbol"
-                                "symbol_array"
-                                "string"
-                                "string_array"
-                                "heredoc_body"
-                                "regex"
-                                "argument_list"
-                                "interpolation"
-                                "instance_variable"
-                                "global_variable"
-                                )
-                               eos)
-                              #'ruby-ts--sexp-p))
+                 (sexp (not (or (and named
+                                     ,(rx (or "program"
+                                              "body_statement")))
+                                (and anonymous
+                                     ,(rx (or "do" "begin"
+                                              "if" "unless"
+                                              "def" "end"
+                                              "{" "}" "[" "]"
+                                              "(" ")" "[" "]" "|"))))))
                  (list ,(cons (rx
                                bos
                                (or