]> git.eshelyaron.com Git - emacs.git/commitdiff
Add 'restricted' tactic in tree-sitter navigation functions
authorYuan Fu <casouri@gmail.com>
Thu, 6 Apr 2023 21:38:00 +0000 (14:38 -0700)
committerYuan Fu <casouri@gmail.com>
Wed, 12 Apr 2023 07:02:20 +0000 (00:02 -0700)
* lisp/treesit.el (treesit-forward-sexp): Add docstring.  Use
'restricted' tactic.
(treesit-transpose-sexps): Fix docstring.
(treesit-beginning-of-thing)
(treesit-end-of-thing): Add support for TACTIC.
(treesit-beginning-of-defun)
(treesit-end-of-defun): Supply treesit-defun-tactic as TACTIC.
(treesit--navigate-thing): Add support for TACTIC.  Wrap the old form
in a new (if (eq tactic 'restricted) (new-code) (old-form)), and
supply the TACTIC parameter when recursing.

lisp/treesit.el

index 4c4ba4ad6ac864be02d4174101d611c04fa7a7cf..9068a1114896795ffa3c27cce24882cd2ce664f8 100644 (file)
@@ -1783,15 +1783,17 @@ however, smaller in scope than sentences.  This is used by
 `treesit-forward-sexp' and friends.")
 
 (defun treesit-forward-sexp (&optional arg)
+  "Tree-sitter implementation for `forward-sexp-function'.
+ARG is described in the docstring of `forward-sexp-function'."
   (interactive "^p")
   (or arg (setq arg 1))
   (funcall
    (if (> arg 0) #'treesit-end-of-thing #'treesit-beginning-of-thing)
-   treesit-sexp-type-regexp (abs arg)))
+   treesit-sexp-type-regexp (abs arg) 'restricted))
 
 (defun treesit-transpose-sexps (&optional arg)
   "Tree-sitter `transpose-sexps' function.
-Arg is the same as in `transpose-sexps'.
+ARG is the same as in `transpose-sexps'.
 
 Locate the node closest to POINT, and transpose that node with
 its sibling node ARG nodes away.
@@ -1896,33 +1898,53 @@ Basically,
       pattern
     (cons pattern nil)))
 
-(defun treesit-beginning-of-thing (pattern &optional arg)
+(defun treesit-beginning-of-thing (pattern &optional arg tactic)
   "Like `beginning-of-defun', but generalized into things.
 
 PATTERN is like `treesit-defun-type-regexp', ARG
 is the same as in `beginning-of-defun'.
 
+TACTIC determines how does this function move between things.  It
+can be `nested', `top-level', `restricted', or nil.  `nested'
+means normal nested navigation: try to move to siblings first,
+and if there aren't enough siblings, move to the parent and its
+siblings.  `top-level' means only consider top-level things, and
+nested things are ignored.  `restricted' means movement is
+restricted inside the thing that encloses POS (i.e., parent),
+should there be one.  If omitted, TACTIC is considered to be
+`nested'.
+
 Return non-nil if successfully moved, nil otherwise."
   (pcase-let* ((arg (or arg 1))
                (`(,regexp . ,pred) (treesit--thing-unpack-pattern
                                     pattern))
                (dest (treesit--navigate-thing
-                      (point) (- arg) 'beg regexp pred)))
+                      (point) (- arg) 'beg regexp pred tactic)))
     (when dest
       (goto-char dest))))
 
-(defun treesit-end-of-thing (pattern &optional arg)
+(defun treesit-end-of-thing (pattern &optional arg tactic)
   "Like `end-of-defun', but generalized into things.
 
 PATTERN is like `treesit-defun-type-regexp', ARG is the same as
 in `end-of-defun'.
 
+TACTIC determines how does this function move between things.  It
+can be `nested', `top-level', `restricted', or nil.  `nested'
+means normal nested navigation: try to move to siblings first,
+and if there aren't enough siblings, move to the parent and its
+siblings.  `top-level' means only consider top-level things, and
+nested things are ignored.  `restricted' means movement is
+restricted inside the thing that encloses POS (i.e., parent),
+should there be one.  If omitted, TACTIC is considered to be
+`nested'.
+
 Return non-nil if successfully moved, nil otherwise."
   (pcase-let* ((arg (or arg 1))
                (`(,regexp . ,pred) (treesit--thing-unpack-pattern
                                     pattern))
                (dest (treesit--navigate-thing
-                      (point) arg 'end regexp pred)))
+                      (point) arg 'end regexp pred tactic)))
     (when dest
       (goto-char dest))))
 
@@ -1943,7 +1965,8 @@ and `treesit-defun-skipper'."
     (catch 'done
       (dotimes (_ 2)
 
-        (when (treesit-beginning-of-thing treesit-defun-type-regexp arg)
+        (when (treesit-beginning-of-thing
+               treesit-defun-type-regexp arg treesit-defun-tactic)
           (when treesit-defun-skipper
             (funcall treesit-defun-skipper)
             (setq success t)))
@@ -1971,7 +1994,8 @@ this function depends on `treesit-defun-type-regexp' and
     (catch 'done
       (dotimes (_ 2) ; Not making progress is better than infloop.
 
-        (when (treesit-end-of-thing treesit-defun-type-regexp arg)
+        (when (treesit-end-of-thing
+               treesit-defun-type-regexp arg treesit-defun-tactic)
           (when treesit-defun-skipper
             (funcall treesit-defun-skipper)))
 
@@ -2144,7 +2168,7 @@ REGEXP and PRED are the same as in `treesit-thing-at-point'."
 ;;    -> Obviously we don't want to go to parent's end, instead, we
 ;;       want to go to parent's prev-sibling's end.  Again, we recurse
 ;;       in the function to do that.
-(defun treesit--navigate-thing (pos arg side regexp &optional pred recursing)
+(defun treesit--navigate-thing (pos arg side regexp &optional pred tactic recursing)
   "Navigate thing ARG steps from POS.
 
 If ARG is positive, move forward that many steps, if negative,
@@ -2157,6 +2181,16 @@ across, return nil.
 
 REGEXP and PRED are the same as in `treesit-thing-at-point'.
 
+TACTIC determines how does this function move between things.  It
+can be `nested', `top-level', `restricted', or nil.  `nested'
+means normal nested navigation: try to move to siblings first,
+and if there aren't enough siblings, move to the parent and its
+siblings.  `top-level' means only consider top-level things, and
+nested things are ignored.  `restricted' means movement is
+restricted inside the thing that encloses POS (i.e., parent),
+should there be one.  If omitted, TACTIC is considered to be
+`nested'.
+
 RECURSING is an internal parameter, if non-nil, it means this
 function is called recursively."
   (pcase-let*
@@ -2178,53 +2212,57 @@ function is called recursively."
           ;; When PARENT is nil, nested and top-level are the same, if
           ;; there is a PARENT, make PARENT to be the top-level parent
           ;; and pretend there is no nested PREV and NEXT.
-          (when (and (eq treesit-defun-tactic 'top-level)
+          (when (and (eq tactic 'top-level)
                      parent)
             (setq parent (treesit--top-level-thing
                           parent regexp pred)
                   prev nil
                   next nil))
-          ;; Move...
-          (if (> arg 0)
-              ;; ...forward.
-              (if (and (eq side 'beg)
-                       ;; Should we skip the defun (recurse)?
-                       (cond (next (and (not recursing) ; [1] (see below)
-                                        (eq pos (funcall advance next))))
-                             (parent t))) ; [2]
-                  ;; Special case: go to next beg-of-defun, but point
-                  ;; is already on beg-of-defun.  Set POS to the end
-                  ;; of next-sib/parent defun, and run one more step.
-                  ;; If there is a next-sib defun, we only need to
-                  ;; recurse once, so we don't need to recurse if we
-                  ;; are already recursing [1]. If there is no
-                  ;; next-sib but a parent, keep stepping out
-                  ;; (recursing) until we got out of the parents until
-                  ;; (1) there is a next sibling defun, or (2) no more
-                  ;; parents [2].
-                  ;;
-                  ;; If point on beg-of-defun but we are already
-                  ;; recurring, that doesn't count as special case,
-                  ;; because we have already made progress (by moving
-                  ;; the end of next before recurring.)
+          ;; If TACTIC is `restricted', the implementation is very simple.
+          (if (eq tactic 'restricted)
+              (setq pos (funcall advance (if (> arg 0) next prev)))
+            ;; For `nested', it's a bit more work:
+            ;; Move...
+            (if (> arg 0)
+                ;; ...forward.
+                (if (and (eq side 'beg)
+                         ;; Should we skip the defun (recurse)?
+                         (cond (next (and (not recursing) ; [1] (see below)
+                                          (eq pos (funcall advance next))))
+                               (parent t))) ; [2]
+                    ;; Special case: go to next beg-of-defun, but point
+                    ;; is already on beg-of-defun.  Set POS to the end
+                    ;; of next-sib/parent defun, and run one more step.
+                    ;; If there is a next-sib defun, we only need to
+                    ;; recurse once, so we don't need to recurse if we
+                    ;; are already recursing [1]. If there is no
+                    ;; next-sib but a parent, keep stepping out
+                    ;; (recursing) until we got out of the parents until
+                    ;; (1) there is a next sibling defun, or (2) no more
+                    ;; parents [2].
+                    ;;
+                    ;; If point on beg-of-defun but we are already
+                    ;; recurring, that doesn't count as special case,
+                    ;; because we have already made progress (by moving
+                    ;; the end of next before recurring.)
+                    (setq pos (or (treesit--navigate-thing
+                                   (treesit-node-end (or next parent))
+                                   1 'beg regexp pred tactic t)
+                                  (throw 'term nil)))
+                  ;; Normal case.
+                  (setq pos (funcall advance (or next parent))))
+              ;; ...backward.
+              (if (and (eq side 'end)
+                       (cond (prev (and (not recursing)
+                                        (eq pos (funcall advance prev))))
+                             (parent t)))
+                  ;; Special case: go to prev end-of-defun.
                   (setq pos (or (treesit--navigate-thing
-                                 (treesit-node-end (or next parent))
-                                 1 'beg regexp pred t)
+                                 (treesit-node-start (or prev parent))
+                                 -1 'end regexp pred tactic t)
                                 (throw 'term nil)))
                 ;; Normal case.
-                (setq pos (funcall advance (or next parent))))
-            ;; ...backward.
-            (if (and (eq side 'end)
-                     (cond (prev (and (not recursing)
-                                      (eq pos (funcall advance prev))))
-                           (parent t)))
-                ;; Special case: go to prev end-of-defun.
-                (setq pos (or (treesit--navigate-thing
-                               (treesit-node-start (or prev parent))
-                               -1 'end regexp pred t)
-                              (throw 'term nil)))
-              ;; Normal case.
-              (setq pos (funcall advance (or prev parent)))))
+                (setq pos (funcall advance (or prev parent))))))
           ;; A successful step! Decrement counter.
           (cl-decf counter))))
     ;; Counter equal to 0 means we successfully stepped ARG steps.