aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/treesit.el
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lisp/treesit.el333
1 files changed, 203 insertions, 130 deletions
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