From 293029458c87d6ec18a6f6a5958d0b499310ca3b Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 13 Apr 2023 19:48:57 -0700 Subject: 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. --- lisp/treesit.el | 86 ++++++++++++++++++--------------------------------------- 1 file changed, 27 insertions(+), 59 deletions(-) (limited to 'lisp') diff --git a/lisp/treesit.el b/lisp/treesit.el index 3932920a962..e7e336f4609 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -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") @@ -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)))) (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)) @@ -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. @@ -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)))) @@ -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)))) @@ -2069,7 +2053,7 @@ 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 @@ -2077,7 +2061,8 @@ 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. @@ -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 @@ -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) @@ -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 @@ -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, @@ -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' @@ -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. @@ -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)))) @@ -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)))))) @@ -2272,18 +2244,14 @@ 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. @@ -2291,7 +2259,7 @@ immediate parent thing if TACTIC is `nested'." 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 () -- cgit v1.2.3