* lisp/emacs-lisp/lisp.el (forward-sexp-default-function):
New function with body from 'forward-sexp' (bug#68993).
(forward-sexp-function): Change the default value from nil to
'forward-sexp-default-function'.
(forward-sexp): Use either 'forward-sexp-function' or
'forward-sexp-default-function'.
* lisp/treesit.el (treesit-forward-sexp): In nodes of type 'text'
fall back to 'forward-sexp-default-function'. Improve docstring.
* doc/lispref/positions.texi (List Motion): Fix pxref.
(cherry picked from commit
568c1741352a4932508fbbd474b9fd9ebe90ddfb)
exactly is considered a sexp varies between languages, a major mode
should set @code{treesit-thing-settings} to determine that. Then
the mode can get navigation-by-sexp functionality for free, by using
-@code{forward-sexp} and @code{backward-sexp}(@pxref{Moving by
-Sentences,,, emacs, The extensible self-documenting text editor}).
+@code{forward-sexp} and @code{backward-sexp}(@pxref{Expressions,
+,, emacs, The extensible self-documenting text editor}).
@node Skipping Characters
@subsection Skipping Characters
** Functions and variables to move by program sexps
+*** New function 'forward-sexp-default-function'.
+The previous implementation of 'forward-sexp' is moved into its
+own function, to be bound by 'forward-sexp-function'.
+
*** New function 'treesit-forward-sexp'.
Tree-sitter conditionally sets 'forward-sexp-function' for major modes
that have defined 'sexp' in 'treesit-thing-settings' to enable
:type 'boolean
:group 'lisp)
-(defvar forward-sexp-function nil
+(defun forward-sexp-default-function (&optional arg)
+ "Default function for `forward-sexp-function'."
+ (goto-char (or (scan-sexps (point) arg) (buffer-end arg)))
+ (if (< arg 0) (backward-prefix-chars)))
+
+(defvar forward-sexp-function #'forward-sexp-default-function
;; FIXME:
;; - for some uses, we may want a "sexp-only" version, which only
;; jumps over a well-formed sexp, rather than some dwimish thing
"No next sexp"
"No previous sexp"))))
(or arg (setq arg 1))
- (if forward-sexp-function
- (funcall forward-sexp-function arg)
- (goto-char (or (scan-sexps (point) arg) (buffer-end arg)))
- (if (< arg 0) (backward-prefix-chars)))))
+ (funcall (or forward-sexp-function
+ #'forward-sexp-default-function)
+ arg)))
(defun backward-sexp (&optional arg interactive)
"Move backward across one balanced expression (sexp).
(defun treesit-forward-sexp (&optional arg)
"Tree-sitter implementation for `forward-sexp-function'.
-ARG is described in the docstring of `forward-sexp-function'. 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."
+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 (or treesit-sexp-type-regexp 'sexp)))
- (or (if (> arg 0)
+ (or (when (treesit-node-match-p (treesit-node-at (point)) 'text t)
+ (funcall #'forward-sexp-default-function arg)
+ t)
+ (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