diff --git a/lisp/treesit.el b/lisp/treesit.el index 18200acf53f..a1c012b6d2f 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -2366,6 +2366,11 @@ treesit-sexp-type-regexp however, smaller in scope than sentences. This is used by `treesit-forward-sexp' and friends.") +(defun treesit-forward-list (&optional arg) + (interactive "^p") + (let ((treesit-sexp-type-regexp 'sexp-list)) + (treesit-forward-sexp arg))) + (defun treesit-forward-sexp (&optional arg) "Tree-sitter implementation for `forward-sexp-function'. @@ -2382,18 +2387,8 @@ treesit-forward-sexp by `text' and `sexp' in `treesit-thing-settings'." (interactive "^p") (let ((arg (or arg 1)) - (pred (or treesit-sexp-type-regexp 'sexp)) - (node-at-point - (treesit-node-at (point) (treesit-language-at (point))))) - (or (when (and node-at-point - ;; Make sure point is strictly inside node. - (< (treesit-node-start node-at-point) - (point) - (treesit-node-end node-at-point)) - (treesit-node-match-p node-at-point 'text t)) - (forward-sexp-default-function arg) - t) - (if (> arg 0) + (pred (or treesit-sexp-type-regexp 'sexp))) + (or (if (> arg 0) (treesit-end-of-thing pred (abs arg) 'restricted) (treesit-beginning-of-thing pred (abs arg) 'restricted)) ;; If we couldn't move, we should signal an error and report @@ -2408,6 +2403,63 @@ treesit-forward-sexp (treesit-node-start boundary) (treesit-node-end boundary))))))) +(defun treesit-forward-sexp-list (&optional arg) + "Tree-sitter implementation for `forward-sexp-function'. + +ARG is described in the docstring of `forward-sexp-function'. + +If point is inside a text environment where tree-sitter is not +supported, go forward a sexp using `forward-sexp-default-function'. +If point is inside code, use tree-sitter functions with the +following behavior. If there are no further sexps to move across, +signal `scan-error' like `forward-sexp' does. If point is already +at top-level, return nil without moving point. + +What constitutes as text and source code sexp is determined +by `text' and `sexp' in `treesit-thing-settings'." + (interactive "^p") + (let* ((arg (or arg 1)) + (pred 'sexp-list) + (default-pos + (condition-case _ + (save-excursion + (forward-sexp-default-function arg) + (point)) + (scan-error nil))) + (default-pos (unless (eq (point) default-pos) default-pos)) + (sibling-pos + (when default-pos + (save-excursion + (and (if (> arg 0) + (treesit-end-of-thing pred (abs arg) 'restricted) + (treesit-beginning-of-thing pred (abs arg) 'restricted)) + (point))))) + (sibling (when sibling-pos + (if (> arg 0) + (treesit-thing-prev sibling-pos pred) + (treesit-thing-next sibling-pos pred)))) + (sibling (when (and sibling + (if (> arg 0) + (<= (point) (treesit-node-start sibling)) + (>= (point) (treesit-node-start sibling)))) + sibling)) + (current-thing (when default-pos + (treesit-thing-at (point) pred t)))) + + ;; 'forward-sexp-default-function' should not go out of the current thing, + ;; neither go inside the next thing, neither go over the next thing + (or (when (and default-pos + (or (null current-thing) + (if (> arg 0) + (< default-pos (treesit-node-end current-thing)) + (> default-pos (treesit-node-start current-thing)))) + (or (null sibling) + (if (> arg 0) + (<= default-pos (treesit-node-start sibling)) + (>= default-pos (treesit-node-end sibling))))) + (goto-char default-pos)) + (treesit-forward-list arg)))) + (defun treesit-transpose-sexps (&optional arg) "Tree-sitter `transpose-sexps' function. ARG is the same as in `transpose-sexps'. @@ -2857,7 +2909,7 @@ treesit-navigate-thing (if (eq tactic 'restricted) (setq pos (funcall advance - (cond ((and (null next) (null prev)) parent) + (cond ((and (null next) (null prev) (not (eq thing 'sexp-list))) parent) ((> arg 0) next) (t prev)))) ;; For `nested', it's a bit more work: @@ -3254,6 +3306,9 @@ treesit-major-mode-setup (setq-local forward-sexp-function #'treesit-forward-sexp) (setq-local transpose-sexps-function #'treesit-transpose-sexps)) + (when (treesit-thing-defined-p 'sexp-list nil) + (setq-local forward-sexp-function #'treesit-forward-sexp-list)) + (when (treesit-thing-defined-p 'sentence nil) (setq-local forward-sentence-function #'treesit-forward-sentence))