1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-24 07:20:37 +00:00

Adjust tree-sitter defun navigation (bug#61617)

Before this change, when you use a tree-sitter navigation function to
move to the next beginning of a thing, it jumps over the immediate
next thing and lands you at the beginning of the next-next thing.

Eg, when point is at the "|", and we evaluate
(treesit--navigate-thing pos 1 'beg), we go from

|  (thing) (thing)

to

   (thing) |(thing)

But some might expect point to go to

   |(thing) (thing)

instead, which makes sense.  Also, that's how Emacs expect defun
navigation functions to work.  The discrepancy in expectation causes
bug#61617.

In this change I made tree-sitter navigation functions to work as what
Emacs expects.  And what I described for moving to the next beginning
of thing is similarly applicable to moving to the end of previous end
of thing.

* lisp/treesit.el (treesit-beginning-of-defun)
(treesit-end-of-defun): Handle the case where defun-skipper moves
point back to where we started, by adding a retry.

(treesit--navigate-thing): Add a single condition checking for
progress to the condition form responsible for checking whether to
skip the next defun.  Namely (eq pos (funcall advance next)))).

* test/src/treesit-tests.el:
(treesit--ert-defun-navigation-nested-master)
(treesit--ert-defun-navigation-top-level-master): Change tests to
reflect the new expectation.
This commit is contained in:
Yuan Fu 2023-02-27 00:14:32 -08:00
parent edf5b97686
commit aee10ca1cb
No known key found for this signature in database
GPG Key ID: 56E19BC57664A442
2 changed files with 68 additions and 37 deletions

View File

@ -1828,10 +1828,23 @@ This is a tree-sitter equivalent of `beginning-of-defun'.
Behavior of this function depends on `treesit-defun-type-regexp'
and `treesit-defun-skipper'."
(interactive "^p")
(when (treesit-beginning-of-thing treesit-defun-type-regexp arg)
(when treesit-defun-skipper
(funcall treesit-defun-skipper))
t))
(let ((orig-point (point))
(success nil))
(catch 'done
(dotimes (_ 2)
(when (treesit-beginning-of-thing treesit-defun-type-regexp arg)
(when treesit-defun-skipper
(funcall treesit-defun-skipper)
(setq success t)))
;; If we end up at the same point, it means we went to the
;; next beg-of-defun, but defun skipper moved point back to
;; where we started, in this case we just move one step
;; further.
(if (or (eq arg 0) (not (eq orig-point (point))))
(throw 'done success)
(setq arg (if (> arg 0) (1+ arg) (1- arg))))))))
(defun treesit-end-of-defun (&optional arg _)
"Move forward to next end of defun.
@ -1843,9 +1856,21 @@ This is a tree-sitter equivalent of `end-of-defun'. Behavior of
this function depends on `treesit-defun-type-regexp' and
`treesit-defun-skipper'."
(interactive "^p\nd")
(when (treesit-end-of-thing treesit-defun-type-regexp arg)
(when treesit-defun-skipper
(funcall treesit-defun-skipper))))
(let ((orig-point (point)))
(catch 'done
(dotimes (_ 2) ; Not making progress is better than infloop.
(when (treesit-end-of-thing treesit-defun-type-regexp arg)
(when treesit-defun-skipper
(funcall treesit-defun-skipper)))
;; If we end up at the same point, it means we went to the
;; prev end-of-defun, but defun skipper moved point back to
;; where we started, in this case we just move one step
;; further.
(if (or (eq arg 0) (not (eq orig-point (point))))
(throw 'done nil)
(setq arg (if (> arg 0) (1+ arg) (1- arg))))))))
(defun treesit-default-defun-skipper ()
"Skips spaces after navigating a defun.
@ -1967,9 +1992,9 @@ REGEXP and PRED are the same as in `treesit-thing-at-point'."
;;
;; prev-end (tricky):
;; 1. prev-sibling exists
;; -> If you think about it, we are already at prev-sibling's end!
;; So we need to go one step further, either to
;; prev-prev-sibling's end, or parent's prev-sibling's end, etc.
;; -> If we are already at prev-sibling's end, we need to go one
;; step further, either to prev-prev-sibling's end, or parent's
;; prev-sibling's end, etc.
;; 2. prev-sibling is nil but parent exists
;; -> 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
@ -2019,18 +2044,24 @@ function is called recursively."
;; ...forward.
(if (and (eq side 'beg)
;; Should we skip the defun (recurse)?
(cond (next (not recursing)) ; [1] (see below)
(parent t) ; [2]
(t nil)))
;; Special case: go to next 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
(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 t)
@ -2039,9 +2070,9 @@ function is called recursively."
(setq pos (funcall advance (or next parent))))
;; ...backward.
(if (and (eq side 'end)
(cond (prev (not recursing))
(parent t)
(t nil)))
(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))

View File

@ -977,22 +977,22 @@ and \"]\"."
(defvar treesit--ert-defun-navigation-nested-master
;; START PREV-BEG NEXT-END PREV-END NEXT-BEG
'((0 103 105 102 106) ; Between Beg of parent & 1st sibling.
'((0 103 105 102 104) ; Between Beg of parent & 1st sibling.
(1 103 105 102 106) ; Beg of 1st sibling.
(2 104 105 102 106) ; Inside 1st sibling.
(3 104 107 102 109) ; End of 1st sibling.
(4 104 107 102 109) ; Between 1st sibling & 2nd sibling.
(5 104 107 102 109) ; Beg of 2nd sibling.
(3 104 107 102 106) ; End of 1st sibling.
(4 104 107 105 106) ; Between 1st sibling & 2nd sibling.
(5 104 107 105 109) ; Beg of 2nd sibling.
(6 106 107 105 109) ; Inside 2nd sibling.
(7 106 108 105 109) ; End of 2nd sibling.
(8 106 108 105 109) ; Between 2nd sibling & end of parent.
(9 103 110 102 nil) ; End of parent.
(8 106 108 107 109) ; Between 2nd sibling & end of parent.
(9 103 110 102 109) ; End of parent.
(100 nil 102 nil 103) ; Before 1st parent.
(100 nil 102 nil 101) ; Before 1st parent.
(101 nil 102 nil 103) ; Beg of 1st parent.
(102 101 108 nil 109) ; Between 1st & 2nd parent.
(103 101 108 nil 109) ; Beg of 2nd parent.
(110 109 nil 108 nil) ; After 3rd parent.
(102 101 108 102 103) ; Between 1st & 2nd parent.
(103 101 108 102 109) ; Beg of 2nd parent.
(110 109 nil 110 nil) ; After 3rd parent.
)
"Master of nested navigation test.
@ -1000,7 +1000,7 @@ This basically says, e.g., \"start with point on marker 0, go to
the prev-beg, now point should be at marker 103\", etc.")
(defvar treesit--ert-defun-navigation-top-level-master
;; START PREV-BEG NEXT-END NEXT-BEG PREV-END
;; START PREV-BEG NEXT-END PREV-END NEXT-BEG
'((0 103 108 102 109) ; Between Beg of parent & 1st sibling.
(1 103 108 102 109) ; Beg of 1st sibling.
(2 103 108 102 109) ; Inside 1st sibling.
@ -1010,14 +1010,14 @@ the prev-beg, now point should be at marker 103\", etc.")
(6 103 108 102 109) ; Inside 2nd sibling.
(7 103 108 102 109) ; End of 2nd sibling.
(8 103 108 102 109) ; Between 2nd sibling & end of parent.
(9 103 110 102 nil) ; End of parent.
(9 103 110 102 109) ; End of parent.
;; Top-level defuns should be identical to the nested test.
(100 nil 102 nil 103) ; Before 1st parent.
(100 nil 102 nil 101) ; Before 1st parent.
(101 nil 102 nil 103) ; Beg of 1st parent.
(102 101 108 nil 109) ; Between 1st & 2nd parent.
(103 101 108 nil 109) ; Beg of 2nd parent.
(110 109 nil 108 nil) ; After 3rd parent.
(102 101 108 102 103) ; Between 1st & 2nd parent.
(103 101 108 102 109) ; Beg of 2nd parent.
(110 109 nil 110 nil) ; After 3rd parent.
)
"Master of top-level navigation test.")