all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Plug treesit.el into other emacs constructs
@ 2022-12-12 14:33 Theodor Thornhill
  2022-12-12 14:45 ` Eli Zaretskii
  2022-12-12 15:46 ` Stefan Monnier
  0 siblings, 2 replies; 54+ messages in thread
From: Theodor Thornhill @ 2022-12-12 14:33 UTC (permalink / raw)
  To: emacs-devel; +Cc: eliz, casouri

[-- Attachment #1: Type: text/plain, Size: 2717 bytes --]


Hello!

I've started plugging tree-sitter support into other constructs in
emacs, and I would love some feedback and suggestions for how to
proceed.  Emacs has some nice utilities we could piggyback on, namely:

- forward-sexp
- forward-sentence
- forward-*

What they provide is suddenly access to all of the transpose-*
functions.  I'll show with some examples, where | denotes point, and if
there are two | it denotes active region, like this: |String foo|.

The patch supplied below enables the following:

* Navigation:
** Forward-sentence:
Executing M-e repeatedly will go from:
```
public void foo() {
  |int x = 5;
  int x = 6;
}
```
to
```
public void foo() {
  int x = 5;|
  int x = 6;
}
```
then
```
public void foo() {
  int x = 5;
  int x = 6;|
}
```
** transpose-sentence:
Executing M-x transpose-sentence repeatedly will go from:
```
public void foo() {
  int x = 5;|
  if (r) {
  
  }
  int x = 6;
}
```
to
```
public void foo() {
  if (r) {
  
  }
  int x = 5;|
  int x = 6;
}
```
then
```
public void foo() {
  if (r) {
  
  }
  int x = 6;
  int x = 5;|
}
```
Further transpose-sentence will not go out of the method because there
are no siblings

** kill-sentence:
Executing M-k repeatedly will go from:
```
public void foo() {
  |int x = 6;
  if (foo) {
            
  }
}
```
to
```
public void foo() {
  |
  if (foo) {
            
  }
}
```
then
```
public void foo() {
    |
}
```

** Forward-sexp:
Executing C-M-f repeatedly will go from:
```
public void foo(|String bar, String baz) {}
```
to
```
public void foo(String bar|, String baz) {}
```
and
```
public void foo(String bar, String baz|) {}
```
** Mark-sexp:
Executing C-M-@ repeatedly will go from:
```
public void foo(|String bar, String baz) {}
```
to
```
public void foo(|String bar|, String baz) {}
```
then
```
public void foo(|String bar, String baz|) {}
```

** transpose-sexp:
Executing C-M-t repeatedly will go from:
```
public void foo(int bar,| String baz) {}
```
to
```
public void foo(String baz, int bar|) {}
```

** kill-sexp:
Executing C-M-k repeatedly will go from:
```
public void foo(|int bar, String baz) {}
```
to
```
public void foo(|, int bar) {}
```
then
```
public void foo() {}
```

And more.


The patch only adds a few things:

- (defvar-local forward-sentence-function nil), the analog to
  forward-sexp-function and beginning-of-defun-function
- treesit-sexp-type-regexp and treesit-sentence-type-regexp, both to be
  added to treesit-major-mode-setup, and added to each *-ts-mode.

Is the general idea ok?  Could we add these or similar changes to
paragraphs.el and just piggyback on it inside of treesit?

If we want this I can start working on preparing a patch for this on the
master branch.

Theo


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-WIP-forward-implementation.patch --]
[-- Type: text/x-diff, Size: 7670 bytes --]

From e67e26f6d4dc39436b169b4fc026376d9f74b6b9 Mon Sep 17 00:00:00 2001
From: Theodor Thornhill <theo@thornhill.no>
Date: Mon, 12 Dec 2022 15:28:05 +0100
Subject: [PATCH] WIP forward-* implementation

---
 lisp/progmodes/java-ts-mode.el | 10 +++++
 lisp/textmodes/paragraphs.el   | 73 +++++++++++++++++++---------------
 lisp/treesit.el                | 50 ++++++++++++++++++++++-
 3 files changed, 100 insertions(+), 33 deletions(-)

diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
index d5f4f55fe0..e76380ec2a 100644
--- a/lisp/progmodes/java-ts-mode.el
+++ b/lisp/progmodes/java-ts-mode.el
@@ -336,6 +336,16 @@ java-ts-mode
                             "package_declaration"
                             "module_declaration")))
 
+  (setq-local treesit-sentence-type-regexp
+              (regexp-opt '("declaration"
+                            "statement")))
+
+  (setq-local treesit-sexp-type-regexp
+              (regexp-opt '("declaration"
+                            "statement"
+                            "formal_parameter"
+                            "string_literal")))
+
   ;; Font-lock.
   (setq-local treesit-font-lock-settings java-ts-mode--font-lock-settings)
   (setq-local treesit-font-lock-feature-list
diff --git a/lisp/textmodes/paragraphs.el b/lisp/textmodes/paragraphs.el
index c500dc014f..b53fe66eee 100644
--- a/lisp/textmodes/paragraphs.el
+++ b/lisp/textmodes/paragraphs.el
@@ -441,6 +441,8 @@ end-of-paragraph-text
 	  (if (< (point) (point-max))
 	      (end-of-paragraph-text))))))
 
+(defvar-local forward-sentence-function nil)
+
 (defun forward-sentence (&optional arg)
   "Move forward to next end of sentence.  With argument, repeat.
 When ARG is negative, move backward repeatedly to start of sentence.
@@ -449,36 +451,40 @@ forward-sentence
 sentences.  Also, every paragraph boundary terminates sentences as well."
   (interactive "^p")
   (or arg (setq arg 1))
-  (let ((opoint (point))
-        (sentence-end (sentence-end)))
-    (while (< arg 0)
-      (let ((pos (point))
-	    par-beg par-text-beg)
-	(save-excursion
-	  (start-of-paragraph-text)
-	  ;; Start of real text in the paragraph.
-	  ;; We move back to here if we don't see a sentence-end.
-	  (setq par-text-beg (point))
-	  ;; Start of the first line of the paragraph.
-	  ;; We use this as the search limit
-	  ;; to allow sentence-end to match if it is anchored at
-	  ;; BOL and the paragraph starts indented.
-	  (beginning-of-line)
-	  (setq par-beg (point)))
-	(if (and (re-search-backward sentence-end par-beg t)
-		 (or (< (match-end 0) pos)
-		     (re-search-backward sentence-end par-beg t)))
-	    (goto-char (match-end 0))
-	  (goto-char par-text-beg)))
-      (setq arg (1+ arg)))
-    (while (> arg 0)
-      (let ((par-end (save-excursion (end-of-paragraph-text) (point))))
-	(if (re-search-forward sentence-end par-end t)
-	    (skip-chars-backward " \t\n")
-	  (goto-char par-end)))
-      (setq arg (1- arg)))
-    (let ((npoint (constrain-to-field nil opoint t)))
-      (not (= npoint opoint)))))
+  (cond
+   (forward-sentence-function
+    (funcall forward-sentence-function arg))
+   (t
+    (let ((opoint (point))
+          (sentence-end (sentence-end)))
+      (while (< arg 0)
+        (let ((pos (point))
+	      par-beg par-text-beg)
+	  (save-excursion
+	    (start-of-paragraph-text)
+	    ;; Start of real text in the paragraph.
+	    ;; We move back to here if we don't see a sentence-end.
+	    (setq par-text-beg (point))
+	    ;; Start of the first line of the paragraph.
+	    ;; We use this as the search limit
+	    ;; to allow sentence-end to match if it is anchored at
+	    ;; BOL and the paragraph starts indented.
+	    (beginning-of-line)
+	    (setq par-beg (point)))
+	  (if (and (re-search-backward sentence-end par-beg t)
+		   (or (< (match-end 0) pos)
+		       (re-search-backward sentence-end par-beg t)))
+	      (goto-char (match-end 0))
+	    (goto-char par-text-beg)))
+        (setq arg (1+ arg)))
+      (while (> arg 0)
+        (let ((par-end (save-excursion (end-of-paragraph-text) (point))))
+	  (if (re-search-forward sentence-end par-end t)
+	      (skip-chars-backward " \t\n")
+	    (goto-char par-end)))
+        (setq arg (1- arg)))
+      (let ((npoint (constrain-to-field nil opoint t)))
+        (not (= npoint opoint)))))))
 
 (defun count-sentences (start end)
   "Count sentences in current buffer from START to END."
@@ -532,14 +538,17 @@ repunctuate-sentences
         (remove-function isearch-filter-predicate
                          repunctuate-sentences-filter)))))
 
-
 (defun backward-sentence (&optional arg)
   "Move backward to start of sentence.
 With ARG, do it ARG times.  See `forward-sentence' for more
 information."
   (interactive "^p")
   (or arg (setq arg 1))
-  (forward-sentence (- arg)))
+  (cond
+   (forward-sentence-function
+    (funcall forward-sentence-function (- arg)))
+   (t
+    (forward-sentence (- arg)))))
 
 (defun kill-sentence (&optional arg)
   "Kill from point to end of sentence.
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 133564f6c8..4a1c10211e 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -1603,6 +1603,50 @@ treesit--defun-maybe-top-level
                  node)
       finally return node))))
 
+(defvar-local treesit-sexp-type-regexp "\\`field_declaration\\'"
+  "A regexp that matches the node type of sexp nodes.")
+
+(defun treesit-forward-sexp (&optional arg)
+  "Tree-sitter `beginning-of-defun' function.
+ARG is the same as in `beginning-of-defun'."
+  (let ((arg (or arg 1))
+        (node (treesit-node-at (point))))
+    (while (and (> arg 0)
+                (setq node (treesit-search-forward-goto
+                            node
+                            treesit-sexp-type-regexp)))
+      (setq arg (1- arg))
+      (goto-char (treesit-node-end node)))
+    (while (and (< arg 0)
+                (setq node (treesit-search-forward-goto
+                            node
+                            treesit-sexp-type-regexp t t)))
+      (setq arg (1+ arg))
+      (goto-char (treesit-node-start node)))
+    t))
+
+(defvar-local treesit-sentence-type-regexp "\\`field_declaration\\'"
+  "A regexp that matches the node type of textual nodes.")
+
+(defun treesit-forward-sentence (&optional arg)
+  "Tree-sitter `beginning-of-defun' function.
+ARG is the same as in `beginning-of-defun'."
+  (let ((arg (or arg 1))
+        (node (treesit-node-at (point))))
+    (while (and (> arg 0)
+                (setq node (treesit-search-forward-goto
+                            node
+                            treesit-sentence-type-regexp)))
+      (setq arg (1- arg))
+      (goto-char (treesit-node-end node)))
+    (while (and (< arg 0)
+                (setq node (treesit-search-forward-goto
+                            node
+                            treesit-sentence-type-regexp t t)))
+      (setq arg (1+ arg))
+      (goto-char (treesit-node-start node)))
+    t))
+
 (defun treesit-beginning-of-defun (&optional arg)
   "Tree-sitter `beginning-of-defun' function.
 ARG is the same as in `beginning-of-defun'."
@@ -1730,7 +1774,11 @@ treesit-major-mode-setup
   ;; Navigation.
   (when treesit-defun-type-regexp
     (setq-local beginning-of-defun-function #'treesit-beginning-of-defun)
-    (setq-local end-of-defun-function #'treesit-end-of-defun)))
+    (setq-local end-of-defun-function #'treesit-end-of-defun))
+  (when treesit-sentence-type-regexp
+    (setq-local forward-sentence-function #'treesit-forward-sentence))
+  (when treesit-sexp-type-regexp
+    (setq-local forward-sexp-function #'treesit-forward-sexp)))
 
 ;;; Debugging
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 54+ messages in thread

end of thread, other threads:[~2022-12-28 18:27 UTC | newest]

Thread overview: 54+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-12-12 14:33 Plug treesit.el into other emacs constructs Theodor Thornhill
2022-12-12 14:45 ` Eli Zaretskii
2022-12-13 18:17   ` Theodor Thornhill
2022-12-12 15:46 ` Stefan Monnier
2022-12-13 18:27   ` Theodor Thornhill
2022-12-13 19:37     ` Stefan Monnier
2022-12-13 19:53       ` Yuan Fu
2022-12-13 20:06         ` Perry Smith
2022-12-13 23:19         ` Stefan Monnier
2022-12-14  8:14           ` Yuan Fu
2022-12-14  8:42             ` Theodor Thornhill
2022-12-14 14:01             ` Stefan Monnier
2022-12-14 16:24               ` Theodor Thornhill
2022-12-14 17:46                 ` Stefan Monnier
2022-12-14 18:07                   ` Theodor Thornhill
2022-12-14 19:25                     ` Stefan Monnier
2022-12-14 19:35                       ` Stefan Monnier
2022-12-14 20:04                       ` Theodor Thornhill
2022-12-14 20:50                         ` Stefan Monnier
2022-12-14 21:15                           ` Theodor Thornhill
2022-12-14 21:34                             ` Stefan Monnier
2022-12-15 19:37                               ` Theodor Thornhill
2022-12-15 19:56                                 ` Stefan Monnier
2022-12-15 20:03                                   ` Theodor Thornhill
2022-12-15 20:33                                     ` Theodor Thornhill
2022-12-15 20:57                                       ` Theodor Thornhill
2022-12-24  7:00                                         ` Eli Zaretskii
2022-12-24  8:44                                           ` Yuan Fu
2022-12-24 14:01                                         ` Stefan Monnier
2022-12-24 14:15                                           ` Theodor Thornhill
2022-12-26 19:11                                             ` Theodor Thornhill
2022-12-26 22:46                                               ` Stefan Monnier
2022-12-26 22:51                                                 ` Stefan Monnier
2022-12-27 22:15                                                   ` Theodor Thornhill via Emacs development discussions.
2022-12-28  0:12                                                     ` Stefan Monnier
2022-12-28  9:26                                                       ` Theodor Thornhill via Emacs development discussions.
2022-12-28 18:01                                                         ` Stefan Monnier
2022-12-28 18:27                                                           ` Theodor Thornhill
2022-12-26 22:56                                                 ` Theodor Thornhill
2022-12-27 15:46                                   ` Lynn Winebarger
2022-12-14 23:31               ` Yuan Fu
2022-12-15  0:05                 ` Yuan Fu
2022-12-15  7:09                   ` Eli Zaretskii
2022-12-15  7:14                     ` Theodor Thornhill
2022-12-15  4:37                 ` Stefan Monnier
2022-12-15  5:59                 ` Theodor Thornhill
2022-12-15 21:23                   ` Yuan Fu
2022-12-15 21:28                     ` Theodor Thornhill
2022-12-13 20:02       ` Theodor Thornhill
2022-12-13 23:10         ` Stefan Monnier
2022-12-14 23:32   ` Stephen Leake
2022-12-16 10:02     ` Kévin Le Gouguec
2022-12-16 11:54       ` [SPAM UNSURE] " Stephen Leake
2022-12-17 15:30         ` Kévin Le Gouguec

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.