Function: treesit--things-around

treesit--things-around is a byte-compiled function defined in treesit.el.gz.

Signature

(treesit--things-around POS REGEXP &optional PRED)

Documentation

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.

Source Code

;; Defined in /usr/src/emacs/lisp/treesit.el.gz
;; prev-sibling:
;; 1. end-of-node before pos
;; 2. highest such node
;;
;; next-sibling:
;; 1. beg-of-node after pos
;; 2. highest such node
;;
;; parent:
;; 1. node covers pos
;; 2. smallest such node
(defun treesit--things-around (pos regexp &optional pred)
  "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'."
  (let* ((node (treesit-node-at pos))
         (result (list nil nil nil)))
    ;; 1. Find previous and next sibling defuns.
    (cl-loop
     for idx from 0 to 1
     for backward in '(t nil)
     ;; Make sure we go in the right direction, and the defun we find
     ;; doesn't cover POS.
     for pos-pred in (list (lambda (n) (<= (treesit-node-end n) pos))
                           (lambda (n) (>= (treesit-node-start n) pos)))
     ;; We repeatedly find next defun candidate with
     ;; `treesit-search-forward', and check if it is a valid defun,
     ;; until the node we find covers POS, meaning we've gone through
     ;; every possible sibling defuns.  But there is a catch:
     ;; `treesit-search-forward' searches bottom-up, so for each
     ;; candidate we need to go up the tree and find the top-most
     ;; valid sibling, this defun will be at the same level as POS.
     ;; Don't use `treesit-search-forward-goto', it skips nodes in
     ;; order to enforce progress.
     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))
                                (funcall pos-pred node)))))
          ;; Find the node just before/after POS to start searching.
          (save-excursion
            (while (and cursor (not (funcall pos-pred cursor)))
              (setq cursor (treesit-search-forward-goto
                            cursor "" backward backward t))))
          ;; Keep searching until we run out of candidates.
          (while (and cursor
                      (funcall pos-pred cursor)
                      (null (nth idx result)))
            (setf (nth idx result)
                  (treesit-node-top-level cursor iter-pred t))
            (setq cursor (treesit-search-forward
                          cursor regexp backward backward)))))
    ;; 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))
                            (not (treesit-node-eq node (nth 0 result)))
                            (not (treesit-node-eq node (nth 1 result)))
                            (< (treesit-node-start node)
                               pos
                               (treesit-node-end node))))))
      (setf (nth 2 result)
            (treesit-parent-until cursor iter-pred)))
    result))