aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodor Thornhill2023-01-08 20:28:02 +0100
committerTheodor Thornhill2023-01-11 16:26:00 +0100
commit37d93975780f294fd7b3fefe6281a0a36638192f (patch)
treebedee2dea254e5d0af68bcdc710d61b7e7154b0d
parent033f2cc6140d03e78403f37689b9f54b64bded01 (diff)
downloademacs-37d93975780f294fd7b3fefe6281a0a36638192f.tar.gz
emacs-37d93975780f294fd7b3fefe6281a0a36638192f.zip
Add forward-sentence with tree sitter support (bug#60623)
* etc/NEWS: Mention the new changes. * lisp/textmodes/paragraphs.el (forward-sentence-default-function): Move old implementation to its own function. (forward-sentence-function): New defvar defaulting to old behavior. (forward-sentence): Use the variable in this function unconditionally. * lisp/treesit.el (treesit-sentence-type-regexp): New defvar. (treesit-forward-sentence): New defun. (treesit-major-mode-setup): Conditionally set forward-sentence-function. * doc/emacs/programs.texi (Defuns): Add new subsection. (Moving by Sentences): Add some documentation with xrefs to the elisp manual and related nodes. * doc/lispref/positions.texi (List Motion): Mention treesit-sentence-type-regexp and describe how to enable this functionality.
-rw-r--r--doc/emacs/programs.texi56
-rw-r--r--doc/emacs/text.texi4
-rw-r--r--doc/lispref/positions.texi17
-rw-r--r--etc/NEWS18
-rw-r--r--lisp/textmodes/paragraphs.el15
-rw-r--r--lisp/treesit.el27
6 files changed, 135 insertions, 2 deletions
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 44cad5a148e..a2cdf6c6eb9 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -163,6 +163,7 @@ Emacs we use it for all languages.
163* Left Margin Paren:: An open-paren or similar opening delimiter 163* Left Margin Paren:: An open-paren or similar opening delimiter
164 starts a defun if it is at the left margin. 164 starts a defun if it is at the left margin.
165* Moving by Defuns:: Commands to move over or mark a major definition. 165* Moving by Defuns:: Commands to move over or mark a major definition.
166* Moving by Sentences:: Commands to move over certain code units.
166* Imenu:: Making buffer indexes as menus. 167* Imenu:: Making buffer indexes as menus.
167* Which Function:: Which Function mode shows which function you are in. 168* Which Function:: Which Function mode shows which function you are in.
168@end menu 169@end menu
@@ -254,6 +255,61 @@ they do their standard jobs in a way better fitting a particular
254language. Other major modes may replace any or all of these key 255language. Other major modes may replace any or all of these key
255bindings for that purpose. 256bindings for that purpose.
256 257
258@node Moving by Sentences
259@subsection Moving by Sentences
260@cindex sentences, in programming languages
261
262 These commands move point or set up the region based on units of
263code, also called @dfn{sentences}. Even though sentences are usually
264considered when writing human languages, Emacs can use the same
265commands to move over certain constructs in programming languages
266(@pxref{Sentences}, @pxref{Moving by Defuns}). In a programming
267language a sentence is usually a complete language construct smaller
268than defuns, but larger than sexps (@pxref{List Motion,,, elisp, The
269Emacs Lisp Reference Manual}). What exactly a sentence is in a
270non-human language is dependent on the target language, but usually it
271is complete statements, such as a variable definition and
272initialization, or a conditional statement. An example of a sentence
273in the C language could be
274
275@example
276int x = 5;
277@end example
278
279or in the JavaScript language it could look like
280
281@example
282const thing = () => console.log("Hi");
283
284const foo = [1] == '1'
285 ? "No way"
286 : "...";
287@end example
288
289@table @kbd
290@item M-a
291Move to beginning of current or preceding sentence
292(@code{backward-sentence}).
293@item M-e
294Move to end of current or following sentence (@code{forward-sentence}).
295@end table
296
297@cindex move to beginning or end of sentence
298@cindex sentence, move to beginning or end
299@kindex M-a @r{(programming modes)}
300@kindex M-e @r{(programming modes)}
301@findex backward-sentence @r{(programming modes)}
302@findex forward-sentence @r{(programming modes)}
303 The commands to move to the beginning and end of the current
304sentence are @kbd{M-a} (@code{backward-sentence}) and @kbd{M-e}
305(@code{forward-sentence}). If you repeat one of these commands, or
306use a positive numeric argument, each repetition moves to the next
307sentence in the direction of motion.
308
309 @kbd{M-a} with a negative argument @minus{}@var{n} moves forward
310@var{n} times to the next end of a sentence. Likewise, @kbd{M-e} with
311a negative argument moves back to a start of a sentence.
312
257@node Imenu 313@node Imenu
258@subsection Imenu 314@subsection Imenu
259@cindex index of buffer definitions 315@cindex index of buffer definitions
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 8fbf731a4f7..6e16e743a52 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -253,6 +253,10 @@ value of @code{sentence-end-double-space}.
253of a sentence. Set the variable @code{sentence-end-without-period} to 253of a sentence. Set the variable @code{sentence-end-without-period} to
254@code{t} in such cases. 254@code{t} in such cases.
255 255
256 Even though the above mentioned sentence movement commands are based
257on human languages, other Emacs modes can set these command to get
258similar functionality (@pxref{Moving by Sentences}).
259
256@node Paragraphs 260@node Paragraphs
257@section Paragraphs 261@section Paragraphs
258@cindex paragraphs 262@cindex paragraphs
diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi
index f3824436246..8d95ecee7ab 100644
--- a/doc/lispref/positions.texi
+++ b/doc/lispref/positions.texi
@@ -858,6 +858,23 @@ top-level defuns, if the value is @code{nested}, navigation functions
858recognize nested defuns. 858recognize nested defuns.
859@end defvar 859@end defvar
860 860
861@defvar treesit-sentence-type-regexp
862The value of this variable is a regexp matching the node type of sentence
863nodes. (For ``node'' and ``node type'', @pxref{Parsing Program Source}.)
864@end defvar
865
866@findex treesit-forward-sentence
867@findex forward-sentence
868@findex backward-sentence
869If Emacs is compiled with tree-sitter, it can use the tree-sitter
870parser information to move across syntax constructs. Since what
871exactly is considered a sentence varies between languages, a major
872mode should set @code{treesit-sentence-type-regexp} to determine that.
873Then the mode can get navigation-by-sentence functionality for free,
874by using @code{forward-sentence} and
875@code{backward-sentence}(@pxref{Moving by Sentences,,, emacs, The
876extensible self-documenting text editor}).
877
861@node Skipping Characters 878@node Skipping Characters
862@subsection Skipping Characters 879@subsection Skipping Characters
863@cindex skipping characters 880@cindex skipping characters
diff --git a/etc/NEWS b/etc/NEWS
index 3aa8f2abb77..0c782eeaee8 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -66,6 +66,24 @@ treesit.el now unconditionally sets 'transpose-sexps-function' for all
66Tree-sitter modes. This functionality utilizes the new 66Tree-sitter modes. This functionality utilizes the new
67'transpose-sexps-function'. 67'transpose-sexps-function'.
68 68
69** Commands and variables to move by program statements
70
71*** New variable 'forward-sentence-function'.
72Major modes now can set this variable to customize the behavior of the
73'forward-sentence' function.
74
75*** New function 'forward-sentence-default-function'.
76The previous implementation of 'forward-sentence' is moved into its
77own function, to be bound by 'forward-sentence-function'.
78
79*** New defvar-local 'treesit-sentence-type-regexp.
80Similarly to 'treesit-defun-type-regexp', this variable is used to
81navigate sentences in Tree-sitter enabled modes.
82
83*** New function 'treesit-forward-sentence'.
84treesit.el now conditionally sets 'forward-sentence-function' for all
85Tree-sitter modes that sets 'treesit-sentence-type-regexp'.
86
69 87
70* Changes in Specialized Modes and Packages in Emacs 30.1 88* Changes in Specialized Modes and Packages in Emacs 30.1
71--- 89---
diff --git a/lisp/textmodes/paragraphs.el b/lisp/textmodes/paragraphs.el
index 73abb155aaa..bf249fdcdfb 100644
--- a/lisp/textmodes/paragraphs.el
+++ b/lisp/textmodes/paragraphs.el
@@ -441,13 +441,12 @@ the current paragraph with the one containing the mark."
441 (if (< (point) (point-max)) 441 (if (< (point) (point-max))
442 (end-of-paragraph-text)))))) 442 (end-of-paragraph-text))))))
443 443
444(defun forward-sentence (&optional arg) 444(defun forward-sentence-default-function (&optional arg)
445 "Move forward to next end of sentence. With argument, repeat. 445 "Move forward to next end of sentence. With argument, repeat.
446When ARG is negative, move backward repeatedly to start of sentence. 446When ARG is negative, move backward repeatedly to start of sentence.
447 447
448The variable `sentence-end' is a regular expression that matches ends of 448The variable `sentence-end' is a regular expression that matches ends of
449sentences. Also, every paragraph boundary terminates sentences as well." 449sentences. Also, every paragraph boundary terminates sentences as well."
450 (interactive "^p")
451 (or arg (setq arg 1)) 450 (or arg (setq arg 1))
452 (let ((opoint (point)) 451 (let ((opoint (point))
453 (sentence-end (sentence-end))) 452 (sentence-end (sentence-end)))
@@ -480,6 +479,18 @@ sentences. Also, every paragraph boundary terminates sentences as well."
480 (let ((npoint (constrain-to-field nil opoint t))) 479 (let ((npoint (constrain-to-field nil opoint t)))
481 (not (= npoint opoint))))) 480 (not (= npoint opoint)))))
482 481
482(defvar forward-sentence-function #'forward-sentence-default-function
483 "Function to be used to calculate sentence movements.
484See `forward-sentence' for a description of its behavior.")
485
486(defun forward-sentence (&optional arg)
487 "Move forward to next end of sentence. With argument ARG, repeat.
488If ARG is negative, move backward repeatedly to start of
489sentence. Delegates its work to `forward-sentence-function'."
490 (interactive "^p")
491 (or arg (setq arg 1))
492 (funcall forward-sentence-function arg))
493
483(defun count-sentences (start end) 494(defun count-sentences (start end)
484 "Count sentences in current buffer from START to END." 495 "Count sentences in current buffer from START to END."
485 (let ((sentences 0) 496 (let ((sentences 0)
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 25b2c70ce0a..f2e1b4ac807 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -1795,6 +1795,31 @@ comments and multiline string literals. For example,
1795\"text_block\" in the case of a string. This is used by 1795\"text_block\" in the case of a string. This is used by
1796`prog-fill-reindent-defun' and friends.") 1796`prog-fill-reindent-defun' and friends.")
1797 1797
1798(defvar-local treesit-sentence-type-regexp nil
1799 "A regexp that matches the node type of sentence nodes.
1800
1801A sentence node is a node that is bigger than a sexp, and
1802delimits larger statements in the source code. It is, however,
1803smaller in scope than defuns. This is used by
1804`treesit-forward-sentence' and friends.")
1805
1806(defun treesit-forward-sentence (&optional arg)
1807 "Tree-sitter `forward-sentence-function' function.
1808
1809ARG is the same as in `forward-sentence'.
1810
1811If inside comment or other nodes described in
1812`treesit-sentence-type-regexp', use
1813`forward-sentence-default-function', else move across nodes as
1814described by `treesit-sentence-type-regexp'."
1815 (if (string-match-p
1816 treesit-text-type-regexp
1817 (treesit-node-type (treesit-node-at (point))))
1818 (funcall #'forward-sentence-default-function arg)
1819 (funcall
1820 (if (> arg 0) #'treesit-end-of-thing #'treesit-beginning-of-thing)
1821 treesit-sentence-type-regexp (abs arg))))
1822
1798(defun treesit-default-defun-skipper () 1823(defun treesit-default-defun-skipper ()
1799 "Skips spaces after navigating a defun. 1824 "Skips spaces after navigating a defun.
1800This function tries to move to the beginning of a line, either by 1825This function tries to move to the beginning of a line, either by
@@ -2259,6 +2284,8 @@ before calling this function."
2259 #'treesit-add-log-current-defun)) 2284 #'treesit-add-log-current-defun))
2260 2285
2261 (setq-local transpose-sexps-function #'treesit-transpose-sexps) 2286 (setq-local transpose-sexps-function #'treesit-transpose-sexps)
2287 (when treesit-sentence-type-regexp
2288 (setq-local forward-sentence-function #'treesit-forward-sentence))
2262 2289
2263 ;; Imenu. 2290 ;; Imenu.
2264 (when treesit-simple-imenu-settings 2291 (when treesit-simple-imenu-settings