Generalize treesit-defun functions to "things"

Change the "defun" in some functions (e.g. treesit--defuns-around) to
"thing".  Add a function treesit-thing-at-point.

* lisp/treesit.el (treesit--thing-unpack-pattern): New subroutine.
(treesit-beginning-of-defun)
(treesit-end-of-defun): Use new function treesit--navigate-thing.
(treesit--defuns-around): Generalize into treesit--thing-around.
(treesit--top-level-defun): Generalize into treesit--top-level-thing.
(treesit--navigate-defun): Generalize into treesit--navigate-thing.
(treesit-thing-at-point): Generalized from treesit-defun-at-point.
(treesit-defun-at-point): Use treesit-thing-at-point to do tht work.
This commit is contained in:
Yuan Fu 2022-12-24 20:17:08 -08:00
parent e8b34109ee
commit a819ca5a93
No known key found for this signature in database
GPG key ID: 56E19BC57664A442

View file

@ -1621,6 +1621,17 @@ nil.")
"The delimiter used to connect several defun names. "The delimiter used to connect several defun names.
This is used in `treesit-add-log-current-defun'.") 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-defun (&optional arg) (defun treesit-beginning-of-defun (&optional arg)
"Move backward to the beginning of a defun. "Move backward to the beginning of a defun.
@ -1633,12 +1644,16 @@ This is a tree-sitter equivalent of `beginning-of-defun'.
Behavior of this function depends on `treesit-defun-type-regexp' Behavior of this function depends on `treesit-defun-type-regexp'
and `treesit-defun-skipper'." and `treesit-defun-skipper'."
(interactive "^p") (interactive "^p")
(when-let* ((arg (or arg 1)) (pcase-let* ((arg (or arg 1))
(dest (treesit--navigate-defun (point) (- arg) 'beg))) (`(,regexp . ,pred)
(goto-char dest) (treesit--thing-unpack-pattern treesit-defun-type-regexp))
(when treesit-defun-skipper (dest (treesit--navigate-thing
(funcall treesit-defun-skipper)) (point) (- arg) 'beg regexp pred)))
t)) (when dest
(goto-char dest)
(when treesit-defun-skipper
(funcall treesit-defun-skipper))
t)))
(defun treesit-end-of-defun (&optional arg _) (defun treesit-end-of-defun (&optional arg _)
"Move forward to next end of defun. "Move forward to next end of defun.
@ -1650,11 +1665,15 @@ This is a tree-sitter equivalent of `end-of-defun'. Behavior of
this function depends on `treesit-defun-type-regexp' and this function depends on `treesit-defun-type-regexp' and
`treesit-defun-skipper'." `treesit-defun-skipper'."
(interactive "^p\nd") (interactive "^p\nd")
(when-let* ((arg (or arg 1)) (pcase-let* ((arg (or arg 1))
(dest (treesit--navigate-defun (point) arg 'end))) (`(,regexp . ,pred)
(goto-char dest) (treesit--thing-unpack-pattern treesit-defun-type-regexp))
(when treesit-defun-skipper (dest (treesit--navigate-thing
(funcall treesit-defun-skipper)))) (point) arg 'end regexp pred)))
(when dest
(goto-char dest)
(when treesit-defun-skipper
(funcall treesit-defun-skipper)))))
(defun treesit-default-defun-skipper () (defun treesit-default-defun-skipper ()
"Skips spaces after navigating a defun. "Skips spaces after navigating a defun.
@ -1680,17 +1699,15 @@ the current line if the beginning of the defun is indented."
;; parent: ;; parent:
;; 1. node covers pos ;; 1. node covers pos
;; 2. smallest such node ;; 2. smallest such node
(defun treesit--defuns-around (pos regexp &optional pred) (defun treesit--things-around (pos regexp &optional pred)
"Return the previous, next, and parent defun around POS. "Return the previous, next, and parent thing around POS.
Return a list of (PREV NEXT PARENT), where PREV and NEXT are Return a list of (PREV NEXT PARENT), where PREV and NEXT are
previous and next sibling defuns around POS, and PARENT is the previous and next sibling things around POS, and PARENT is the
parent defun surrounding POS. All of three could be nil if no parent thing surrounding POS. All of three could be nil if no
sound defun exists. sound things exists.
REGEXP and PRED are the same as in `treesit-defun-type-regexp'. REGEXP and PRED are the same as in `treesit-thing-at-point'."
Assumes `treesit-defun-type-regexp' is set."
(let* ((node (treesit-node-at pos)) (let* ((node (treesit-node-at pos))
;; NODE-BEFORE/AFTER = NODE when POS is completely in NODE, ;; NODE-BEFORE/AFTER = NODE when POS is completely in NODE,
;; but if not, that means point could be in between two ;; but if not, that means point could be in between two
@ -1750,9 +1767,9 @@ Assumes `treesit-defun-type-regexp' is set."
return cursor)) return cursor))
result)) result))
(defun treesit--top-level-defun (node regexp &optional pred) (defun treesit--top-level-thing (node regexp &optional pred)
"Return the top-level parent defun of NODE. "Return the top-level parent thing of NODE.
REGEXP and PRED are the same as in `treesit-defun-type-regexp'." REGEXP and PRED are the same as in `treesit-thing-at-point'."
(let* ((pred (or pred (lambda (_) t)))) (let* ((pred (or pred (lambda (_) t))))
;; `treesit-search-forward-goto' will make sure the matched node ;; `treesit-search-forward-goto' will make sure the matched node
;; is before POS. ;; is before POS.
@ -1792,25 +1809,23 @@ REGEXP and PRED are the same as in `treesit-defun-type-regexp'."
;; -> Obviously we don't want to go to parent's end, instead, we ;; -> 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 ;; want to go to parent's prev-sibling's end. Again, we recurse
;; in the function to do that. ;; in the function to do that.
(defun treesit--navigate-defun (pos arg side &optional recursing) (defun treesit--navigate-thing (pos arg side regexp &optional pred recursing)
"Navigate defun ARG steps from POS. "Navigate thing ARG steps from POS.
If ARG is positive, move forward that many steps, if negative, If ARG is positive, move forward that many steps, if negative,
move backward. If SIDE is `beg', stop at the beginning of a move backward. If SIDE is `beg', stop at the beginning of a
defun, if SIDE is `end', stop at the end. thing, if SIDE is `end', stop at the end.
This function doesn't actually move point, it just returns the This function doesn't actually move point, it just returns the
position it would move to. If there aren't enough defuns to move position it would move to. If there aren't enough things to move
across, return nil. across, return nil.
REGEXP and PRED are the same as in `treesit-thing-at-point'.
RECURSING is an internal parameter, if non-nil, it means this RECURSING is an internal parameter, if non-nil, it means this
function is called recursively." function is called recursively."
(pcase-let* (pcase-let*
((counter (abs arg)) ((counter (abs arg))
(`(,regexp . ,pred)
(if (consp treesit-defun-type-regexp)
treesit-defun-type-regexp
(cons treesit-defun-type-regexp nil)))
;; Move POS to the beg/end of NODE. If NODE is nil, terminate. ;; Move POS to the beg/end of NODE. If NODE is nil, terminate.
;; Return the position we moved to. ;; Return the position we moved to.
(advance (lambda (node) (advance (lambda (node)
@ -1824,13 +1839,13 @@ function is called recursively."
(while (> counter 0) (while (> counter 0)
(pcase-let (pcase-let
((`(,prev ,next ,parent) ((`(,prev ,next ,parent)
(treesit--defuns-around pos regexp pred))) (treesit--things-around pos regexp pred)))
;; When PARENT is nil, nested and top-level are the same, if ;; When PARENT is nil, nested and top-level are the same, if
;; there is a PARENT, make PARENT to be the top-level parent ;; there is a PARENT, make PARENT to be the top-level parent
;; and pretend there is no nested PREV and NEXT. ;; and pretend there is no nested PREV and NEXT.
(when (and (eq treesit-defun-tactic 'top-level) (when (and (eq treesit-defun-tactic 'top-level)
parent) parent)
(setq parent (treesit--top-level-defun (setq parent (treesit--top-level-thing
parent regexp pred) parent regexp pred)
prev nil prev nil
next nil)) next nil))
@ -1851,9 +1866,9 @@ function is called recursively."
;; (recursing) until we got out of the parents until ;; (recursing) until we got out of the parents until
;; (1) there is a next sibling defun, or (2) no more ;; (1) there is a next sibling defun, or (2) no more
;; parents [2]. ;; parents [2].
(setq pos (or (treesit--navigate-defun (setq pos (or (treesit--navigate-thing
(treesit-node-end (or next parent)) (treesit-node-end (or next parent))
1 'beg t) 1 'beg regexp pred t)
(throw 'term nil))) (throw 'term nil)))
;; Normal case. ;; Normal case.
(setq pos (funcall advance (or next parent)))) (setq pos (funcall advance (or next parent))))
@ -1863,9 +1878,9 @@ function is called recursively."
(parent t) (parent t)
(t nil))) (t nil)))
;; Special case: go to prev end-of-defun. ;; Special case: go to prev end-of-defun.
(setq pos (or (treesit--navigate-defun (setq pos (or (treesit--navigate-thing
(treesit-node-start (or prev parent)) (treesit-node-start (or prev parent))
-1 'end t) -1 'end regexp pred t)
(throw 'term nil))) (throw 'term nil)))
;; Normal case. ;; Normal case.
(setq pos (funcall advance (or prev parent))))) (setq pos (funcall advance (or prev parent)))))
@ -1875,6 +1890,28 @@ function is called recursively."
(if (eq counter 0) pos nil))) (if (eq counter 0) pos nil)))
;; TODO: In corporate into thing-at-point. ;; TODO: In corporate into thing-at-point.
(defun treesit-thing-at-point (regexp tactic &optional pred)
"Return the thing node at point or nil if none is found.
\"Thing\" is defined by REGEXP: 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.
Return the top-level defun if TACTIC is `top-level', return the
immediate parent thing if TACTIC is `nested'."
(pcase-let* ((`(,_ ,next ,parent)
(treesit--things-around (point) regexp pred))
;; 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)
node)))
(defun treesit-defun-at-point () (defun treesit-defun-at-point ()
"Return the defun node at point or nil if none is found. "Return the defun node at point or nil if none is found.
@ -1884,21 +1921,10 @@ is `top-level', return the immediate parent defun if it is
Return nil if `treesit-defun-type-regexp' is not set." Return nil if `treesit-defun-type-regexp' is not set."
(when treesit-defun-type-regexp (when treesit-defun-type-regexp
(pcase-let* ((`(,regexp . ,pred) (pcase-let ((`(,regexp . ,pred)
(if (consp treesit-defun-type-regexp) (treesit--thing-unpack-pattern
treesit-defun-type-regexp treesit-defun-type-regexp)))
(cons treesit-defun-type-regexp nil))) (treesit-thing-at-point regexp treesit-defun-tactic pred))))
(`(,_ ,next ,parent)
(treesit--defuns-around (point) regexp pred))
;; If point is at the beginning of a defun, we
;; prioritize that defun over the parent in nested
;; mode.
(node (or (and (eq (treesit-node-start next) (point))
next)
parent)))
(if (eq treesit-defun-tactic 'top-level)
(treesit--top-level-defun node regexp pred)
node))))
(defun treesit-defun-name (node) (defun treesit-defun-name (node)
"Return the defun name of NODE. "Return the defun name of NODE.