diff options
| author | Theodor Thornhill | 2023-01-08 20:28:02 +0100 |
|---|---|---|
| committer | Theodor Thornhill | 2023-01-11 16:26:00 +0100 |
| commit | 37d93975780f294fd7b3fefe6281a0a36638192f (patch) | |
| tree | bedee2dea254e5d0af68bcdc710d61b7e7154b0d | |
| parent | 033f2cc6140d03e78403f37689b9f54b64bded01 (diff) | |
| download | emacs-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.texi | 56 | ||||
| -rw-r--r-- | doc/emacs/text.texi | 4 | ||||
| -rw-r--r-- | doc/lispref/positions.texi | 17 | ||||
| -rw-r--r-- | etc/NEWS | 18 | ||||
| -rw-r--r-- | lisp/textmodes/paragraphs.el | 15 | ||||
| -rw-r--r-- | lisp/treesit.el | 27 |
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 | |||
| 254 | language. Other major modes may replace any or all of these key | 255 | language. Other major modes may replace any or all of these key |
| 255 | bindings for that purpose. | 256 | bindings 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 | ||
| 263 | code, also called @dfn{sentences}. Even though sentences are usually | ||
| 264 | considered when writing human languages, Emacs can use the same | ||
| 265 | commands to move over certain constructs in programming languages | ||
| 266 | (@pxref{Sentences}, @pxref{Moving by Defuns}). In a programming | ||
| 267 | language a sentence is usually a complete language construct smaller | ||
| 268 | than defuns, but larger than sexps (@pxref{List Motion,,, elisp, The | ||
| 269 | Emacs Lisp Reference Manual}). What exactly a sentence is in a | ||
| 270 | non-human language is dependent on the target language, but usually it | ||
| 271 | is complete statements, such as a variable definition and | ||
| 272 | initialization, or a conditional statement. An example of a sentence | ||
| 273 | in the C language could be | ||
| 274 | |||
| 275 | @example | ||
| 276 | int x = 5; | ||
| 277 | @end example | ||
| 278 | |||
| 279 | or in the JavaScript language it could look like | ||
| 280 | |||
| 281 | @example | ||
| 282 | const thing = () => console.log("Hi"); | ||
| 283 | |||
| 284 | const foo = [1] == '1' | ||
| 285 | ? "No way" | ||
| 286 | : "..."; | ||
| 287 | @end example | ||
| 288 | |||
| 289 | @table @kbd | ||
| 290 | @item M-a | ||
| 291 | Move to beginning of current or preceding sentence | ||
| 292 | (@code{backward-sentence}). | ||
| 293 | @item M-e | ||
| 294 | Move 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 | ||
| 304 | sentence are @kbd{M-a} (@code{backward-sentence}) and @kbd{M-e} | ||
| 305 | (@code{forward-sentence}). If you repeat one of these commands, or | ||
| 306 | use a positive numeric argument, each repetition moves to the next | ||
| 307 | sentence 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 | ||
| 311 | a 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}. | |||
| 253 | of a sentence. Set the variable @code{sentence-end-without-period} to | 253 | of 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 | ||
| 257 | on human languages, other Emacs modes can set these command to get | ||
| 258 | similar 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 | |||
| 858 | recognize nested defuns. | 858 | recognize nested defuns. |
| 859 | @end defvar | 859 | @end defvar |
| 860 | 860 | ||
| 861 | @defvar treesit-sentence-type-regexp | ||
| 862 | The value of this variable is a regexp matching the node type of sentence | ||
| 863 | nodes. (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 | ||
| 869 | If Emacs is compiled with tree-sitter, it can use the tree-sitter | ||
| 870 | parser information to move across syntax constructs. Since what | ||
| 871 | exactly is considered a sentence varies between languages, a major | ||
| 872 | mode should set @code{treesit-sentence-type-regexp} to determine that. | ||
| 873 | Then the mode can get navigation-by-sentence functionality for free, | ||
| 874 | by using @code{forward-sentence} and | ||
| 875 | @code{backward-sentence}(@pxref{Moving by Sentences,,, emacs, The | ||
| 876 | extensible 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 |
| @@ -66,6 +66,24 @@ treesit.el now unconditionally sets 'transpose-sexps-function' for all | |||
| 66 | Tree-sitter modes. This functionality utilizes the new | 66 | Tree-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'. | ||
| 72 | Major modes now can set this variable to customize the behavior of the | ||
| 73 | 'forward-sentence' function. | ||
| 74 | |||
| 75 | *** New function 'forward-sentence-default-function'. | ||
| 76 | The previous implementation of 'forward-sentence' is moved into its | ||
| 77 | own function, to be bound by 'forward-sentence-function'. | ||
| 78 | |||
| 79 | *** New defvar-local 'treesit-sentence-type-regexp. | ||
| 80 | Similarly to 'treesit-defun-type-regexp', this variable is used to | ||
| 81 | navigate sentences in Tree-sitter enabled modes. | ||
| 82 | |||
| 83 | *** New function 'treesit-forward-sentence'. | ||
| 84 | treesit.el now conditionally sets 'forward-sentence-function' for all | ||
| 85 | Tree-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. |
| 446 | When ARG is negative, move backward repeatedly to start of sentence. | 446 | When ARG is negative, move backward repeatedly to start of sentence. |
| 447 | 447 | ||
| 448 | The variable `sentence-end' is a regular expression that matches ends of | 448 | The variable `sentence-end' is a regular expression that matches ends of |
| 449 | sentences. Also, every paragraph boundary terminates sentences as well." | 449 | sentences. 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. | ||
| 484 | See `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. | ||
| 488 | If ARG is negative, move backward repeatedly to start of | ||
| 489 | sentence. 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 | |||
| 1801 | A sentence node is a node that is bigger than a sexp, and | ||
| 1802 | delimits larger statements in the source code. It is, however, | ||
| 1803 | smaller 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 | |||
| 1809 | ARG is the same as in `forward-sentence'. | ||
| 1810 | |||
| 1811 | If inside comment or other nodes described in | ||
| 1812 | `treesit-sentence-type-regexp', use | ||
| 1813 | `forward-sentence-default-function', else move across nodes as | ||
| 1814 | described 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. |
| 1800 | This function tries to move to the beginning of a line, either by | 1825 | This 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 |