]> git.eshelyaron.com Git - emacs.git/commitdiff
New treesit thing 'sexp-list' (bug#73404)
authorJuri Linkov <juri@linkov.net>
Thu, 19 Dec 2024 07:27:28 +0000 (09:27 +0200)
committerEshel Yaron <me@eshelyaron.com>
Mon, 23 Dec 2024 15:15:32 +0000 (16:15 +0100)
* lisp/treesit.el (treesit-forward-list): New command.
(treesit-forward-sexp): Check the thing 'text' only when
'treesit-sexp-type-regexp' is nil.
(treesit-forward-sexp-list): New function.
(treesit-navigate-thing): Don't try moving to parent
when thing is 'sexp-list'.
(treesit-major-mode-setup): Set 'forward-sexp-function' to
'treesit-forward-sexp-list' when the 'sexp-list' thing is defined.

(cherry picked from commit d930d7f8651897dc3130ff16731751691566d490)

etc/NEWS
lisp/treesit.el

index 7dff82b8cbdeaca65d4c7418ccdc82d65fe19894..bf4c890dfdd092aafa644c9a9372dfa61fdb0bf7 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -821,6 +821,13 @@ override flag by 'treesit-font-lock-setting-query',
 'treesit-font-lock-setting-feature', 'treesit-font-lock-setting-enable',
 and 'treesit-font-lock-setting-override'.
 
+*** New treesit thing 'sexp-list'.
+Unlike the existing thing 'sexp' that defines both lists and atoms,
+'sexp-list' defines only lists to be navigated by 'forward-sexp'.
+The new function 'treesit-forward-sexp-list' uses 'sexp-list'
+to move across lists.  But to move across atoms inside the list
+it uses `forward-sexp-default-function'.
+
 +++
 ** New optional BUFFER argument for 'string-pixel-width'.
 If supplied, 'string-pixel-width' will use any face remappings from
index 6cb3763191955d5d10a290da3c4a5eeec6af7234..0fa0862f441dffc86c258f973d21c7ac7c34523d 100644 (file)
@@ -2358,6 +2358,25 @@ delimits medium sized statements in the source code.  It is,
 however, smaller in scope than sentences.  This is used by
 `treesit-forward-sexp' and friends.")
 
+(defun treesit-forward-list (&optional arg)
+  "Move forward across a list.
+What constitutes a list is determined by `sexp-list' in
+`treesit-thing-settings' that usually defines
+parentheses-like expressions.
+
+Unlike `forward-sexp', this command moves only across a list,
+but not across atoms (such as symbols or words) inside the list.
+
+This command is the tree-sitter variant of `forward-list'.  But since
+`forward-list' has no \"forward-list-function\" like there is
+`forward-sexp-function' for `forward-sexp', this command
+can be used on its own.
+
+ARG is described in the docstring of `forward-list'."
+  (interactive "^p")
+  (let ((treesit-sexp-type-regexp 'sexp-list))
+    (treesit-forward-sexp arg)))
+
 (defun treesit-forward-sexp (&optional arg)
   "Tree-sitter implementation for `forward-sexp-function'.
 
@@ -2371,12 +2390,18 @@ signal `scan-error' like `forward-sexp' does.  If point is already
 at top-level, return nil without moving point.
 
 What constitutes as text and source code sexp is determined
-by `text' and `sexp' in `treesit-thing-settings'."
+by `text' and `sexp' in `treesit-thing-settings'.
+
+There is an alternative implementation in `treesit-forward-sexp-list'
+that uses `sexp-list' in `treesit-thing-settings' to move only
+across lists, whereas uses `forward-sexp-default-function' to move
+across atoms (such as symbols or words) inside the list."
   (interactive "^p")
   (let ((arg (or arg 1))
         (pred (or treesit-sexp-type-regexp 'sexp))
         (node-at-point
-         (treesit-node-at (point) (treesit-language-at (point)))))
+         (when (null treesit-sexp-type-regexp)
+           (treesit-node-at (point) (treesit-language-at (point))))))
     (or (when (and node-at-point
                    ;; Make sure point is strictly inside node.
                    (< (treesit-node-start node-at-point)
@@ -2400,6 +2425,60 @@ by `text' and `sexp' in `treesit-thing-settings'."
                                     (treesit-node-start boundary)
                                     (treesit-node-end boundary)))))))
 
+(defun treesit-forward-sexp-list (&optional arg)
+  "Alternative tree-sitter implementation for `forward-sexp-function'.
+
+Whereas `treesit-forward-sexp' moves across both lists and atoms
+using `sexp' in `treesit-thing-settings', this function uses
+`sexp-list' in `treesit-thing-settings' to move only across lists.
+But to move across atoms (such as symbols or words) inside the list
+it uses `forward-sexp-default-function' as long as it doesn't go
+outside of the boundaries of the current list.
+
+ARG is described in the docstring of `forward-sexp-function'."
+  (interactive "^p")
+  (let* ((arg (or arg 1))
+         (pred 'sexp-list)
+         (default-pos
+          (condition-case _
+              (save-excursion
+                (forward-sexp-default-function arg)
+                (point))
+            (scan-error nil)))
+         (default-pos (unless (eq (point) default-pos) default-pos))
+         (sibling-pos
+          (when default-pos
+            (save-excursion
+              (and (if (> arg 0)
+                       (treesit-end-of-thing pred (abs arg) 'restricted)
+                     (treesit-beginning-of-thing pred (abs arg) 'restricted))
+                   (point)))))
+         (sibling (when sibling-pos
+                    (if (> arg 0)
+                        (treesit-thing-prev sibling-pos pred)
+                      (treesit-thing-next sibling-pos pred))))
+         (sibling (when (and sibling
+                             (if (> arg 0)
+                                 (<= (point) (treesit-node-start sibling))
+                               (>= (point) (treesit-node-end sibling))))
+                    sibling))
+         (current-thing (when default-pos
+                          (treesit-thing-at (point) pred t))))
+
+    ;; 'forward-sexp-default-function' should not go out of the current thing,
+    ;; neither go inside the next thing or go over the next thing
+    (or (when (and default-pos
+                   (or (null current-thing)
+                       (if (> arg 0)
+                           (< default-pos (treesit-node-end current-thing))
+                         (> default-pos (treesit-node-start current-thing))))
+                   (or (null sibling)
+                       (if (> arg 0)
+                           (<= default-pos (treesit-node-start sibling))
+                         (>= default-pos (treesit-node-end sibling)))))
+          (goto-char default-pos))
+        (treesit-forward-list arg))))
+
 (defun treesit-transpose-sexps (&optional arg)
   "Tree-sitter `transpose-sexps' function.
 ARG is the same as in `transpose-sexps'.
@@ -2849,7 +2928,9 @@ function is called recursively."
           (if (eq tactic 'restricted)
               (setq pos (funcall
                          advance
-                         (cond ((and (null next) (null prev)) parent)
+                         (cond ((and (null next) (null prev)
+                                     (not (eq thing 'sexp-list)))
+                                parent)
                                ((> arg 0) next)
                                (t prev))))
             ;; For `nested', it's a bit more work:
@@ -3246,6 +3327,9 @@ before calling this function."
     (setq-local forward-sexp-function #'treesit-forward-sexp)
     (setq-local transpose-sexps-function #'treesit-transpose-sexps))
 
+  (when (treesit-thing-defined-p 'sexp-list nil)
+    (setq-local forward-sexp-function #'treesit-forward-sexp-list))
+
   (when (treesit-thing-defined-p 'sentence nil)
     (setq-local forward-sentence-function #'treesit-forward-sentence))