From 17493fe6bfbf29dd200ec5a63114d49c4d20bfb5 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Sun, 29 Dec 2024 19:51:18 +0200 Subject: [PATCH] Add new variable 'down-list-function' for 'treesit-down-list' * lisp/emacs-lisp/lisp.el (down-list-default-function): New function. (down-list-function): New variable (bug#73404). (down-list): Move meat to 'down-list-default-function', and call 'down-list-function' when non-nil. Don't raise an error in strings or comments when 'down-list-function' is non-nil. * lisp/treesit.el (treesit--scan-error): New internal function. (treesit-forward-sexp, treesit-forward-list): Use 'treesit--scan-error'. (treesit-down-list): New function. (treesit-major-mode-setup): Set 'down-list-function' to 'treesit-down-list'. (cherry picked from commit 3c50edb2b500c6ac18696e99c3f8df597dea54d4) --- etc/NEWS | 5 ++++ lisp/emacs-lisp/lisp.el | 22 +++++++++++++---- lisp/treesit.el | 53 +++++++++++++++++++++++++++++------------ 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index d1a0b2ebe50..68f3892fdb6 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -847,6 +847,11 @@ Tree-sitter conditionally sets 'forward-list-function' for major modes that have defined 'sexp-list' in 'treesit-thing-settings' to enable the 'forward-list' motion command. +*** New function 'treesit-down-list'. +Tree-sitter conditionally sets 'down-list-function' for major modes +that have defined 'sexp-list' in 'treesit-thing-settings' to enable +the 'down-list' motion command. + +++ *** New function 'treesit-language-display-name'. This new function returns the display name of a language given the diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el index dac9dbcd83d..f6146405416 100644 --- a/lisp/emacs-lisp/lisp.el +++ b/lisp/emacs-lisp/lisp.el @@ -187,6 +187,17 @@ report errors as appropriate for this kind of usage." (or arg (setq arg 1)) (forward-list (- arg) interactive)) +(defun down-list-default-function (&optional arg) + "Default function for `down-list-function'." + (let ((inc (if (> arg 0) 1 -1))) + (while (/= arg 0) + (goto-char (or (scan-lists (point) inc -1) (buffer-end arg))) + (setq arg (- arg inc))))) + +(defvar down-list-function nil + "If non-nil, `down-list' delegates to this function. +Should take the same arguments and behave similarly to `down-list'.") + (defun down-list (&optional arg interactive) "Move forward down one level of parentheses. This command will also work on other parentheses-like expressions @@ -194,20 +205,21 @@ defined by the current language mode. With ARG, do this that many times. A negative argument means move backward but still go down a level. This command assumes point is not in a string or comment. +Calls `down-list-function' to do the work, if that is non-nil. If INTERACTIVE is non-nil, as it is interactively, report errors as appropriate for this kind of usage." (interactive "^p\nd") - (when (ppss-comment-or-string-start (syntax-ppss)) + (when (and (null down-list-function) + (ppss-comment-or-string-start (syntax-ppss))) (user-error "This command doesn't work in strings or comments")) (if interactive (condition-case _ (down-list arg nil) (scan-error (user-error "At bottom level"))) (or arg (setq arg 1)) - (let ((inc (if (> arg 0) 1 -1))) - (while (/= arg 0) - (goto-char (or (scan-lists (point) inc -1) (buffer-end arg))) - (setq arg (- arg inc)))))) + (if down-list-function + (funcall down-list-function arg) + (down-list-default-function arg)))) (defun backward-up-list (&optional arg escape-strings no-syntax-crossing) "Move backward out of one level of parentheses. diff --git a/lisp/treesit.el b/lisp/treesit.el index 21375475d9a..975f9e5c890 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -2412,6 +2412,13 @@ delimits medium sized statements in the source code. It is, however, smaller in scope than sentences. This is used by `treesit-forward-sexp' and friends.") +(defun treesit--scan-error (pred arg) + (when-let* ((parent (treesit-thing-at (point) pred t)) + (boundary (treesit-node-child parent (if (> arg 0) -1 0)))) + (signal 'scan-error (list (format-message "No more %S to move across" pred) + (treesit-node-start boundary) + (treesit-node-end boundary))))) + (defun treesit-forward-sexp (&optional arg) "Tree-sitter implementation for `forward-sexp-function'. @@ -2452,13 +2459,7 @@ across atoms (such as symbols or words) inside the list." ;; the obstacle, like `forward-sexp' does. If we couldn't ;; find a parent, we simply return nil without moving point, ;; then functions like `up-list' will signal "at top level". - (when-let* ((parent (treesit-thing-at (point) pred t)) - (boundary (if (> arg 0) - (treesit-node-child parent -1) - (treesit-node-child parent 0)))) - (signal 'scan-error (list "No more sexp to move across" - (treesit-node-start boundary) - (treesit-node-end boundary))))))) + (treesit--scan-error pred arg)))) (defun treesit-forward-sexp-list (&optional arg) "Alternative tree-sitter implementation for `forward-sexp-function'. @@ -2533,13 +2534,34 @@ ARG is described in the docstring of `forward-list'." (or (if (> arg 0) (treesit-end-of-thing pred (abs arg) 'restricted) (treesit-beginning-of-thing pred (abs arg) 'restricted)) - (when-let* ((parent (treesit-thing-at (point) pred t)) - (boundary (if (> arg 0) - (treesit-node-child parent -1) - (treesit-node-child parent 0)))) - (signal 'scan-error (list "No more group to move across" - (treesit-node-start boundary) - (treesit-node-end boundary))))))) + (treesit--scan-error pred arg)))) + +(defun treesit-down-list (&optional arg) + "Move forward down one level of parentheses. +What constitutes a level of parentheses is determined by +`sexp-list' in `treesit-thing-settings' that usually defines +parentheses-like expressions. + +This command is the tree-sitter variant of `down-list' +redefined by the variable `down-list-function'. + +ARG is described in the docstring of `down-list'." + (interactive "^p") + (let* ((pred 'sexp-list) + (arg (or arg 1)) + (inc (if (> arg 0) 1 -1))) + (while (/= arg 0) + (let* ((node (if (> arg 0) + (treesit-thing-next (point) pred) + (treesit-thing-prev (point) pred))) + (child (when node + (treesit-node-child node (if (> arg 0) 0 -1)))) + (pos (when child + (if (> arg 0) + (treesit-node-end child) + (treesit-node-start child))))) + (if pos (goto-char pos) (treesit--scan-error pred arg))) + (setq arg (- arg inc))))) (defun treesit-transpose-sexps (&optional arg) "Tree-sitter `transpose-sexps' function. @@ -3466,7 +3488,8 @@ before calling this function." (when (treesit-thing-defined-p 'sexp-list nil) (setq-local forward-sexp-function #'treesit-forward-sexp-list) - (setq-local forward-list-function #'treesit-forward-list)) + (setq-local forward-list-function #'treesit-forward-list) + (setq-local down-list-function #'treesit-down-list)) (when (treesit-thing-defined-p 'sentence nil) (setq-local forward-sentence-function #'treesit-forward-sentence)) -- 2.39.5