From: Juri Linkov <juri@linkov.net>
To: Eli Zaretskii <eliz@gnu.org>
Cc: mickey@masteringemacs.org, casouri@gmail.com, theo@thornhill.no,
monnier@iro.umontreal.ca, 73404@debbugs.gnu.org
Subject: bug#73404: 30.0.50; [forward/kill/etc]-sexp commands do not behave as expected in tree-sitter modes
Date: Sat, 14 Dec 2024 20:14:54 +0200 [thread overview]
Message-ID: <87jzc25dzx.fsf@mail.linkov.net> (raw)
In-Reply-To: <86ed2av878.fsf@gnu.org> (Eli Zaretskii's message of "Sat, 14 Dec 2024 13:02:19 +0200")
[-- Attachment #1: Type: text/plain, Size: 937 bytes --]
>> Actually, it's not a new command, but a new function for C-M-<RIGHT>
>> via forward-sexp-function.
>
> It's an interactive function, so it's a command.
>
>> When a ts-mode defines the 'sexp-list' thing, only then it's used.
>> Otherwise the current implementation with 'sexp' is used for C-M-<RIGHT>.
>
> Then why is the new function interactive?
Ah, I didn't notice it's interactive!
treesit-forward-sexp-list was based on treesit-forward-sexp
that has the interactive spec added by Theo in the commit 207901457c01.
I guess that Theo decided to make this function interactive
for such use case that users could use it separately
or bind it to a key.
What really needs to be interactive is the new function
treesit-forward-list added in the following patch
because there is no special variable forward-list-function for
forward-list like there is forward-sexp-function for forward-sexp.
So users might want to use it separately:
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: treesit-forward-list.patch --]
[-- Type: text/x-diff, Size: 5315 bytes --]
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))
prev parent reply other threads:[~2024-12-14 18:14 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-21 5:06 bug#73404: 30.0.50; [forward/kill/etc]-sexp commands do not behave as expected in tree-sitter modes Mickey Petersen
2024-09-26 7:42 ` Yuan Fu
2024-09-26 9:56 ` Mickey Petersen
2024-09-26 10:53 ` Eli Zaretskii
2024-09-26 12:13 ` Mickey Petersen
2024-09-26 13:46 ` Eli Zaretskii
2024-09-26 15:21 ` Mickey Petersen
2024-09-26 15:45 ` Eli Zaretskii
2024-09-27 5:43 ` Yuan Fu
2024-09-29 16:56 ` Juri Linkov
2024-10-01 3:57 ` Yuan Fu
2024-10-01 17:49 ` Juri Linkov
2024-10-02 6:14 ` Yuan Fu
2024-12-05 18:52 ` Juri Linkov
2024-12-05 19:53 ` Juri Linkov
2024-12-10 17:20 ` Juri Linkov
2024-12-11 6:31 ` Yuan Fu
2024-12-11 15:12 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-12-11 15:29 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-12-11 16:50 ` Mickey Petersen
2024-12-11 18:27 ` Yuan Fu
2024-12-12 7:17 ` Juri Linkov
2024-12-12 7:40 ` Eli Zaretskii
2024-12-12 7:58 ` Juri Linkov
2024-12-12 8:14 ` Juri Linkov
2024-12-12 16:31 ` Juri Linkov
2024-12-12 17:49 ` Juri Linkov
2024-12-12 19:13 ` Eli Zaretskii
2024-12-13 7:06 ` Juri Linkov
2024-12-14 11:02 ` Eli Zaretskii
2024-12-14 18:14 ` Juri Linkov [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87jzc25dzx.fsf@mail.linkov.net \
--to=juri@linkov.net \
--cc=73404@debbugs.gnu.org \
--cc=casouri@gmail.com \
--cc=eliz@gnu.org \
--cc=mickey@masteringemacs.org \
--cc=monnier@iro.umontreal.ca \
--cc=theo@thornhill.no \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).