diff options
| author | Yuan Fu | 2026-03-02 23:35:05 -0800 |
|---|---|---|
| committer | Yuan Fu | 2026-03-02 23:59:46 -0800 |
| commit | 18c24866afa786d70f663e2b715e672ec5808f18 (patch) | |
| tree | 9e0dfb15c16f861a105546e971701ed5c35385bc | |
| parent | b8ee7d667fc2b5ef147d2a42ada1cb325f085d12 (diff) | |
| download | emacs-master.tar.gz emacs-master.zip | |
Not a bug report, but this is from the emacs-devel thread
"Tree-sitter: Transcendental things".
Improve tree-sitter thing navigation functions to handle the
case where there are multiple nested parsers in the
buffer. Previously these functions just grab the node at point
and are limited to the parser of that node. Now these functions
will thoUrouly search through all parsers that covers point
before giving up.
* doc/lispref/parsing.texi: Mention the change.
* lisp/treesit.el (treesit--some): New macro.
(treesit--thing-sibling): Use the new macro, add a new PARSER
parameter.
(treesit-thing-prev):
(treesit-thing-next): Add PARSER parameter.
(treesit-thing-at):
(treesit-navigate-thing): Use the new macro, add a new PARSER
parameter. Also pass the PARSER arg to recursive calls and
calls to treesit-thing-prev, etc.
Diffstat (limited to '')
| -rw-r--r-- | doc/lispref/parsing.texi | 38 | ||||
| -rw-r--r-- | etc/NEWS | 13 | ||||
| -rw-r--r-- | lisp/treesit.el | 333 |
3 files changed, 249 insertions, 135 deletions
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index fde8d403f54..35776b88c67 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi | |||
| @@ -1786,22 +1786,40 @@ undefined and just returns @code{nil}; but it still signals the error if | |||
| 1786 | @var{thing} is a malformed predicate. | 1786 | @var{thing} is a malformed predicate. |
| 1787 | @end defun | 1787 | @end defun |
| 1788 | 1788 | ||
| 1789 | @defun treesit-thing-prev position thing | 1789 | Functions below are responsible for finding things and moving across |
| 1790 | them, and they have to deal with the fact that a buffer sometimes | ||
| 1791 | contains multiple adjacent or nested parsers. By default, these | ||
| 1792 | functions try to be helpful and search in every relevant parser at | ||
| 1793 | point, from most specific (deepest embedded) to the least. Lisp | ||
| 1794 | programs should be cautious and assess whether this behavior is desired | ||
| 1795 | when using these functions as building blocks of other functions; if | ||
| 1796 | not, explicitly pass a parser or language. | ||
| 1797 | |||
| 1798 | @defun treesit-thing-prev position thing &optional parser | ||
| 1790 | This function returns the first node before @var{position} in the | 1799 | This function returns the first node before @var{position} in the |
| 1791 | current buffer that is the specified @var{thing}. If no such node | 1800 | current buffer that is the specified @var{thing}. If no such node |
| 1792 | exists, it returns @code{nil}. | 1801 | exists, it returns @code{nil}. |
| 1802 | |||
| 1793 | It's guaranteed that, if a node is returned, the node's end position is | 1803 | It's guaranteed that, if a node is returned, the node's end position is |
| 1794 | less or equal to @var{position}. In other words, this function never | 1804 | less or equal to @var{position}. In other words, this function never |
| 1795 | returns a node that encloses @var{position}. | 1805 | returns a node that encloses @var{position}. |
| 1796 | 1806 | ||
| 1797 | Again, @var{thing} can be either a symbol or a predicate. | 1807 | Again, @var{thing} can be either a symbol or a predicate. |
| 1808 | |||
| 1809 | If @var{parser} is non-nil, only use that parser's parse tree. | ||
| 1810 | Otherwise each parser covering point is tried from most specific | ||
| 1811 | (deepest embedded) to least specific. If there are multiple parsers with | ||
| 1812 | the same embed level at @var{pos}, which parser is tried first is | ||
| 1813 | undefined. If PARSER is a language symbol, the parsers tried are | ||
| 1814 | limited to ones for that language. | ||
| 1798 | @end defun | 1815 | @end defun |
| 1799 | 1816 | ||
| 1800 | @defun treesit-thing-next position thing | 1817 | @defun treesit-thing-next position thing &optional parser |
| 1801 | This function is similar to @code{treesit-thing-prev}, only it returns | 1818 | This function is similar to @code{treesit-thing-prev}, only it returns |
| 1802 | the first node @emph{after} @var{position} that's the @var{thing}. It | 1819 | the first node @emph{after} @var{position} that's the @var{thing}. It |
| 1803 | also guarantees that if a node is returned, the node's start position is | 1820 | also guarantees that if a node is returned, the node's start position is |
| 1804 | greater or equal to @var{position}. | 1821 | greater or equal to @var{position}. The @var{parser} parameter is the |
| 1822 | same as in @code{treesit-thing-prev} as well. | ||
| 1805 | @end defun | 1823 | @end defun |
| 1806 | 1824 | ||
| 1807 | @defun treesit-navigate-thing position arg side thing &optional tactic | 1825 | @defun treesit-navigate-thing position arg side thing &optional tactic |
| @@ -1819,7 +1837,11 @@ A positive @var{arg} means moving forward that many instances of | |||
| 1819 | @var{thing}. | 1837 | @var{thing}. |
| 1820 | 1838 | ||
| 1821 | Like in @code{treesit-thing-prev}, @var{thing} can be a thing symbol | 1839 | Like in @code{treesit-thing-prev}, @var{thing} can be a thing symbol |
| 1822 | defined in @code{treesit-thing-settings}, or a predicate. | 1840 | defined in @code{treesit-thing-settings}, or a predicate, and |
| 1841 | @var{parser} can be either @code{nil}, a parser, or a language symbol. | ||
| 1842 | Like in that function, @var{parser} decides which parsers or languages | ||
| 1843 | are searched. When there are multiple parsers available, this function | ||
| 1844 | tries each until it succeeds. | ||
| 1823 | 1845 | ||
| 1824 | @var{tactic} determines how this function moves between things. It can | 1846 | @var{tactic} determines how this function moves between things. It can |
| 1825 | be @code{nested}, @code{top-level}, @code{restricted}, | 1847 | be @code{nested}, @code{top-level}, @code{restricted}, |
| @@ -1835,7 +1857,7 @@ means move to the parent if there is one; and move to siblings if | |||
| 1835 | there's no parent. | 1857 | there's no parent. |
| 1836 | @end defun | 1858 | @end defun |
| 1837 | 1859 | ||
| 1838 | @defun treesit-thing-at position thing &optional strict | 1860 | @defun treesit-thing-at position thing &optional strict parser |
| 1839 | This function returns the smallest node that's the @var{thing} and | 1861 | This function returns the smallest node that's the @var{thing} and |
| 1840 | encloses @var{position}; if there's no such node, it returns @code{nil}. | 1862 | encloses @var{position}; if there's no such node, it returns @code{nil}. |
| 1841 | 1863 | ||
| @@ -1849,6 +1871,12 @@ position must be strictly greater than @var{position}. | |||
| 1849 | 1871 | ||
| 1850 | @var{thing} can be either a thing symbol defined in | 1872 | @var{thing} can be either a thing symbol defined in |
| 1851 | @code{treesit-thing-settings}, or a predicate. | 1873 | @code{treesit-thing-settings}, or a predicate. |
| 1874 | |||
| 1875 | If @var{parser} is non-nil, only use that parser's parse tree. | ||
| 1876 | Otherwise each parser covering point is tried from most specific (deepest | ||
| 1877 | embedded) to least specific. If there are multiple parsers with the same | ||
| 1878 | embed level at @var{pos}, which parser is tried first is undefined. | ||
| 1879 | @var{parser} can also be a language symbol. | ||
| 1852 | @end defun | 1880 | @end defun |
| 1853 | 1881 | ||
| 1854 | @findex treesit-beginning-of-thing | 1882 | @findex treesit-beginning-of-thing |
| @@ -1250,6 +1250,19 @@ Multi-language major modes can rely on the default return value from | |||
| 1250 | 'treesit-language-at' that uses the new function 'treesit-parsers-at'. | 1250 | 'treesit-language-at' that uses the new function 'treesit-parsers-at'. |
| 1251 | 1251 | ||
| 1252 | +++ | 1252 | +++ |
| 1253 | *** Tree-sitter thing functions now work better with multiple parsers | ||
| 1254 | These functions now have better handling when there are multiple | ||
| 1255 | parsers at point: 'treesit-thing-prev', 'treesit-thing-next', | ||
| 1256 | 'treesit-navigate-thing', 'treesit-thing-at'. When there are multiple | ||
| 1257 | parsers at point, instead of using whatever 'treesit-node-at' returns at | ||
| 1258 | point, these functions now try every relevant parser in descending | ||
| 1259 | order of relevance. (Deeper embedded parser has higher relevance.) | ||
| 1260 | These functions now also take an additional optional argument, PARSER, | ||
| 1261 | that allows caller to specify a parser or language to use. That also | ||
| 1262 | means 'treesit-beginning/end-of-defun' now can move across parsers. | ||
| 1263 | |||
| 1264 | |||
| 1265 | +++ | ||
| 1253 | *** New command 'treesit-explore'. | 1266 | *** New command 'treesit-explore'. |
| 1254 | This command replaces 'treesit-explore-mode'. It turns on | 1267 | This command replaces 'treesit-explore-mode'. It turns on |
| 1255 | 'treesit-explore-mode' if it is not on, and pops up the explorer buffer | 1268 | 'treesit-explore-mode' if it is not on, and pops up the explorer buffer |
diff --git a/lisp/treesit.el b/lisp/treesit.el index 0bc115a3331..7d6113e3249 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el | |||
| @@ -3665,60 +3665,106 @@ the current line if the beginning of the defun is indented." | |||
| 3665 | (line-beginning-position)) | 3665 | (line-beginning-position)) |
| 3666 | (beginning-of-line)))) | 3666 | (beginning-of-line)))) |
| 3667 | 3667 | ||
| 3668 | (defun treesit--thing-sibling (pos thing prev) | 3668 | (defmacro treesit--some (sym-val &rest body) |
| 3669 | "Evaluate BODY with SYM bounded to each value in VAL. | ||
| 3670 | |||
| 3671 | Return the first non-nil evaluation of BODY. | ||
| 3672 | |||
| 3673 | \(fn (SYM VAL) &rest BODY)" | ||
| 3674 | (declare (indent 1)) | ||
| 3675 | (let ((result-sym (gensym)) | ||
| 3676 | (val-sym (gensym)) | ||
| 3677 | (sym (car sym-val)) | ||
| 3678 | (val (cadr sym-val))) | ||
| 3679 | `(let ((,val-sym ,val) | ||
| 3680 | (,sym nil) | ||
| 3681 | (,result-sym nil)) | ||
| 3682 | (while (and (null ,result-sym) | ||
| 3683 | (setq ,sym (pop ,val-sym))) | ||
| 3684 | (setq ,result-sym | ||
| 3685 | ,@body)) | ||
| 3686 | ,result-sym))) | ||
| 3687 | |||
| 3688 | (defun treesit--thing-sibling (pos thing prev &optional parser) | ||
| 3669 | "Return the next or previous THING at POS. | 3689 | "Return the next or previous THING at POS. |
| 3670 | 3690 | ||
| 3671 | If PREV is non-nil, return the previous THING. It's guaranteed | 3691 | If PREV is non-nil, return the previous THING. It's guaranteed |
| 3672 | that returned previous sibling's end <= POS, and returned next | 3692 | that returned previous sibling's end <= POS, and returned next |
| 3673 | sibling's beginning >= POS. | 3693 | sibling's beginning >= POS. |
| 3674 | 3694 | ||
| 3695 | If PARSER is non-nil, only use that parser's parse tree. Otherwise each | ||
| 3696 | parser covering point is tried from most specific (deepest embedded) to | ||
| 3697 | least specific. If there are multiple parsers with the same embed level | ||
| 3698 | at POS, which parser is tried first is undefined. If PARSER is a | ||
| 3699 | language symbol, the parsers tried are limited to ones for that | ||
| 3700 | language. | ||
| 3701 | |||
| 3675 | Return nil if no THING can be found. THING should be a thing | 3702 | Return nil if no THING can be found. THING should be a thing |
| 3676 | defined in `treesit-thing-settings', or a predicate as described | 3703 | defined in `treesit-thing-settings', or a predicate as described |
| 3677 | in `treesit-thing-settings'." | 3704 | in `treesit-thing-settings'." |
| 3678 | (let* ((cursor (treesit-node-at pos)) | 3705 | (treesit--some (parser (cond ((null parser) |
| 3679 | (pos-pred (if prev | 3706 | (treesit-parsers-at pos)) |
| 3680 | (lambda (n) (<= (treesit-node-end n) pos)) | 3707 | ((symbolp parser) |
| 3681 | (lambda (n) (>= (treesit-node-start n) pos)))) | 3708 | (treesit-parsers-at pos parser)) |
| 3682 | (iter-pred (lambda (node) | 3709 | (t (list parser)))) |
| 3683 | (and (treesit-node-match-p node thing t) | 3710 | (let* ((cursor (treesit-node-at pos parser)) |
| 3684 | (funcall pos-pred node)))) | 3711 | (pos-pred (if prev |
| 3685 | (sibling nil)) | 3712 | (lambda (n) (<= (treesit-node-end n) pos)) |
| 3686 | (when cursor | 3713 | (lambda (n) (>= (treesit-node-start n) pos)))) |
| 3687 | ;; Find the node just before/after POS to start searching. | 3714 | (iter-pred (lambda (node) |
| 3688 | (save-excursion | 3715 | (and (treesit-node-match-p node thing t) |
| 3689 | (while (and cursor (not (funcall pos-pred cursor))) | 3716 | (funcall pos-pred node)))) |
| 3690 | (setq cursor (treesit-search-forward-goto | 3717 | (sibling nil)) |
| 3691 | cursor "" prev prev t)))) | 3718 | (when cursor |
| 3692 | ;; Keep searching until we run out of candidates or found a | 3719 | ;; Find the node just before/after POS to start searching. |
| 3693 | ;; return value. | 3720 | (save-excursion |
| 3694 | (while (and cursor | 3721 | (while (and cursor (not (funcall pos-pred cursor))) |
| 3695 | (funcall pos-pred cursor) | 3722 | (setq cursor (treesit-search-forward-goto |
| 3696 | (null sibling)) | 3723 | cursor "" prev prev t)))) |
| 3697 | (setq sibling (treesit-node-top-level cursor iter-pred t)) | 3724 | ;; Keep searching until we run out of candidates or found a |
| 3698 | (setq cursor (treesit-search-forward cursor thing prev prev))) | 3725 | ;; return value. |
| 3699 | sibling))) | 3726 | (while (and cursor |
| 3700 | 3727 | (funcall pos-pred cursor) | |
| 3701 | (defun treesit-thing-prev (pos thing) | 3728 | (null sibling)) |
| 3729 | (setq sibling (treesit-node-top-level cursor iter-pred t)) | ||
| 3730 | (setq cursor (treesit-search-forward cursor thing prev prev))) | ||
| 3731 | sibling)))) | ||
| 3732 | |||
| 3733 | (defun treesit-thing-prev (pos thing &optional parser) | ||
| 3702 | "Return the previous THING at POS. | 3734 | "Return the previous THING at POS. |
| 3703 | 3735 | ||
| 3704 | The returned node, if non-nil, must be before POS, i.e., its end | 3736 | The returned node, if non-nil, must be before POS, i.e., its end |
| 3705 | <= POS. | 3737 | <= POS. |
| 3706 | 3738 | ||
| 3707 | THING should be a thing defined in `treesit-thing-settings', or a | 3739 | THING should be a thing defined in `treesit-thing-settings', or a |
| 3708 | predicate as described in `treesit-thing-settings'." | 3740 | predicate as described in `treesit-thing-settings'. |
| 3709 | (treesit--thing-sibling pos thing t)) | 3741 | |
| 3742 | If PARSER is non-nil, only use that parser's parse tree. Otherwise each | ||
| 3743 | parser covering point is tried from most specific (deepest embedded) to | ||
| 3744 | least specific. If there are multiple parsers with the same embed level | ||
| 3745 | at POS, which parser is tried first is undefined. If PARSER is a | ||
| 3746 | language symbol, the parsers tried are limited to ones for that | ||
| 3747 | language." | ||
| 3748 | (treesit--thing-sibling pos thing t parser)) | ||
| 3710 | 3749 | ||
| 3711 | (defun treesit-thing-next (pos thing) | 3750 | (defun treesit-thing-next (pos thing &optional parser) |
| 3712 | "Return the next THING at POS. | 3751 | "Return the next THING at POS. |
| 3713 | 3752 | ||
| 3714 | The returned node, if non-nil, must be after POS, i.e., its | 3753 | The returned node, if non-nil, must be after POS, i.e., its |
| 3715 | start >= POS. | 3754 | start >= POS. |
| 3716 | 3755 | ||
| 3717 | THING should be a thing defined in `treesit-thing-settings', or a | 3756 | THING should be a thing defined in `treesit-thing-settings', or a |
| 3718 | predicate as described in `treesit-thing-settings'." | 3757 | predicate as described in `treesit-thing-settings'. |
| 3719 | (treesit--thing-sibling pos thing nil)) | ||
| 3720 | 3758 | ||
| 3721 | (defun treesit-thing-at (pos thing &optional strict) | 3759 | If PARSER is non-nil, only use that parser's parse tree. Otherwise each |
| 3760 | parser covering point is tried from most specific (deepest embedded) to | ||
| 3761 | least specific. If there are multiple parsers with the same embed level | ||
| 3762 | at POS, which parser is tried first is undefined. If PARSER is a | ||
| 3763 | language symbol, the parsers tried are limited to ones for that | ||
| 3764 | language." | ||
| 3765 | (treesit--thing-sibling pos thing nil parser)) | ||
| 3766 | |||
| 3767 | (defun treesit-thing-at (pos thing &optional strict parser) | ||
| 3722 | "Return the smallest node enclosing POS for THING. | 3768 | "Return the smallest node enclosing POS for THING. |
| 3723 | 3769 | ||
| 3724 | The returned node, if non-nil, must enclose POS, i.e., its | 3770 | The returned node, if non-nil, must enclose POS, i.e., its |
| @@ -3727,15 +3773,27 @@ node's start must be < POS rather than <= POS. | |||
| 3727 | 3773 | ||
| 3728 | THING should be a thing defined in `treesit-thing-settings' for | 3774 | THING should be a thing defined in `treesit-thing-settings' for |
| 3729 | the current buffer's major mode, or it can be a predicate | 3775 | the current buffer's major mode, or it can be a predicate |
| 3730 | described in `treesit-thing-settings'." | 3776 | described in `treesit-thing-settings'. |
| 3731 | (let* ((cursor (treesit-node-at pos)) | 3777 | |
| 3732 | (iter-pred (lambda (node) | 3778 | If PARSER is non-nil, only use that parser's parse tree. Otherwise each |
| 3733 | (and (treesit-node-match-p node thing t) | 3779 | parser covering point is tried from most specific (deepest embedded) to |
| 3734 | (if strict | 3780 | least specific. If there are multiple parsers with the same embed level |
| 3735 | (< (treesit-node-start node) pos) | 3781 | at POS, which parser is tried first is undefined. If PARSER is a |
| 3736 | (<= (treesit-node-start node) pos)) | 3782 | language symbol, the parsers tried are limited to ones for that |
| 3737 | (< pos (treesit-node-end node)))))) | 3783 | language." |
| 3738 | (treesit-parent-until cursor iter-pred t))) | 3784 | (treesit--some (parser (cond ((null parser) |
| 3785 | (treesit-parsers-at pos)) | ||
| 3786 | ((symbolp parser) | ||
| 3787 | (treesit-parsers-at pos parser)) | ||
| 3788 | (t (list parser)))) | ||
| 3789 | (let ((iter-pred (lambda (node) | ||
| 3790 | (and (treesit-node-match-p node thing t) | ||
| 3791 | (if strict | ||
| 3792 | (< (treesit-node-start node) pos) | ||
| 3793 | (<= (treesit-node-start node) pos)) | ||
| 3794 | (< pos (treesit-node-end node)))))) | ||
| 3795 | (treesit-parent-until | ||
| 3796 | (treesit-node-at pos parser) iter-pred t)))) | ||
| 3739 | 3797 | ||
| 3740 | ;; The basic idea for nested defun navigation is that we first try to | 3798 | ;; The basic idea for nested defun navigation is that we first try to |
| 3741 | ;; move across sibling defuns in the same level, if no more siblings | 3799 | ;; move across sibling defuns in the same level, if no more siblings |
| @@ -3764,7 +3822,7 @@ described in `treesit-thing-settings'." | |||
| 3764 | ;; -> Obviously we don't want to go to parent's end, instead, we | 3822 | ;; -> Obviously we don't want to go to parent's end, instead, we |
| 3765 | ;; want to go to parent's prev-sibling's end. Again, we recurse | 3823 | ;; want to go to parent's prev-sibling's end. Again, we recurse |
| 3766 | ;; in the function to do that. | 3824 | ;; in the function to do that. |
| 3767 | (defun treesit-navigate-thing (pos arg side thing &optional tactic recursing) | 3825 | (defun treesit-navigate-thing (pos arg side thing &optional tactic parser recursing) |
| 3768 | "Navigate thing ARG steps from POS. | 3826 | "Navigate thing ARG steps from POS. |
| 3769 | 3827 | ||
| 3770 | If ARG is positive, move forward that many steps, if negative, | 3828 | If ARG is positive, move forward that many steps, if negative, |
| @@ -3788,100 +3846,112 @@ that encloses POS (i.e., parent), should there be one. `parent' means | |||
| 3788 | move to the parent if there is one; and move to siblings if there's no | 3846 | move to the parent if there is one; and move to siblings if there's no |
| 3789 | parent. If omitted, TACTIC is considered to be `nested'. | 3847 | parent. If omitted, TACTIC is considered to be `nested'. |
| 3790 | 3848 | ||
| 3849 | If PARSER is non-nil, only use that parser's parse tree. Otherwise each | ||
| 3850 | parser covering point is tried from most specific (deepest embedded) to | ||
| 3851 | least specific. If there are multiple parsers with the same embed level | ||
| 3852 | at POS, which parser is tried first is undefined. If PARSER is a | ||
| 3853 | language symbol, the parsers tried are limited to ones for that | ||
| 3854 | language. | ||
| 3855 | |||
| 3791 | RECURSING is an internal parameter, if non-nil, it means this | 3856 | RECURSING is an internal parameter, if non-nil, it means this |
| 3792 | function is called recursively." | 3857 | function is called recursively." |
| 3793 | (pcase-let* | 3858 | (treesit--some (parser (cond ((null parser) |
| 3794 | ((counter (abs arg)) | 3859 | (treesit-parsers-at pos)) |
| 3795 | ;; Move POS to the beg/end of NODE. If NODE is nil, terminate. | 3860 | ((symbolp parser) |
| 3796 | ;; Return the position we moved to. | 3861 | (treesit-parsers-at pos parser)) |
| 3797 | (advance (lambda (node) | 3862 | (t (list parser)))) |
| 3798 | (let ((dest (pcase side | 3863 | (pcase-let* |
| 3799 | ('beg (treesit-node-start node)) | 3864 | ((counter (abs arg)) |
| 3800 | ('end (treesit-node-end node))))) | 3865 | ;; Move POS to the beg/end of NODE. If NODE is nil, terminate. |
| 3801 | (if (null dest) | 3866 | ;; Return the position we moved to. |
| 3802 | (throw 'term nil) | 3867 | (advance (lambda (node) |
| 3803 | dest))))) | 3868 | (let ((dest (pcase side |
| 3804 | (catch 'term | 3869 | ('beg (treesit-node-start node)) |
| 3805 | (while (> counter 0) | 3870 | ('end (treesit-node-end node))))) |
| 3806 | (let ((prev (treesit-thing-prev pos thing)) | 3871 | (if (null dest) |
| 3807 | (next (treesit-thing-next pos thing)) | 3872 | (throw 'term nil) |
| 3808 | (parent (treesit-thing-at pos thing t))) | 3873 | dest))))) |
| 3809 | (when (and parent prev | 3874 | (catch 'term |
| 3810 | (not (treesit-node-enclosed-p prev parent))) | 3875 | (while (> counter 0) |
| 3811 | (setq prev nil)) | 3876 | (let ((prev (treesit-thing-prev pos thing parser)) |
| 3812 | (when (and parent next | 3877 | (next (treesit-thing-next pos thing parser)) |
| 3813 | (not (treesit-node-enclosed-p next parent))) | 3878 | (parent (treesit-thing-at pos thing t parser))) |
| 3814 | (setq next nil)) | 3879 | (when (and parent prev |
| 3815 | ;; When PARENT is nil, nested and top-level are the same, if | 3880 | (not (treesit-node-enclosed-p prev parent))) |
| 3816 | ;; there is a PARENT, make PARENT to be the top-level parent | 3881 | (setq prev nil)) |
| 3817 | ;; and pretend there is no nested PREV and NEXT. | 3882 | (when (and parent next |
| 3818 | (when (and (eq tactic 'top-level) | 3883 | (not (treesit-node-enclosed-p next parent))) |
| 3819 | parent) | 3884 | (setq next nil)) |
| 3820 | (setq parent (treesit-node-top-level parent thing t) | 3885 | ;; When PARENT is nil, nested and top-level are the same, if |
| 3821 | prev nil | 3886 | ;; there is a PARENT, make PARENT to be the top-level parent |
| 3822 | next nil)) | 3887 | ;; and pretend there is no nested PREV and NEXT. |
| 3823 | ;; When PARENT is nil, `nested' and `parent-first' are the | 3888 | (when (and (eq tactic 'top-level) |
| 3824 | ;; same, if there is a PARENT, pretend there is no nested PREV | 3889 | parent) |
| 3825 | ;; and NEXT so the following code moves to the parent. | 3890 | (setq parent (treesit-node-top-level parent thing t) |
| 3826 | (when (and (eq tactic 'parent-first) parent) | 3891 | prev nil |
| 3827 | (setq prev nil next nil)) | 3892 | next nil)) |
| 3828 | ;; If TACTIC is `restricted', the implementation is simple. | 3893 | ;; When PARENT is nil, `nested' and `parent-first' are the |
| 3829 | ;; In principle we don't go to parent's beg/end for | 3894 | ;; same, if there is a PARENT, pretend there is no nested PREV |
| 3830 | ;; `restricted' tactic, but if the parent is a "leaf thing" | 3895 | ;; and NEXT so the following code moves to the parent. |
| 3831 | ;; (doesn't have any child "thing" inside it), then we can | 3896 | (when (and (eq tactic 'parent-first) parent) |
| 3832 | ;; move to the beg/end of it (bug#68899). | 3897 | (setq prev nil next nil)) |
| 3833 | (if (eq tactic 'restricted) | 3898 | ;; If TACTIC is `restricted', the implementation is simple. |
| 3834 | (setq pos (funcall | 3899 | ;; In principle we don't go to parent's beg/end for |
| 3835 | advance | 3900 | ;; `restricted' tactic, but if the parent is a "leaf thing" |
| 3836 | (cond ((and (null next) (null prev)) parent) | 3901 | ;; (doesn't have any child "thing" inside it), then we can |
| 3837 | ((> arg 0) next) | 3902 | ;; move to the beg/end of it (bug#68899). |
| 3838 | (t prev)))) | 3903 | (if (eq tactic 'restricted) |
| 3839 | ;; For `nested', it's a bit more work: | 3904 | (setq pos (funcall |
| 3840 | ;; Move... | 3905 | advance |
| 3841 | (if (> arg 0) | 3906 | (cond ((and (null next) (null prev)) parent) |
| 3842 | ;; ...forward. | 3907 | ((> arg 0) next) |
| 3843 | (if (and (eq side 'beg) | 3908 | (t prev)))) |
| 3844 | ;; Should we skip the defun (recurse)? | 3909 | ;; For `nested', it's a bit more work: |
| 3845 | (cond (next (and (not recursing) ; [1] (see below) | 3910 | ;; Move... |
| 3846 | (eq pos (funcall advance next)))) | 3911 | (if (> arg 0) |
| 3847 | (parent t))) ; [2] | 3912 | ;; ...forward. |
| 3848 | ;; Special case: go to next beg-of-defun, but point | 3913 | (if (and (eq side 'beg) |
| 3849 | ;; is already on beg-of-defun. Set POS to the end | 3914 | ;; Should we skip the defun (recurse)? |
| 3850 | ;; of next-sib/parent defun, and run one more step. | 3915 | (cond (next (and (not recursing) ; [1] (see below) |
| 3851 | ;; If there is a next-sib defun, we only need to | 3916 | (eq pos (funcall advance next)))) |
| 3852 | ;; recurse once, so we don't need to recurse if we | 3917 | (parent t))) ; [2] |
| 3853 | ;; are already recursing [1]. If there is no | 3918 | ;; Special case: go to next beg-of-defun, but point |
| 3854 | ;; next-sib but a parent, keep stepping out | 3919 | ;; is already on beg-of-defun. Set POS to the end |
| 3855 | ;; (recursing) until we got out of the parents until | 3920 | ;; of next-sib/parent defun, and run one more step. |
| 3856 | ;; (1) there is a next sibling defun, or (2) no more | 3921 | ;; If there is a next-sib defun, we only need to |
| 3857 | ;; parents [2]. | 3922 | ;; recurse once, so we don't need to recurse if we |
| 3858 | ;; | 3923 | ;; are already recursing [1]. If there is no |
| 3859 | ;; If point on beg-of-defun but we are already | 3924 | ;; next-sib but a parent, keep stepping out |
| 3860 | ;; recurring, that doesn't count as special case, | 3925 | ;; (recursing) until we got out of the parents until |
| 3861 | ;; because we have already made progress (by moving | 3926 | ;; (1) there is a next sibling defun, or (2) no more |
| 3862 | ;; the end of next before recurring.) | 3927 | ;; parents [2]. |
| 3928 | ;; | ||
| 3929 | ;; If point on beg-of-defun but we are already | ||
| 3930 | ;; recurring, that doesn't count as special case, | ||
| 3931 | ;; because we have already made progress (by moving | ||
| 3932 | ;; the end of next before recurring.) | ||
| 3933 | (setq pos (or (treesit-navigate-thing | ||
| 3934 | (treesit-node-end (or next parent)) | ||
| 3935 | 1 'beg thing tactic parser t) | ||
| 3936 | (throw 'term nil))) | ||
| 3937 | ;; Normal case. | ||
| 3938 | (setq pos (funcall advance (or next parent)))) | ||
| 3939 | ;; ...backward. | ||
| 3940 | (if (and (eq side 'end) | ||
| 3941 | (cond (prev (and (not recursing) | ||
| 3942 | (eq pos (funcall advance prev)))) | ||
| 3943 | (parent t))) | ||
| 3944 | ;; Special case: go to prev end-of-defun. | ||
| 3863 | (setq pos (or (treesit-navigate-thing | 3945 | (setq pos (or (treesit-navigate-thing |
| 3864 | (treesit-node-end (or next parent)) | 3946 | (treesit-node-start (or prev parent)) |
| 3865 | 1 'beg thing tactic t) | 3947 | -1 'end thing tactic parser t) |
| 3866 | (throw 'term nil))) | 3948 | (throw 'term nil))) |
| 3867 | ;; Normal case. | 3949 | ;; Normal case. |
| 3868 | (setq pos (funcall advance (or next parent)))) | 3950 | (setq pos (funcall advance (or prev parent)))))) |
| 3869 | ;; ...backward. | 3951 | ;; A successful step! Decrement counter. |
| 3870 | (if (and (eq side 'end) | 3952 | (decf counter)))) |
| 3871 | (cond (prev (and (not recursing) | 3953 | ;; Counter equal to 0 means we successfully stepped ARG steps. |
| 3872 | (eq pos (funcall advance prev)))) | 3954 | (if (eq counter 0) pos nil)))) |
| 3873 | (parent t))) | ||
| 3874 | ;; Special case: go to prev end-of-defun. | ||
| 3875 | (setq pos (or (treesit-navigate-thing | ||
| 3876 | (treesit-node-start (or prev parent)) | ||
| 3877 | -1 'end thing tactic t) | ||
| 3878 | (throw 'term nil))) | ||
| 3879 | ;; Normal case. | ||
| 3880 | (setq pos (funcall advance (or prev parent)))))) | ||
| 3881 | ;; A successful step! Decrement counter. | ||
| 3882 | (decf counter)))) | ||
| 3883 | ;; Counter equal to 0 means we successfully stepped ARG steps. | ||
| 3884 | (if (eq counter 0) pos nil))) | ||
| 3885 | 3955 | ||
| 3886 | (defun treesit-thing-at-point (thing tactic) | 3956 | (defun treesit-thing-at-point (thing tactic) |
| 3887 | "Return the node for THING at point, or nil if no THING is found at point. | 3957 | "Return the node for THING at point, or nil if no THING is found at point. |
| @@ -4542,6 +4612,9 @@ before calling this function." | |||
| 4542 | 4612 | ||
| 4543 | (setq-local transpose-sexps-function #'treesit-transpose-sexps) | 4613 | (setq-local transpose-sexps-function #'treesit-transpose-sexps) |
| 4544 | 4614 | ||
| 4615 | ;; TODO: Add validation to check `treesit-thing-settings', the thing | ||
| 4616 | ;; name cannot also be a function. E.g., string. | ||
| 4617 | |||
| 4545 | (when (treesit-thing-defined-p 'sexp nil) | 4618 | (when (treesit-thing-defined-p 'sexp nil) |
| 4546 | (setq-local forward-sexp-function #'treesit-forward-sexp)) | 4619 | (setq-local forward-sexp-function #'treesit-forward-sexp)) |
| 4547 | 4620 | ||