From: Juri Linkov Date: Thu, 19 Dec 2024 07:27:28 +0000 (+0200) Subject: New treesit thing 'sexp-list' (bug#73404) X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=bb7534bfdd5fc6ff103e078294abbfbdff10151d;p=emacs.git New treesit thing 'sexp-list' (bug#73404) * lisp/treesit.el (treesit-forward-list): New command. (treesit-forward-sexp): Check the thing 'text' only when 'treesit-sexp-type-regexp' is nil. (treesit-forward-sexp-list): New function. (treesit-navigate-thing): Don't try moving to parent when thing is 'sexp-list'. (treesit-major-mode-setup): Set 'forward-sexp-function' to 'treesit-forward-sexp-list' when the 'sexp-list' thing is defined. (cherry picked from commit d930d7f8651897dc3130ff16731751691566d490) --- diff --git a/etc/NEWS b/etc/NEWS index 7dff82b8cbd..bf4c890dfdd 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -821,6 +821,13 @@ override flag by 'treesit-font-lock-setting-query', 'treesit-font-lock-setting-feature', 'treesit-font-lock-setting-enable', and 'treesit-font-lock-setting-override'. +*** New treesit thing 'sexp-list'. +Unlike the existing thing 'sexp' that defines both lists and atoms, +'sexp-list' defines only lists to be navigated by 'forward-sexp'. +The new function 'treesit-forward-sexp-list' uses 'sexp-list' +to move across lists. But to move across atoms inside the list +it uses `forward-sexp-default-function'. + +++ ** New optional BUFFER argument for 'string-pixel-width'. If supplied, 'string-pixel-width' will use any face remappings from diff --git a/lisp/treesit.el b/lisp/treesit.el index 6cb37631919..0fa0862f441 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -2358,6 +2358,25 @@ 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-forward-list (&optional arg) + "Move forward across a list. +What constitutes a list is determined by `sexp-list' in +`treesit-thing-settings' that usually defines +parentheses-like expressions. + +Unlike `forward-sexp', this command moves only across a list, +but not across atoms (such as symbols or words) inside the list. + +This command is the tree-sitter variant of `forward-list'. But since +`forward-list' has no \"forward-list-function\" like there is +`forward-sexp-function' for `forward-sexp', this command +can be used on its own. + +ARG is described in the docstring of `forward-list'." + (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'. @@ -2371,12 +2390,18 @@ 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'." +by `text' and `sexp' in `treesit-thing-settings'. + +There is an alternative implementation in `treesit-forward-sexp-list' +that uses `sexp-list' in `treesit-thing-settings' to move only +across lists, whereas uses `forward-sexp-default-function' to move +across atoms (such as symbols or words) inside the list." (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))))) + (when (null treesit-sexp-type-regexp) + (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) @@ -2400,6 +2425,60 @@ by `text' and `sexp' in `treesit-thing-settings'." (treesit-node-start boundary) (treesit-node-end boundary))))))) +(defun treesit-forward-sexp-list (&optional arg) + "Alternative tree-sitter implementation for `forward-sexp-function'. + +Whereas `treesit-forward-sexp' moves across both lists and atoms +using `sexp' in `treesit-thing-settings', this function uses +`sexp-list' in `treesit-thing-settings' to move only across lists. +But to move across atoms (such as symbols or words) inside the list +it uses `forward-sexp-default-function' as long as it doesn't go +outside of the boundaries of the current list. + +ARG is described in the docstring of `forward-sexp-function'." + (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-end 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 or 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'. @@ -2849,7 +2928,9 @@ function is called recursively." (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: @@ -3246,6 +3327,9 @@ before calling this function." (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))