diff options
Diffstat (limited to '')
| -rw-r--r-- | lisp/treesit.el | 333 |
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 | |||
| 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 | ||