(declare-function treesit-search-forward "treesit.c")
(declare-function treesit-induce-sparse-tree "treesit.c")
(declare-function treesit-subtree-stat "treesit.c")
+(declare-function treesit-node-match-p "treesit.c")
(declare-function treesit-available-p "treesit.c")
Specifically, return the highest parent of NODE that has the same
type as it. If no such parent exists, return nil.
-If PRED is non-nil, match each parent's type with PRED as a
-regexp, rather than using NODE's type. PRED can also be a
-function that takes the node as an argument, and return
-non-nil/nil for match/no match.
+If PRED is non-nil, match each parent's type with PRED rather
+than using NODE's type. PRED can also be a predicate function,
+and more. See `treesit-things-definition' for detail.
If INCLUDE-NODE is non-nil, return NODE if it satisfies PRED."
- (let ((pred (or pred (treesit-node-type node)))
+ (let ((pred (or pred (rx-to-string
+ `(bos ,(treesit-node-type node) eos))))
(result nil))
(cl-loop for cursor = (if include-node node
(treesit-node-parent node))
then (treesit-node-parent cursor)
while cursor
- if (if (stringp pred)
- (string-match-p pred (treesit-node-type cursor))
- (funcall pred cursor))
+ if (treesit-node-match-p cursor pred)
do (setq result cursor))
result))
"The delimiter used to connect several defun names.
This is used in `treesit-add-log-current-defun'.")
-(defsubst treesit--thing-unpack-pattern (pattern)
- "Unpack PATTERN in the shape of `treesit-defun-type-regexp'.
-
-Basically,
-
- (unpack REGEXP) = (REGEXP . nil)
- (unpack (REGEXP . PRED)) = (REGEXP . PRED)"
- (if (consp pattern)
- pattern
- (cons pattern nil)))
-
(defun treesit-beginning-of-thing (pattern &optional arg tactic)
"Like `beginning-of-defun', but generalized into things.
Return non-nil if successfully moved, nil otherwise."
(pcase-let* ((arg (or arg 1))
- (`(,regexp . ,pred) (treesit--thing-unpack-pattern
- pattern))
(dest (treesit--navigate-thing
- (point) (- arg) 'beg regexp pred tactic)))
+ (point) (- arg) 'beg pattern tactic)))
(when dest
(goto-char dest))))
Return non-nil if successfully moved, nil otherwise."
(pcase-let* ((arg (or arg 1))
- (`(,regexp . ,pred) (treesit--thing-unpack-pattern
- pattern))
(dest (treesit--navigate-thing
- (point) arg 'end regexp pred tactic)))
+ (point) arg 'end pattern tactic)))
(when dest
(goto-char dest))))
;; parent:
;; 1. node covers pos
;; 2. smallest such node
-(defun treesit--things-around (pos regexp &optional pred)
+(defun treesit--things-around (pos regexp)
"Return the previous, next, and parent thing around POS.
Return a list of (PREV NEXT PARENT), where PREV and NEXT are
parent thing surrounding POS. All of three could be nil if no
sound things exists.
-REGEXP and PRED are the same as in `treesit-thing-at-point'."
+REGEXP can be a regexp, a predicate function, and more. See
+`treesit-things-definition' for details."
(let* ((node (treesit-node-at pos))
(result (list nil nil nil)))
;; 1. Find previous and next sibling defuns.
when node
do (let ((cursor node)
(iter-pred (lambda (node)
- (and (string-match-p
- regexp (treesit-node-type node))
- (or (null pred) (funcall pred node))
+ (and (treesit-node-match-p node regexp)
(funcall pos-pred node)))))
;; Find the node just before/after POS to start searching.
(save-excursion
;; 2. Find the parent defun.
(let ((cursor (or (nth 0 result) (nth 1 result) node))
(iter-pred (lambda (node)
- (and (string-match-p
- regexp (treesit-node-type node))
- (or (null pred) (funcall pred node))
+ (and (treesit-node-match-p node regexp)
(not (treesit-node-eq node (nth 0 result)))
(not (treesit-node-eq node (nth 1 result)))
(< (treesit-node-start node)
(treesit-parent-until cursor iter-pred)))
result))
-(defun treesit--top-level-thing (node regexp &optional pred)
- "Return the top-level parent thing of NODE.
-REGEXP and PRED are the same as in `treesit-thing-at-point'."
- (treesit-node-top-level
- node (lambda (node)
- (and (string-match-p regexp (treesit-node-type node))
- (or (null pred) (funcall pred node))))
- t))
-
;; The basic idea for nested defun navigation is that we first try to
;; move across sibling defuns in the same level, if no more siblings
;; exist, we move to parents's beg/end, rinse and repeat. We never
;; -> Obviously we don't want to go to parent's end, instead, we
;; want to go to parent's prev-sibling's end. Again, we recurse
;; in the function to do that.
-(defun treesit--navigate-thing (pos arg side regexp &optional pred tactic recursing)
+(defun treesit--navigate-thing (pos arg side regexp &optional tactic recursing)
"Navigate thing ARG steps from POS.
If ARG is positive, move forward that many steps, if negative,
position it would move to. If there aren't enough things to move
across, return nil.
-REGEXP and PRED are the same as in `treesit-thing-at-point'.
+REGEXP can be a regexp, a predicate function, and more. See
+`treesit-things-definition' for detail.
TACTIC determines how does this function move between things. It
can be `nested', `top-level', `restricted', or nil. `nested'
(while (> counter 0)
(pcase-let
((`(,prev ,next ,parent)
- (treesit--things-around pos regexp pred)))
+ (treesit--things-around pos regexp)))
;; When PARENT is nil, nested and top-level are the same, if
;; there is a PARENT, make PARENT to be the top-level parent
;; and pretend there is no nested PREV and NEXT.
(when (and (eq tactic 'top-level)
parent)
- (setq parent (treesit--top-level-thing
- parent regexp pred)
+ (setq parent (treesit-node-top-level parent regexp t)
prev nil
next nil))
;; If TACTIC is `restricted', the implementation is very simple.
;; the end of next before recurring.)
(setq pos (or (treesit--navigate-thing
(treesit-node-end (or next parent))
- 1 'beg regexp pred tactic t)
+ 1 'beg regexp tactic t)
(throw 'term nil)))
;; Normal case.
(setq pos (funcall advance (or next parent))))
;; Special case: go to prev end-of-defun.
(setq pos (or (treesit--navigate-thing
(treesit-node-start (or prev parent))
- -1 'end regexp pred tactic t)
+ -1 'end regexp tactic t)
(throw 'term nil)))
;; Normal case.
(setq pos (funcall advance (or prev parent))))))
(defun treesit-thing-at-point (pattern tactic)
"Return the thing node at point or nil if none is found.
-\"Thing\" is defined by PATTERN, which can be either a string
-REGEXP or a cons cell (REGEXP . PRED): if a node's type matches
-REGEXP, it is a thing. The \"thing\" could be further restricted
-by PRED: if non-nil, PRED should be a function that takes a node
-and returns t if the node is a \"thing\", and nil if not.
+\"Thing\" is defined by PATTERN, which can be a regexp, a
+predication function, and more, see `treesit-things-definition'
+for detail.
Return the top-level defun if TACTIC is `top-level', return the
immediate parent thing if TACTIC is `nested'."
- (pcase-let* ((`(,regexp . ,pred)
- (treesit--thing-unpack-pattern pattern))
- (`(,_ ,next ,parent)
- (treesit--things-around (point) regexp pred))
+ (pcase-let* ((`(,_ ,next ,parent)
+ (treesit--things-around (point) pattern))
;; If point is at the beginning of a thing, we
;; prioritize that thing over the parent in nested
;; mode.
next)
parent)))
(if (eq tactic 'top-level)
- (treesit--top-level-thing node regexp pred)
+ (treesit-node-top-level node pattern t)
node)))
(defun treesit-defun-at-point ()