aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--doc/lispref/parsing.texi38
-rw-r--r--etc/NEWS13
-rw-r--r--lisp/treesit.el333
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 1789Functions below are responsible for finding things and moving across
1790them, and they have to deal with the fact that a buffer sometimes
1791contains multiple adjacent or nested parsers. By default, these
1792functions try to be helpful and search in every relevant parser at
1793point, from most specific (deepest embedded) to the least. Lisp
1794programs should be cautious and assess whether this behavior is desired
1795when using these functions as building blocks of other functions; if
1796not, explicitly pass a parser or language.
1797
1798@defun treesit-thing-prev position thing &optional parser
1790This function returns the first node before @var{position} in the 1799This function returns the first node before @var{position} in the
1791current buffer that is the specified @var{thing}. If no such node 1800current buffer that is the specified @var{thing}. If no such node
1792exists, it returns @code{nil}. 1801exists, it returns @code{nil}.
1802
1793It's guaranteed that, if a node is returned, the node's end position is 1803It's guaranteed that, if a node is returned, the node's end position is
1794less or equal to @var{position}. In other words, this function never 1804less or equal to @var{position}. In other words, this function never
1795returns a node that encloses @var{position}. 1805returns a node that encloses @var{position}.
1796 1806
1797Again, @var{thing} can be either a symbol or a predicate. 1807Again, @var{thing} can be either a symbol or a predicate.
1808
1809If @var{parser} is non-nil, only use that parser's parse tree.
1810Otherwise each parser covering point is tried from most specific
1811(deepest embedded) to least specific. If there are multiple parsers with
1812the same embed level at @var{pos}, which parser is tried first is
1813undefined. If PARSER is a language symbol, the parsers tried are
1814limited 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
1801This function is similar to @code{treesit-thing-prev}, only it returns 1818This function is similar to @code{treesit-thing-prev}, only it returns
1802the first node @emph{after} @var{position} that's the @var{thing}. It 1819the first node @emph{after} @var{position} that's the @var{thing}. It
1803also guarantees that if a node is returned, the node's start position is 1820also guarantees that if a node is returned, the node's start position is
1804greater or equal to @var{position}. 1821greater or equal to @var{position}. The @var{parser} parameter is the
1822same 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
1821Like in @code{treesit-thing-prev}, @var{thing} can be a thing symbol 1839Like in @code{treesit-thing-prev}, @var{thing} can be a thing symbol
1822defined in @code{treesit-thing-settings}, or a predicate. 1840defined in @code{treesit-thing-settings}, or a predicate, and
1841@var{parser} can be either @code{nil}, a parser, or a language symbol.
1842Like in that function, @var{parser} decides which parsers or languages
1843are searched. When there are multiple parsers available, this function
1844tries 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
1825be @code{nested}, @code{top-level}, @code{restricted}, 1847be @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
1835there's no parent. 1857there'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
1839This function returns the smallest node that's the @var{thing} and 1861This function returns the smallest node that's the @var{thing} and
1840encloses @var{position}; if there's no such node, it returns @code{nil}. 1862encloses @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
1875If @var{parser} is non-nil, only use that parser's parse tree.
1876Otherwise each parser covering point is tried from most specific (deepest
1877embedded) to least specific. If there are multiple parsers with the same
1878embed 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
diff --git a/etc/NEWS b/etc/NEWS
index 4b242370713..5e63c879e5f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -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
1254These functions now have better handling when there are multiple
1255parsers at point: 'treesit-thing-prev', 'treesit-thing-next',
1256'treesit-navigate-thing', 'treesit-thing-at'. When there are multiple
1257parsers at point, instead of using whatever 'treesit-node-at' returns at
1258point, these functions now try every relevant parser in descending
1259order of relevance. (Deeper embedded parser has higher relevance.)
1260These functions now also take an additional optional argument, PARSER,
1261that allows caller to specify a parser or language to use. That also
1262means 'treesit-beginning/end-of-defun' now can move across parsers.
1263
1264
1265+++
1253*** New command 'treesit-explore'. 1266*** New command 'treesit-explore'.
1254This command replaces 'treesit-explore-mode'. It turns on 1267This 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
3671Return 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
3671If PREV is non-nil, return the previous THING. It's guaranteed 3691If PREV is non-nil, return the previous THING. It's guaranteed
3672that returned previous sibling's end <= POS, and returned next 3692that returned previous sibling's end <= POS, and returned next
3673sibling's beginning >= POS. 3693sibling's beginning >= POS.
3674 3694
3695If PARSER is non-nil, only use that parser's parse tree. Otherwise each
3696parser covering point is tried from most specific (deepest embedded) to
3697least specific. If there are multiple parsers with the same embed level
3698at POS, which parser is tried first is undefined. If PARSER is a
3699language symbol, the parsers tried are limited to ones for that
3700language.
3701
3675Return nil if no THING can be found. THING should be a thing 3702Return nil if no THING can be found. THING should be a thing
3676defined in `treesit-thing-settings', or a predicate as described 3703defined in `treesit-thing-settings', or a predicate as described
3677in `treesit-thing-settings'." 3704in `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
3704The returned node, if non-nil, must be before POS, i.e., its end 3736The returned node, if non-nil, must be before POS, i.e., its end
3705<= POS. 3737<= POS.
3706 3738
3707THING should be a thing defined in `treesit-thing-settings', or a 3739THING should be a thing defined in `treesit-thing-settings', or a
3708predicate as described in `treesit-thing-settings'." 3740predicate as described in `treesit-thing-settings'.
3709 (treesit--thing-sibling pos thing t)) 3741
3742If PARSER is non-nil, only use that parser's parse tree. Otherwise each
3743parser covering point is tried from most specific (deepest embedded) to
3744least specific. If there are multiple parsers with the same embed level
3745at POS, which parser is tried first is undefined. If PARSER is a
3746language symbol, the parsers tried are limited to ones for that
3747language."
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
3714The returned node, if non-nil, must be after POS, i.e., its 3753The returned node, if non-nil, must be after POS, i.e., its
3715start >= POS. 3754start >= POS.
3716 3755
3717THING should be a thing defined in `treesit-thing-settings', or a 3756THING should be a thing defined in `treesit-thing-settings', or a
3718predicate as described in `treesit-thing-settings'." 3757predicate as described in `treesit-thing-settings'.
3719 (treesit--thing-sibling pos thing nil))
3720 3758
3721(defun treesit-thing-at (pos thing &optional strict) 3759If PARSER is non-nil, only use that parser's parse tree. Otherwise each
3760parser covering point is tried from most specific (deepest embedded) to
3761least specific. If there are multiple parsers with the same embed level
3762at POS, which parser is tried first is undefined. If PARSER is a
3763language symbol, the parsers tried are limited to ones for that
3764language."
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
3724The returned node, if non-nil, must enclose POS, i.e., its 3770The 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
3728THING should be a thing defined in `treesit-thing-settings' for 3774THING should be a thing defined in `treesit-thing-settings' for
3729the current buffer's major mode, or it can be a predicate 3775the current buffer's major mode, or it can be a predicate
3730described in `treesit-thing-settings'." 3776described in `treesit-thing-settings'.
3731 (let* ((cursor (treesit-node-at pos)) 3777
3732 (iter-pred (lambda (node) 3778If PARSER is non-nil, only use that parser's parse tree. Otherwise each
3733 (and (treesit-node-match-p node thing t) 3779parser covering point is tried from most specific (deepest embedded) to
3734 (if strict 3780least specific. If there are multiple parsers with the same embed level
3735 (< (treesit-node-start node) pos) 3781at POS, which parser is tried first is undefined. If PARSER is a
3736 (<= (treesit-node-start node) pos)) 3782language symbol, the parsers tried are limited to ones for that
3737 (< pos (treesit-node-end node)))))) 3783language."
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
3770If ARG is positive, move forward that many steps, if negative, 3828If 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
3788move to the parent if there is one; and move to siblings if there's no 3846move to the parent if there is one; and move to siblings if there's no
3789parent. If omitted, TACTIC is considered to be `nested'. 3847parent. If omitted, TACTIC is considered to be `nested'.
3790 3848
3849If PARSER is non-nil, only use that parser's parse tree. Otherwise each
3850parser covering point is tried from most specific (deepest embedded) to
3851least specific. If there are multiple parsers with the same embed level
3852at POS, which parser is tried first is undefined. If PARSER is a
3853language symbol, the parsers tried are limited to ones for that
3854language.
3855
3791RECURSING is an internal parameter, if non-nil, it means this 3856RECURSING is an internal parameter, if non-nil, it means this
3792function is called recursively." 3857function 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