Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Make use of the new pred shapes in treesit.el
treesit-search-forward and friends now accept more shapes for PRED,
make use of it in navigation functions.

* lisp/treesit.el (treesit-node-top-level): Use treesit-node-match-p.
(treesit--thing-unpack-pattern): Remove function.
(treesit-beginning-of-thing)
(treesit-end-of-thing): Remove PRED argument.
(treesit--things-around): Remove PRED argument, use
treesit-node-match-p.
(treesit--top-level-thing): Remove function.
(treesit--navigate-thing): Remove PRED argument.
(treesit-thing-at-point): Update docstring, don't unpack PATTERN.

* test/src/treesit-tests.el:
(treesit--ert-test-defun-navigation): Don't unpack pattern.
  • Loading branch information
casouri committed Apr 14, 2023
1 parent a4de6d8 commit 2930294
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 62 deletions.
86 changes: 27 additions & 59 deletions lisp/treesit.el
Expand Up @@ -88,6 +88,7 @@
(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")

Expand Down Expand Up @@ -245,21 +246,19 @@ is nil, try to guess the language at BEG using `treesit-language-at'."
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))))

This comment has been minimized.

Copy link
@domq

domq May 8, 2023

According to my experiments, there's a seq missing here, i.e.

`(seq 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))

Expand Down Expand Up @@ -1887,17 +1886,6 @@ nil.")
"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.
Expand All @@ -1916,10 +1904,8 @@ should there be one. If omitted, TACTIC is considered to be
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))))

Expand All @@ -1941,10 +1927,8 @@ should there be one. If omitted, TACTIC is considered to be
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))))

Expand Down Expand Up @@ -2069,15 +2053,16 @@ the current line if the beginning of the defun is indented."
;; 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
previous and next sibling things around POS, and PARENT is the
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.
Expand All @@ -2100,9 +2085,7 @@ REGEXP and PRED are the same as in `treesit-thing-at-point'."
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
Expand All @@ -2120,9 +2103,7 @@ REGEXP and PRED are the same as in `treesit-thing-at-point'."
;; 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)
Expand All @@ -2132,15 +2113,6 @@ REGEXP and PRED are the same as in `treesit-thing-at-point'."
(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
Expand Down Expand Up @@ -2168,7 +2140,7 @@ REGEXP and PRED are the same as in `treesit-thing-at-point'."
;; -> 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,
Expand All @@ -2179,7 +2151,8 @@ This function doesn't actually move point, it just returns the
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'
Expand Down Expand Up @@ -2208,14 +2181,13 @@ function is called recursively."
(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.
Expand Down Expand Up @@ -2247,7 +2219,7 @@ function is called recursively."
;; 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))))
Expand All @@ -2259,7 +2231,7 @@ function is called recursively."
;; 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))))))
Expand All @@ -2272,26 +2244,22 @@ function is called recursively."
(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.
(node (or (and (eq (treesit-node-start next) (point))
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 ()
Expand Down
4 changes: 1 addition & 3 deletions test/src/treesit-tests.el
Expand Up @@ -916,8 +916,6 @@ and \"]\"."
collect
(cl-loop for pos in record
collect (alist-get pos marker-alist))))
(`(,regexp . ,pred) (treesit--thing-unpack-pattern
treesit-defun-type-regexp))
;; Collect positions each function returns.
(positions
(treesit--ert-collect-positions
Expand All @@ -929,7 +927,7 @@ and \"]\"."
(if-let ((pos (funcall
#'treesit--navigate-thing
(point) (car conf) (cdr conf)
regexp pred tactic)))
treesit-defun-type-regexp tactic)))
(save-excursion
(goto-char pos)
(funcall treesit-defun-skipper)
Expand Down

0 comments on commit 2930294

Please sign in to comment.