Subject: 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 19:49:01 +0200

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

Ok, here is more complete patch with a small improvement in c-ts-mode,
and using the same pattern in java/css/ruby-ts-mode:

diff --git a/lisp/treesit.el b/lisp/treesit.el
index 46332cb1e4b..f5cd3f2067c 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -3310,6 +3317,24 @@ treesit-end-of-thing
     (when dest
       (goto-char dest))))
 
+(defun treesit-toggle-sexp-mode ()
+  "Toggle the mode of navigation for sexp commands."
+  (interactive)
+  (when (treesit-thing-defined-p 'list nil)
+    (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 treesit-sexp-type-regexp
+                    #'treesit-forward-sexp
+                  #'treesit-forward-sexp-list))
+    (message "Toggle to mode where sexp commands navigate %s"
+             (or (and treesit-sexp-type-regexp
+                      "treesit nodes")
+                 "syntax symbols and treesit lists"))))
+
 (defun treesit-beginning-of-defun (&optional arg)
   "Move backward to the beginning of a defun.
 
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index fa5f8567b60..285d38e6411 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -1194,7 +1194,11 @@ c-ts-mode--emacs-current-defun-name
 (defvar c-ts-mode--thing-settings
   `(;; 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 (not (or (and named
+                        ,(rx bos (or "translation_unit") eos))
+                   (and anonymous
+                        ,(rx (or "{" "}" "[" "]" "(" ")"
+                                 "," ";"))))))
     (list
      ,(regexp-opt '("preproc_params"
                     "preproc_if"
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
index c4b2282b574..cd18d6d1e28 100644
--- a/lisp/progmodes/java-ts-mode.el
+++ b/lisp/progmodes/java-ts-mode.el
@@ -436,19 +436,12 @@ java-ts-mode
 
     (setq-local treesit-thing-settings
                 `((java
-                   (sexp ,(rx (or "annotation"
-                                  "parenthesized_expression"
-                                  "argument_list"
-                                  "identifier"
-                                  "modifiers"
-                                  "block"
-                                  "body"
-                                  "literal"
-                                  "access"
-                                  "reference"
-                                  "_type"
-                                  "true"
-                                  "false")))
+                   (sexp (not (or (and named
+                                       ,(rx bos (or "program") eos))
+                                  (and anonymous
+                                       ,(rx (or "{" "}" "[" "]"
+                                                "(" ")" "<" ">"
+                                                "," ";"))))))
                    (list ,(rx bos (or "inferred_parameters"
                                       "parenthesized_expression"
                                       "argument_list"
diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el
index 1594f301641..e8fb0f62d46 100644
--- a/lisp/progmodes/ruby-ts-mode.el
+++ b/lisp/progmodes/ruby-ts-mode.el
@@ -1167,52 +1167,17 @@ 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 bos (or "program"
+                                                  "body_statement"
+                                                  "then")
+                                          eos))
+                                (and anonymous
+                                     ,(rx (or "do" "begin"
+                                              "if" "unless"
+                                              "def" "end"
+                                              "{" "}" "[" "]"
+                                              "(" ")" "[" "]" "|"))))))
                  (list ,(cons (rx
                                bos
                                (or
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 02c3618d281..9d063646184 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -1781,7 +1781,13 @@ css--extract-index-name
            res)))))))
 
 (defvar css--treesit-thing-settings
-  `((css (list
+  `((css (sexp
+          (not (or (and named
+                        ,(rx bos (or "stylesheet") eos))
+                   (and anonymous
+                        ,(rx (or "{" "}" "[" "]" "(" ")"
+                                 "," ";"))))))
+         (list
           ,(rx bos (or "keyframe_block_list"
                        "block"
                        "pseudo_class_arguments"