unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: "João Paulo Labegalini de Carvalho" <jaopaulolc@gmail.com>
To: emacs-devel@gnu.org
Subject: Code navigation for sh-mode with Tree-sitter
Date: Sat, 3 Dec 2022 13:23:25 -0700	[thread overview]
Message-ID: <CAGjvy2-FPGt9o=31MuOh4U3JwPpwoLu0OR+L2Oi3f4GPw_K93g@mail.gmail.com> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 1930 bytes --]

Hi all,

I am working on basic navigation for sh-mode. My idea is that in sh-mode
invoking C-M-a or C-M-e moves point to the beginning/end of the
surrounding function if the point was inside of said function or to the
beginning/end of the next/previous top-level function otherwise.

I got the functions to do so in the attached patch (I did not include the
C-M-a with a negative argument yet).

If the functions are defined and used in isolation they work as intended.
However, because `end-of-defun' calls `beginning-of-defun-raw' (which,
AFAIK, uses `beginning-of-defun-function') it causes the C-M-e to not work
when the point is not inside of a function.

What I think is happening is that, when `end-of-defun' calls
`beginning-of-defun-raw' it brings point to the beginning of a top-level
function, so the subsequent call to `end-of-defun' moves point to the start
location, does it make it seems as the point did not move.

I have also noticed that calling the private function I defined directly
via M-: brings the point to a slightly different location. For example:

<---- point before C-M-e
A {
} <--- point after M-: (sh-mode--treesit-end-of-defun)
<---- pint after C-M-e

Calling sh-mode--treesit-end-of-defun brings the point right after the
closing curly brace and with C-M-e the point is positioned at the next line.

That might be due to additional processing done inside `end-of-defun'.

So, would it be ok to override `beginning-of-defun' and `end-of-defun' and
thus have bash-ts-mode use the functions I wrote directly? If so, how could
I do that? If not, how can I fix the "miss-behavior"?

I would appreciate all feedback and suggestions.

-- 
João Paulo L. de Carvalho
Ph.D Computer Science |  IC-UNICAMP | Campinas , SP - Brazil
Postdoctoral Research Fellow | University of Alberta | Edmonton, AB - Canada
joao.carvalho@ic.unicamp.br
joao.carvalho@ualberta.ca

[-- Attachment #1.2: Type: text/html, Size: 2423 bytes --]

[-- Attachment #2: 0001-Basic-navigation-for-sh-mode.patch --]
[-- Type: text/x-patch, Size: 3759 bytes --]

From ec7e99cc93fb8f11d94e4c02c558cbfd756237a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20P=2E=20L=2E=20de=20Carvalho?=
 <jaopaulolc@gmail.com>
Date: Sat, 3 Dec 2022 12:55:27 -0700
Subject: [PATCH] Basic navigation for sh-mode

---
 lisp/progmodes/sh-script.el | 63 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 408ebfc045..5885261c01 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -1619,7 +1619,13 @@ bash-ts-mode
                   ( bracket delimiter misc-punctuation operator)))
     (setq-local treesit-font-lock-settings
                 sh-mode--treesit-settings)
-    (treesit-major-mode-setup)))
+    (setq-local treesit-defun-prefer-top-level t)
+    (setq-local treesit-defun-type-regexp "function_definition")
+    (treesit-major-mode-setup)
+    (setq-local beginning-of-defun-function
+                #'sh-mode--treesit-beginning-of-defun)
+    (setq-local end-of-defun-function
+                #'sh-mode--treesit-end-of-defun)))
 
 (advice-add 'bash-ts-mode :around #'sh--redirect-bash-ts-mode
             ;; Give it lower precedence than normal advice, so other
@@ -3364,5 +3370,60 @@ sh-mode--treesit-settings
    '((["$"]) @font-lock-misc-punctuation-face))
   "Tree-sitter font-lock settings for `sh-mode'.")
 
+\f
+;;; Tree-sitter navigation
+
+(defmacro get-fun-or-nil (node)
+  `((treesit-parent-until ,node
+                        (lambda (p)
+                          (string= (treesit-node-type p)
+                                   "function_definition"))))
+
+(defun sh-mode--treesit-beginning-of-defun (&optional arg)
+  "Tree-sitter `beginning-of-defun' function.
+ARG is the same as in `beginning-of-defun'."
+  (let ((arg (or arg 1))
+        (target-node nil)
+        (node-at-point nil)
+        (function treesit-defun-type-regexp))
+    (if (> arg 0)
+        ;; Go backward.
+        (while (> arg 0)
+          (setq node-at-point (treesit-node-at (point)))
+          (if (string= (treesit-node-type node-at-point) "function")
+              (setq node-at-point (treesit-node-parent node-at-point)))
+          (setq target-node (get-fun-or-nil node-at-point))
+          (unless target-node
+            (setq maybe-target-node (treesit-search-forward node-at-point
+                                                            function
+                                                            t))
+            (setq target-node (or (treesit-node-top-level maybe-target-node)
+                                  maybe-target-node)))
+          (when target-node
+            (goto-char (treesit-node-start target-node)))
+          (setq arg (1- arg)))
+      ;; Go forward.
+      (while (< arg 0)
+        (setq arg (1+ arg))))))
+
+(defun sh-mode--treesit-end-of-defun ()
+  "Tree-sitter `end-of-defun' function."
+  (let ((node-at-point nil)
+        (target-node nil)
+        (function treesit-defun-type-regexp))
+    (setq node-at-point (treesit-node-at (point)))
+    (setq target-node (get-fun-or-nil node-at-point))
+    (unless target-node
+      (setq target-node (treesit-search-forward node-at-point
+                                                function))
+      (when (and target-node
+                 (treesit-parent-until target-node
+                                       (lambda (p)
+                                         (string= (treesit-node-type p)
+                                                  function))))
+        (setq target-node (treesit-node-top-level target-node))))
+    (when target-node
+      (goto-char (treesit-node-end target-node)))))
+
 (provide 'sh-script)
 ;;; sh-script.el ends here
-- 
2.31.1


             reply	other threads:[~2022-12-03 20:23 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-03 20:23 João Paulo Labegalini de Carvalho [this message]
2022-12-03 21:46 ` Code navigation for sh-mode with Tree-sitter Alan Mackenzie
2022-12-05 15:24   ` João Paulo Labegalini de Carvalho
2022-12-05 20:12     ` Stefan Monnier
2022-12-05 21:29       ` Alan Mackenzie
2022-12-05 21:56         ` Stefan Monnier
2022-12-06 15:51       ` João Paulo Labegalini de Carvalho
2022-12-06 16:48         ` Stefan Monnier
2022-12-06 21:04           ` Yuan Fu
2022-12-06 21:08             ` Yuan Fu
2022-12-06 21:40               ` Alan Mackenzie
2022-12-06 21:46                 ` João Paulo Labegalini de Carvalho
2022-12-06 21:55                   ` João Paulo Labegalini de Carvalho
2022-12-06 22:35                     ` Stefan Monnier
2022-12-06 22:41                       ` João Paulo Labegalini de Carvalho
2022-12-06 22:57                       ` Stefan Monnier
2022-12-06 23:43                         ` João Paulo Labegalini de Carvalho
2022-12-06 23:50                           ` Stefan Monnier
2022-12-07  1:12                             ` João Paulo Labegalini de Carvalho
2022-12-07 17:20                               ` João Paulo Labegalini de Carvalho
2022-12-10  4:58                                 ` Yuan Fu
2022-12-13  4:55                                 ` Yuan Fu
2022-12-13 16:00                                   ` João Paulo Labegalini de Carvalho
2022-12-13  5:20                                 ` New defun navigation for tree-sitter (Was: Code navigation for sh-mode with Tree-sitter) Yuan Fu
2022-12-13 16:11                                   ` João Paulo Labegalini de Carvalho
2022-12-13 16:38                                     ` Eli Zaretskii
2022-12-13 18:03                                       ` João Paulo Labegalini de Carvalho
2022-12-13 18:07                                     ` Yuan Fu
2022-12-13 18:48                                       ` João Paulo Labegalini de Carvalho
2022-12-13 18:56                                         ` Yuan Fu
2022-12-13 19:46                                           ` João Paulo Labegalini de Carvalho
2022-12-16  1:49                                             ` Yuan Fu
2022-12-16 16:24                                               ` João Paulo Labegalini de Carvalho
2022-12-17 23:32                                                 ` Yuan Fu
2022-12-07  0:41                 ` Code navigation for sh-mode with Tree-sitter Yuan Fu

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='CAGjvy2-FPGt9o=31MuOh4U3JwPpwoLu0OR+L2Oi3f4GPw_K93g@mail.gmail.com' \
    --to=jaopaulolc@gmail.com \
    --cc=emacs-devel@gnu.org \
    /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).