all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Juri Linkov <juri@linkov.net>
To: Yuan Fu <casouri@gmail.com>
Cc: 68824@debbugs.gnu.org
Subject: bug#68824: treesitter support for outline-minor-mode
Date: Sun, 04 Feb 2024 19:15:20 +0200	[thread overview]
Message-ID: <86r0hsxiaf.fsf@mail.linkov.net> (raw)
In-Reply-To: <3D859057-3DE9-470A-80A4-E0F734A0A622@gmail.com> (Yuan Fu's message of "Thu, 1 Feb 2024 16:34:47 -0800")

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

> Instead of using treesit-search-forward, can you use
> treesit-beginning-of-thing or treesit--navigate-thing to do what you want?
> They handle the “child before parent” problem for you, and handles some
> other edge cases.

Thanks, I tried and it works.

Probably treesit--navigate-thing is not internal anymore.
Also the patch below uses treesit--thing-at that doesn't look
internal either.  So maybe two dashes could be removed from names.

Also a remaining question: why treesit-parent-until simply calls
‘(funcall pred node)’ instead of supporting the standard format
that includes a regexp by using treesit-node-match-p?

This causes such an inconvenience that for treesit-outline-level
that uses treesit-parent-until there is a need to wrap a regexp
in a lambda such as for html-ts-mode below.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: treesit-outline-search.patch --]
[-- Type: text/x-diff, Size: 5106 bytes --]

diff --git a/lisp/treesit.el b/lisp/treesit.el
index fab2ddd88e6..921e61b2160 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2854,6 +2854,53 @@ treesit-simple-imenu
                     index))))
             treesit-simple-imenu-settings)))
 
+;;; Outline minor mode
+
+(defvar-local treesit-outline-predicate nil
+  "Predicate used to find outline headings in the syntax tree.
+Intended to be set by a major mode.  When nil, the predicate
+is constructed from the value of `treesit-simple-imenu-settings'
+when a major mode sets it.")
+
+(defun treesit-outline-search (&optional bound move backward looking-at)
+  "Search for the next outline heading in the syntax tree.
+See the descriptions of arguments in `outline-search-function'."
+  (if looking-at
+      (when-let* ((node (or (treesit--thing-at (pos-eol) treesit-outline-predicate)
+                            (treesit--thing-at (pos-bol) treesit-outline-predicate)))
+                  (start (treesit-node-start node)))
+        (eq (pos-bol) (save-excursion (goto-char start) (pos-bol))))
+
+    (let* ((pos
+            ;; When function wants to find the current outline, point
+            ;; is at the beginning of the current line.  When it wants
+            ;; to find the next outline, point is at the second column.
+            (if (eq (point) (pos-bol))
+                (if (bobp) (point) (1- (point)))
+              (pos-eol)))
+           (found (treesit--navigate-thing pos (if backward -1 1) 'beg
+                                           treesit-outline-predicate)))
+      (if found
+          (if (or (not bound) (if backward (>= found bound) (<= found bound)))
+              (progn
+                (goto-char found)
+                (goto-char (pos-bol))
+                (set-match-data (list (point) (pos-eol)))
+                t)
+            (when move (goto-char bound))
+            nil)
+        (when move (goto-char (or bound (if backward (point-min) (point-max)))))
+        nil))))
+
+(defun treesit-outline-level ()
+  "Return the depth of the current outline heading."
+  (let* ((node (treesit-node-at (point)))
+         (level (if (treesit-node-match-p node treesit-outline-predicate t)
+                    1 0)))
+    (while (setq node (treesit-parent-until node treesit-outline-predicate))
+      (setq level (1+ level)))
+    (if (zerop level) 1 level)))
+
 ;;; Activating tree-sitter
 
 (defun treesit-ready-p (language &optional quiet)
@@ -2984,6 +3031,23 @@ treesit-major-mode-setup
     (setq-local imenu-create-index-function
                 #'treesit-simple-imenu))
 
+  ;; Outline minor mode.
+  (when (and (or treesit-outline-predicate treesit-simple-imenu-settings)
+             (not (seq-some #'local-variable-p
+                            '(outline-search-function
+                              outline-regexp outline-level))))
+    (unless treesit-outline-predicate
+      (setq treesit-outline-predicate
+            (lambda (node)
+              (seq-some
+               (lambda (setting)
+                 (and (string-match-p (nth 1 setting) (treesit-node-type node))
+                      (or (null (nth 2 setting))
+                          (funcall (nth 2 setting) node))))
+               treesit-simple-imenu-settings))))
+    (setq-local outline-search-function #'treesit-outline-search
+                outline-level #'treesit-outline-level))
+
   ;; Remove existing local parsers.
   (dolist (ov (overlays-in (point-min) (point-max)))
     (when-let ((parser (overlay-get ov 'treesit-parser)))
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index e5835bdb62d..ac0682a2dc7 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -1259,6 +1259,15 @@ c-ts-base-mode
                                 eos)
                    c-ts-mode--defun-for-class-in-imenu-p nil))))
 
+  (setq-local treesit-outline-predicate
+              (lambda (node)
+                (and (treesit-node-match-p
+                      node "\\`function_declarator\\'" t)
+                     (when-let ((parent (treesit-node-parent node)))
+                       (treesit-node-match-p
+                        parent
+                        "\\`function_definition\\'" t)))))
+
   (setq-local treesit-font-lock-feature-list
               c-ts-mode--feature-list))
 
diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el
index 301f3e8791c..71fed15f2fc 100644
--- a/lisp/textmodes/html-ts-mode.el
+++ b/lisp/textmodes/html-ts-mode.el
@@ -121,6 +121,16 @@ html-ts-mode
   ;; Imenu.
   (setq-local treesit-simple-imenu-settings
               '(("Element" "\\`tag_name\\'" nil nil)))
+
+  ;; Outline minor mode.
+  (setq-local treesit-outline-predicate
+              (lambda (node) (treesit-node-match-p
+                              node "\\`element\\'" t)))
+  ;; Restore default value for `treesit-outline-search'.
+  (kill-local-variable 'outline-regexp)
+  (kill-local-variable 'outline-heading-end-regexp)
+  (kill-local-variable 'outline-level)
+
   (treesit-major-mode-setup))
 
 (if (treesit-ready-p 'html)

  parent reply	other threads:[~2024-02-04 17:15 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-30 17:37 bug#68824: treesitter support for outline-minor-mode Juri Linkov
2024-01-30 18:46 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-01-31  7:20   ` Juri Linkov
2024-01-30 19:21 ` Eli Zaretskii
2024-01-31  7:32   ` Juri Linkov
2024-02-01 17:12     ` Juri Linkov
2024-02-02  0:34       ` Yuan Fu
2024-02-02  7:05         ` Eli Zaretskii
2024-02-02  7:27           ` Yuan Fu
2024-02-02  7:34             ` Eli Zaretskii
2024-02-02  7:53         ` Juri Linkov
2024-02-04 17:15         ` Juri Linkov [this message]
2024-02-05  4:34           ` Yuan Fu
2024-02-05  7:22             ` Juri Linkov
2024-02-06  7:21               ` Yuan Fu
2024-02-08  7:40     ` Juri Linkov
2024-02-08  8:30       ` Eli Zaretskii
2024-02-08 17:20         ` Juri Linkov
2024-02-08 18:56           ` Eli Zaretskii
2024-02-09  7:07             ` Juri Linkov
2024-02-09 19:25               ` Eli Zaretskii
2024-02-10 17:29                 ` Juri Linkov
2024-02-10 17:57                   ` Eli Zaretskii
2024-02-11 17:34                     ` Juri Linkov
2024-02-11 19:22                       ` Eli Zaretskii
2024-02-12  1:07                         ` Yuan Fu
2024-02-12 12:59                           ` Eli Zaretskii
2024-02-12 18:16                             ` Juri Linkov
2024-02-13 17:02                               ` Juri Linkov

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

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86r0hsxiaf.fsf@mail.linkov.net \
    --to=juri@linkov.net \
    --cc=68824@debbugs.gnu.org \
    --cc=casouri@gmail.com \
    /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 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.