diff options
| author | Yuan Fu | 2023-02-27 00:14:32 -0800 |
|---|---|---|
| committer | Yuan Fu | 2023-02-27 00:14:32 -0800 |
| commit | aee10ca1cbee1d653f89f028c34066bf3ebb32ab (patch) | |
| tree | 2f95d63c61b4acb2dbd35965c866da045cf80caf | |
| parent | edf5b97686908114f254b5077c71e8202149545f (diff) | |
| download | emacs-aee10ca1cbee1d653f89f028c34066bf3ebb32ab.tar.gz emacs-aee10ca1cbee1d653f89f028c34066bf3ebb32ab.zip | |
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.
| -rw-r--r-- | lisp/treesit.el | 73 | ||||
| -rw-r--r-- | test/src/treesit-tests.el | 32 |
2 files changed, 68 insertions, 37 deletions
diff --git a/lisp/treesit.el b/lisp/treesit.el index 6b4db2a990c..052f641abfd 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el | |||
| @@ -1828,10 +1828,23 @@ This is a tree-sitter equivalent of `beginning-of-defun'. | |||
| 1828 | Behavior of this function depends on `treesit-defun-type-regexp' | 1828 | Behavior of this function depends on `treesit-defun-type-regexp' |
| 1829 | and `treesit-defun-skipper'." | 1829 | and `treesit-defun-skipper'." |
| 1830 | (interactive "^p") | 1830 | (interactive "^p") |
| 1831 | (when (treesit-beginning-of-thing treesit-defun-type-regexp arg) | 1831 | (let ((orig-point (point)) |
| 1832 | (when treesit-defun-skipper | 1832 | (success nil)) |
| 1833 | (funcall treesit-defun-skipper)) | 1833 | (catch 'done |
| 1834 | t)) | 1834 | (dotimes (_ 2) |
| 1835 | |||
| 1836 | (when (treesit-beginning-of-thing treesit-defun-type-regexp arg) | ||
| 1837 | (when treesit-defun-skipper | ||
| 1838 | (funcall treesit-defun-skipper) | ||
| 1839 | (setq success t))) | ||
| 1840 | |||
| 1841 | ;; If we end up at the same point, it means we went to the | ||
| 1842 | ;; next beg-of-defun, but defun skipper moved point back to | ||
| 1843 | ;; where we started, in this case we just move one step | ||
| 1844 | ;; further. | ||
| 1845 | (if (or (eq arg 0) (not (eq orig-point (point)))) | ||
| 1846 | (throw 'done success) | ||
| 1847 | (setq arg (if (> arg 0) (1+ arg) (1- arg)))))))) | ||
| 1835 | 1848 | ||
| 1836 | (defun treesit-end-of-defun (&optional arg _) | 1849 | (defun treesit-end-of-defun (&optional arg _) |
| 1837 | "Move forward to next end of defun. | 1850 | "Move forward to next end of defun. |
| @@ -1843,9 +1856,21 @@ This is a tree-sitter equivalent of `end-of-defun'. Behavior of | |||
| 1843 | this function depends on `treesit-defun-type-regexp' and | 1856 | this function depends on `treesit-defun-type-regexp' and |
| 1844 | `treesit-defun-skipper'." | 1857 | `treesit-defun-skipper'." |
| 1845 | (interactive "^p\nd") | 1858 | (interactive "^p\nd") |
| 1846 | (when (treesit-end-of-thing treesit-defun-type-regexp arg) | 1859 | (let ((orig-point (point))) |
| 1847 | (when treesit-defun-skipper | 1860 | (catch 'done |
| 1848 | (funcall treesit-defun-skipper)))) | 1861 | (dotimes (_ 2) ; Not making progress is better than infloop. |
| 1862 | |||
| 1863 | (when (treesit-end-of-thing treesit-defun-type-regexp arg) | ||
| 1864 | (when treesit-defun-skipper | ||
| 1865 | (funcall treesit-defun-skipper))) | ||
| 1866 | |||
| 1867 | ;; If we end up at the same point, it means we went to the | ||
| 1868 | ;; prev end-of-defun, but defun skipper moved point back to | ||
| 1869 | ;; where we started, in this case we just move one step | ||
| 1870 | ;; further. | ||
| 1871 | (if (or (eq arg 0) (not (eq orig-point (point)))) | ||
| 1872 | (throw 'done nil) | ||
| 1873 | (setq arg (if (> arg 0) (1+ arg) (1- arg)))))))) | ||
| 1849 | 1874 | ||
| 1850 | (defun treesit-default-defun-skipper () | 1875 | (defun treesit-default-defun-skipper () |
| 1851 | "Skips spaces after navigating a defun. | 1876 | "Skips spaces after navigating a defun. |
| @@ -1967,9 +1992,9 @@ REGEXP and PRED are the same as in `treesit-thing-at-point'." | |||
| 1967 | ;; | 1992 | ;; |
| 1968 | ;; prev-end (tricky): | 1993 | ;; prev-end (tricky): |
| 1969 | ;; 1. prev-sibling exists | 1994 | ;; 1. prev-sibling exists |
| 1970 | ;; -> If you think about it, we are already at prev-sibling's end! | 1995 | ;; -> If we are already at prev-sibling's end, we need to go one |
| 1971 | ;; So we need to go one step further, either to | 1996 | ;; step further, either to prev-prev-sibling's end, or parent's |
| 1972 | ;; prev-prev-sibling's end, or parent's prev-sibling's end, etc. | 1997 | ;; prev-sibling's end, etc. |
| 1973 | ;; 2. prev-sibling is nil but parent exists | 1998 | ;; 2. prev-sibling is nil but parent exists |
| 1974 | ;; -> Obviously we don't want to go to parent's end, instead, we | 1999 | ;; -> Obviously we don't want to go to parent's end, instead, we |
| 1975 | ;; want to go to parent's prev-sibling's end. Again, we recurse | 2000 | ;; want to go to parent's prev-sibling's end. Again, we recurse |
| @@ -2019,18 +2044,24 @@ function is called recursively." | |||
| 2019 | ;; ...forward. | 2044 | ;; ...forward. |
| 2020 | (if (and (eq side 'beg) | 2045 | (if (and (eq side 'beg) |
| 2021 | ;; Should we skip the defun (recurse)? | 2046 | ;; Should we skip the defun (recurse)? |
| 2022 | (cond (next (not recursing)) ; [1] (see below) | 2047 | (cond (next (and (not recursing) ; [1] (see below) |
| 2023 | (parent t) ; [2] | 2048 | (eq pos (funcall advance next)))) |
| 2024 | (t nil))) | 2049 | (parent t))) ; [2] |
| 2025 | ;; Special case: go to next beg-of-defun. Set POS | 2050 | ;; Special case: go to next beg-of-defun, but point |
| 2026 | ;; to the end of next-sib/parent defun, and run one | 2051 | ;; is already on beg-of-defun. Set POS to the end |
| 2027 | ;; more step. If there is a next-sib defun, we only | 2052 | ;; of next-sib/parent defun, and run one more step. |
| 2028 | ;; need to recurse once, so we don't need to recurse | 2053 | ;; If there is a next-sib defun, we only need to |
| 2029 | ;; if we are already recursing [1]. If there is no | 2054 | ;; recurse once, so we don't need to recurse if we |
| 2055 | ;; are already recursing [1]. If there is no | ||
| 2030 | ;; next-sib but a parent, keep stepping out | 2056 | ;; next-sib but a parent, keep stepping out |
| 2031 | ;; (recursing) until we got out of the parents until | 2057 | ;; (recursing) until we got out of the parents until |
| 2032 | ;; (1) there is a next sibling defun, or (2) no more | 2058 | ;; (1) there is a next sibling defun, or (2) no more |
| 2033 | ;; parents [2]. | 2059 | ;; parents [2]. |
| 2060 | ;; | ||
| 2061 | ;; If point on beg-of-defun but we are already | ||
| 2062 | ;; recurring, that doesn't count as special case, | ||
| 2063 | ;; because we have already made progress (by moving | ||
| 2064 | ;; the end of next before recurring.) | ||
| 2034 | (setq pos (or (treesit--navigate-thing | 2065 | (setq pos (or (treesit--navigate-thing |
| 2035 | (treesit-node-end (or next parent)) | 2066 | (treesit-node-end (or next parent)) |
| 2036 | 1 'beg regexp pred t) | 2067 | 1 'beg regexp pred t) |
| @@ -2039,9 +2070,9 @@ function is called recursively." | |||
| 2039 | (setq pos (funcall advance (or next parent)))) | 2070 | (setq pos (funcall advance (or next parent)))) |
| 2040 | ;; ...backward. | 2071 | ;; ...backward. |
| 2041 | (if (and (eq side 'end) | 2072 | (if (and (eq side 'end) |
| 2042 | (cond (prev (not recursing)) | 2073 | (cond (prev (and (not recursing) |
| 2043 | (parent t) | 2074 | (eq pos (funcall advance prev)))) |
| 2044 | (t nil))) | 2075 | (parent t))) |
| 2045 | ;; Special case: go to prev end-of-defun. | 2076 | ;; Special case: go to prev end-of-defun. |
| 2046 | (setq pos (or (treesit--navigate-thing | 2077 | (setq pos (or (treesit--navigate-thing |
| 2047 | (treesit-node-start (or prev parent)) | 2078 | (treesit-node-start (or prev parent)) |
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index 5aa12e8aa0e..468cd221ef9 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el | |||
| @@ -977,22 +977,22 @@ and \"]\"." | |||
| 977 | 977 | ||
| 978 | (defvar treesit--ert-defun-navigation-nested-master | 978 | (defvar treesit--ert-defun-navigation-nested-master |
| 979 | ;; START PREV-BEG NEXT-END PREV-END NEXT-BEG | 979 | ;; START PREV-BEG NEXT-END PREV-END NEXT-BEG |
| 980 | '((0 103 105 102 106) ; Between Beg of parent & 1st sibling. | 980 | '((0 103 105 102 104) ; Between Beg of parent & 1st sibling. |
| 981 | (1 103 105 102 106) ; Beg of 1st sibling. | 981 | (1 103 105 102 106) ; Beg of 1st sibling. |
| 982 | (2 104 105 102 106) ; Inside 1st sibling. | 982 | (2 104 105 102 106) ; Inside 1st sibling. |
| 983 | (3 104 107 102 109) ; End of 1st sibling. | 983 | (3 104 107 102 106) ; End of 1st sibling. |
| 984 | (4 104 107 102 109) ; Between 1st sibling & 2nd sibling. | 984 | (4 104 107 105 106) ; Between 1st sibling & 2nd sibling. |
| 985 | (5 104 107 102 109) ; Beg of 2nd sibling. | 985 | (5 104 107 105 109) ; Beg of 2nd sibling. |
| 986 | (6 106 107 105 109) ; Inside 2nd sibling. | 986 | (6 106 107 105 109) ; Inside 2nd sibling. |
| 987 | (7 106 108 105 109) ; End of 2nd sibling. | 987 | (7 106 108 105 109) ; End of 2nd sibling. |
| 988 | (8 106 108 105 109) ; Between 2nd sibling & end of parent. | 988 | (8 106 108 107 109) ; Between 2nd sibling & end of parent. |
| 989 | (9 103 110 102 nil) ; End of parent. | 989 | (9 103 110 102 109) ; End of parent. |
| 990 | 990 | ||
| 991 | (100 nil 102 nil 103) ; Before 1st parent. | 991 | (100 nil 102 nil 101) ; Before 1st parent. |
| 992 | (101 nil 102 nil 103) ; Beg of 1st parent. | 992 | (101 nil 102 nil 103) ; Beg of 1st parent. |
| 993 | (102 101 108 nil 109) ; Between 1st & 2nd parent. | 993 | (102 101 108 102 103) ; Between 1st & 2nd parent. |
| 994 | (103 101 108 nil 109) ; Beg of 2nd parent. | 994 | (103 101 108 102 109) ; Beg of 2nd parent. |
| 995 | (110 109 nil 108 nil) ; After 3rd parent. | 995 | (110 109 nil 110 nil) ; After 3rd parent. |
| 996 | ) | 996 | ) |
| 997 | "Master of nested navigation test. | 997 | "Master of nested navigation test. |
| 998 | 998 | ||
| @@ -1000,7 +1000,7 @@ This basically says, e.g., \"start with point on marker 0, go to | |||
| 1000 | the prev-beg, now point should be at marker 103\", etc.") | 1000 | the prev-beg, now point should be at marker 103\", etc.") |
| 1001 | 1001 | ||
| 1002 | (defvar treesit--ert-defun-navigation-top-level-master | 1002 | (defvar treesit--ert-defun-navigation-top-level-master |
| 1003 | ;; START PREV-BEG NEXT-END NEXT-BEG PREV-END | 1003 | ;; START PREV-BEG NEXT-END PREV-END NEXT-BEG |
| 1004 | '((0 103 108 102 109) ; Between Beg of parent & 1st sibling. | 1004 | '((0 103 108 102 109) ; Between Beg of parent & 1st sibling. |
| 1005 | (1 103 108 102 109) ; Beg of 1st sibling. | 1005 | (1 103 108 102 109) ; Beg of 1st sibling. |
| 1006 | (2 103 108 102 109) ; Inside 1st sibling. | 1006 | (2 103 108 102 109) ; Inside 1st sibling. |
| @@ -1010,14 +1010,14 @@ the prev-beg, now point should be at marker 103\", etc.") | |||
| 1010 | (6 103 108 102 109) ; Inside 2nd sibling. | 1010 | (6 103 108 102 109) ; Inside 2nd sibling. |
| 1011 | (7 103 108 102 109) ; End of 2nd sibling. | 1011 | (7 103 108 102 109) ; End of 2nd sibling. |
| 1012 | (8 103 108 102 109) ; Between 2nd sibling & end of parent. | 1012 | (8 103 108 102 109) ; Between 2nd sibling & end of parent. |
| 1013 | (9 103 110 102 nil) ; End of parent. | 1013 | (9 103 110 102 109) ; End of parent. |
| 1014 | 1014 | ||
| 1015 | ;; Top-level defuns should be identical to the nested test. | 1015 | ;; Top-level defuns should be identical to the nested test. |
| 1016 | (100 nil 102 nil 103) ; Before 1st parent. | 1016 | (100 nil 102 nil 101) ; Before 1st parent. |
| 1017 | (101 nil 102 nil 103) ; Beg of 1st parent. | 1017 | (101 nil 102 nil 103) ; Beg of 1st parent. |
| 1018 | (102 101 108 nil 109) ; Between 1st & 2nd parent. | 1018 | (102 101 108 102 103) ; Between 1st & 2nd parent. |
| 1019 | (103 101 108 nil 109) ; Beg of 2nd parent. | 1019 | (103 101 108 102 109) ; Beg of 2nd parent. |
| 1020 | (110 109 nil 108 nil) ; After 3rd parent. | 1020 | (110 109 nil 110 nil) ; After 3rd parent. |
| 1021 | ) | 1021 | ) |
| 1022 | "Master of top-level navigation test.") | 1022 | "Master of top-level navigation test.") |
| 1023 | 1023 | ||