From da87895df2d6b21468b187f5a4e2ca8710e35ee7 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Sat, 29 Oct 2022 14:11:52 -0700 Subject: [PATCH] Add treesit-node-top-level Merge treesit-node-top-level-p and treesit--find-top-level-match into treesit-node-top-level. * doc/lispref/parsing.texi (Retrieving Node): Add manual entry for it. * lisp/progmodes/js.el (js--treesit-imenu-1): Replace treesit-node-top-level-p with treesit-node-top-level. * lisp/treesit.el (treesit-node-top-level-p) (treesit--find-top-level-match): Remove functions. (treesit-node-top-level): New function. * lisp/treesit.el (treesit-beginning-of-defun) (treesit-end-of-defun): Replace treesit--find-top-level-match with treesit-node-top-level. --- doc/lispref/parsing.texi | 10 +++++++ lisp/progmodes/js.el | 3 +- lisp/treesit.el | 61 ++++++++++++++++++---------------------- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index fe51222e9bc..524b81a2bbd 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi @@ -782,6 +782,16 @@ takes a node as the argument. That is, this function returns the farthest parent that still satisfies @var{predicate}. @end defun +@defun treesit-node-top-level node &optional type +This function returns the highest parent of @var{node} that has the +same type as @var{node}. If no such parent exists, it returns +@code{nil}. Therefore this function is also useful for testing +whether @var{node} is top-level. + +If @var{type} is non-@code{nil}, this function matches each parent's +type with @var{type} as a regexp, rather than using @var{node}'s type. +@end defun + @node Accessing Node Information @section Accessing Node Information @cindex information of node, syntax trees diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index d6ec3199abf..8d1cfbd3c0e 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3670,8 +3670,9 @@ definition*\"." (cond ((null ts-node) subtrees) + ;; Don't included non-top-level variable declarations. ((and (eq type 'variable) - (not (treesit-node-top-level-p ts-node))) + (treesit-node-top-level ts-node)) nil) (subtrees (let ((parent-label (js--treesit-imenu-label type name)) diff --git a/lisp/treesit.el b/lisp/treesit.el index 6176fb57ef6..027f5ecb77d 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -246,21 +246,21 @@ that language in the current buffer, and use that." (treesit-buffer-root-node parser-or-lang)))) (treesit-node-descendant-for-range root beg (or end beg) named))) -(defun treesit-node-top-level-p (node &optional type) - "Return non-nil if NODE is top-level, and nil otherwise. -Being top-level means there is no parent of NODE that has the -same type. - -If TYPE is non-nil, match each parent's type with TYPE as a regexp." - (when node - (catch 'term - (let ((plain-type (treesit-node-type node))) - (while (setq node (treesit-node-parent node)) - (when (if type - (string-match-p type (treesit-node-type node)) - (equal (treesit-node-type node) plain-type)) - (throw 'term nil)))) - t))) +(defun treesit-node-top-level (node &optional type) + "Return the top-level equivalent of NODE. +Specifically, return the highest parent of NODE that has the same +type as it. If no such parent exists, return nil. + +If TYPE is non-nil, match each parent's type with TYPE as a +regexp, rather than using NODE's type." + (let ((type (or type (treesit-node-type node))) + (result nil)) + (cl-loop for cursor = (treesit-node-parent node) + then (treesit-node-parent cursor) + while cursor + if (string-match-p type (treesit-node-type cursor)) + do (setq result cursor)) + result)) (defun treesit-buffer-root-node (&optional language) "Return the root node of the current buffer. @@ -1162,17 +1162,6 @@ For example, \"(function|class)_definition\". This is used by `treesit-beginning-of-defun' and friends.") -(defun treesit--find-top-level-match (node type) - "Return the top-level parent of NODE matching TYPE. -TYPE is a regexp, this function matches TYPE with each parent's -type." - (cl-loop for cursor = (treesit-node-parent node) - then (treesit-node-parent cursor) - while cursor - if (string-match-p type (treesit-node-type cursor)) - do (setq node cursor) - finally return node)) - (defun treesit-beginning-of-defun (&optional arg) "Tree-sitter `beginning-of-defun' function. ARG is the same as in `beginning-of-defun'." @@ -1183,15 +1172,17 @@ ARG is the same as in `beginning-of-defun'." (while (and (> arg 0) (setq node (treesit-search-forward-goto node treesit-defun-type-regexp t t))) - (setq node (treesit--find-top-level-match - node treesit-defun-type-regexp)) + (setq node (or (treesit-node-top-level + node treesit-defun-type-regexp) + node)) (setq arg (1- arg))) ;; Go forward. (while (and (< arg 0) (setq node (treesit-search-forward-goto node treesit-defun-type-regexp))) - (setq node (treesit--find-top-level-match - node treesit-defun-type-regexp)) + (setq node (or (treesit-node-top-level + node treesit-defun-type-regexp) + node)) (setq arg (1+ arg)))) (when node (goto-char (treesit-node-start node)) @@ -1201,10 +1192,12 @@ ARG is the same as in `beginning-of-defun'." "Tree-sitter `end-of-defun' function." ;; Why not simply get the largest node at point: when point is at ;; (point-min), that gives us the root node. - (let ((node (treesit--find-top-level-match - (treesit-node-at (point)) - treesit-defun-type-regexp))) - (goto-char (treesit-node-end node)))) + (let* ((node (treesit-node-at (point))) + (top (or (treesit-node-top-level + node + treesit-defun-type-regexp) + node))) + (goto-char (treesit-node-end top)))) ;;; Imenu -- 2.39.2