unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#68824: treesitter support for outline-minor-mode
@ 2024-01-30 17:37 Juri Linkov
  2024-01-30 18:46 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-01-30 19:21 ` Eli Zaretskii
  0 siblings, 2 replies; 29+ messages in thread
From: Juri Linkov @ 2024-01-30 17:37 UTC (permalink / raw)
  To: 68824; +Cc: Yuan Fu

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

Tags: patch

As discussed on https://lists.gnu.org/archive/html/emacs-devel/2024-01/msg00916.html
here is the patch that adds the support for outline-minor-mode to treesit.el.
It has been tested on c-ts-mode, dockerfile-ts-mode, elixir-ts-mode, heex-ts-mode,
java-ts-mode, js-ts-mode, typescript-ts-mode, css-ts-mode, html-ts-mode, toml-ts-mode.


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

diff --git a/etc/NEWS b/etc/NEWS
index a9d6eb6789d..6ceaa6b7b6c 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -130,6 +130,12 @@ the signature) the automatically inferred function type as well.
 This user option controls outline visibility in the output buffer of
 'describe-bindings' when 'describe-bindings-outline' is non-nil.
 
+** Outline Mode
+
+*** 'outline-minor-mode' is supported in tree-sitter major modes.
+It can be used in all tree-sitter major modes that set either the
+variable 'treesit-simple-imenu-settings' or 'treesit-outline-predicate'.
+
 ** X selection requests are now handled much faster and asynchronously.
 This means it should be less necessary to disable the likes of
 'select-active-regions' when Emacs is running over a slow network
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 96222ed81cb..13319bb0483 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2836,6 +2836,99 @@ treesit-simple-imenu
                     index))))
             treesit-simple-imenu-settings)))
 
+;;; Outline minor mode
+
+(defvar-local treesit-outline-predicate nil
+  "Predicate used to find outline headings in a sparse 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.")
+
+(defvar-local treesit-outline-levels nil
+  "Holds a cached structure that corresponds to the outline tree.
+It's a list of (MARKER . LEVEL) where MARKER is a position of the
+beginning of the outline heading, and LEVEL is its depth in the
+outline tree.")
+
+(defun treesit-outline-levels (node level)
+  "Given a sparse tree, return a list for `treesit-outline-levels'."
+  (let* ((ts-node (car node))
+         (children (cdr node))
+         (subtrees (mapcan (lambda (node)
+                             (treesit-outline-levels node (1+ level)))
+                           children))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (save-excursion
+                                 (goto-char (treesit-node-start ts-node))
+                                 (search-forward (or (treesit-defun-name ts-node) ""))
+                                 (pos-bol))))))
+    (cond
+     ((null ts-node)
+      subtrees)
+     (subtrees
+      (cons (cons marker level) subtrees))
+     (t
+      (list (cons marker level))))))
+
+(defun treesit-outline-prepare ()
+  "Prepare `treesit-outline-levels' to be used by `treesit-outline-search'.
+Build the internal structure based either on the value
+`treesit-outline-predicate' that should be a predicate for
+`treesit-induce-sparse-tree', or use the existing value of
+`treesit-simple-imenu-settings' where outline headings are
+on the same lines as the imenu items."
+  (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 treesit-outline-levels
+        (treesit-outline-levels
+         (treesit-induce-sparse-tree
+          (treesit-buffer-root-node)
+          treesit-outline-predicate)
+         0)))
+
+(defun treesit-outline-search (&optional bound move backward looking-at)
+  "Search for the next outline heading.
+See the descriptions of arguments in `outline-search-function'.
+Uses the value of `treesit-outline-levels' prepared by
+`treesit-outline-prepare'."
+  (unless treesit-outline-levels
+    (treesit-outline-prepare))
+
+  (let ((positions (mapcar #'car treesit-outline-levels)))
+    (if looking-at
+        (when (member (point-marker) positions)
+          (set-match-data (list (pos-bol) (pos-eol)))
+          t)
+
+      (let ((found (seq-find (lambda (p) (>= p (point)))
+                             (if backward (nreverse positions) positions))))
+        (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.
+Uses the value of `treesit-outline-levels'."
+  (or (alist-get (point) treesit-outline-levels nil nil
+                 (lambda (m k) (eq (marker-position m) k)))
+      1))
+
 ;;; Activating tree-sitter
 
 (defun treesit-ready-p (language &optional quiet)
@@ -2966,6 +3059,14 @@ 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))))
+    (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/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el
index 7b53a44deb2..23e2afbaaca 100644
--- a/lisp/progmodes/heex-ts-mode.el
+++ b/lisp/progmodes/heex-ts-mode.el
@@ -166,6 +166,12 @@ heex-ts-mode
                   ("Slot" "\\`slot\\'" nil nil)
                   ("Tag" "\\`tag\\'" nil nil)))
 
+    ;; Outline minor mode
+    ;; Restore default value for `treesit-outline-search'.
+    (kill-local-variable 'outline-regexp)
+    (kill-local-variable 'outline-heading-end-regexp)
+    (kill-local-variable 'outline-level)
+
     (setq-local treesit-font-lock-settings heex-ts--font-lock-settings)
 
     (setq-local treesit-simple-indent-rules heex-ts--indent-rules)
diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el
index 301f3e8791c..f157d2d6949 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.
+  ;; Override default predicate to use "element" for outline headings
+  ;; instead of "tag_name" from `treesit-simple-imenu-settings'.
+  (setq-local treesit-outline-predicate "\\`element\\'")
+  ;; 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)

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

* bug#68824: treesitter support for outline-minor-mode
  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
  1 sibling, 1 reply; 29+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-01-30 18:46 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Yuan Fu, 68824

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

Juri Linkov <juri@linkov.net> writes:

> Tags: patch
>
> As discussed on https://lists.gnu.org/archive/html/emacs-devel/2024-01/msg00916.html
> here is the patch that adds the support for outline-minor-mode to treesit.el.
> It has been tested on c-ts-mode, dockerfile-ts-mode, elixir-ts-mode, heex-ts-mode,
> java-ts-mode, js-ts-mode, typescript-ts-mode, css-ts-mode, html-ts-mode, toml-ts-mode.

Thanks for working on this. It works well with lua-ts-mode too.
If you want you can add the diff to your patch.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Use-built-in-outline-support-in-lua-ts-mode.patch --]
[-- Type: text/x-patch, Size: 1114 bytes --]

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 05a3ff6d7c6..dc2a8fcec1e 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -774,7 +774,7 @@ lua-ts-mode
                                       "vararg_expression"))))
                    (text "comment"))))
 
-    ;; Imenu.
+    ;; Imenu/Outline.
     (setq-local treesit-simple-imenu-settings
                 `(("Requires"
                    "\\`function_call\\'"
@@ -789,15 +789,5 @@ lua-ts-mode
     ;; Which-function.
     (setq-local which-func-functions (treesit-defun-at-point))
 
-    ;; Outline.
-    (setq-local outline-regexp
-                (rx (seq (0+ space)
-                         (or (seq "--[[" (0+ space) eol)
-                             (seq symbol-start
-                                  (or "do" "for" "if" "repeat" "while"
-                                      (seq (? (seq "local" (1+ space)))
-                                           "function"))
-                                  symbol-end)))))
-
     ;; Align.
     (setq-local align-indent-before-aligning t)

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

* bug#68824: treesitter support for outline-minor-mode
  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-30 19:21 ` Eli Zaretskii
  2024-01-31  7:32   ` Juri Linkov
  1 sibling, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-01-30 19:21 UTC (permalink / raw)
  To: Juri Linkov; +Cc: casouri, 68824

> Cc: Yuan Fu <casouri@gmail.com>
> From: Juri Linkov <juri@linkov.net>
> Date: Tue, 30 Jan 2024 19:37:20 +0200
> 
> As discussed on https://lists.gnu.org/archive/html/emacs-devel/2024-01/msg00916.html
> here is the patch that adds the support for outline-minor-mode to treesit.el.
> It has been tested on c-ts-mode, dockerfile-ts-mode, elixir-ts-mode, heex-ts-mode,
> java-ts-mode, js-ts-mode, typescript-ts-mode, css-ts-mode, html-ts-mode, toml-ts-mode.

Thanks, but shouldn't this minor mode be documented more prominently
in the Emacs user manual, now that there are so many major modes that
support it?  And since major modes that want to support it have to set
up its support, I would say the ELisp manual should document that
setup.

IOW, it makes little sense to make this minor mode more and more
prolific in Emacs, and still have almost nothing about it in our
manuals.

> +*** 'outline-minor-mode' is supported in tree-sitter major modes.
> +It can be used in all tree-sitter major modes that set either the
> +variable 'treesit-simple-imenu-settings' or 'treesit-outline-predicate'.

This terse description is okay, if the ELisp manual describes these
setups in more detail; alone it is insufficient.

> +(defvar-local treesit-outline-predicate nil
> +  "Predicate used to find outline headings in a sparse tree.

How do "sparse trees" enter the scene here?  AFAICS, "sparse trees" is
mentioned only once in the entire chapter dedicated to Tree Sitter
support in the ELisp manual.  Is it important to mention here the fact
that the tree is sparse?

> diff --git a/lisp/progmodes/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el
> index 7b53a44deb2..23e2afbaaca 100644
> --- a/lisp/progmodes/heex-ts-mode.el
> +++ b/lisp/progmodes/heex-ts-mode.el
> @@ -166,6 +166,12 @@ heex-ts-mode
>                    ("Slot" "\\`slot\\'" nil nil)
>                    ("Tag" "\\`tag\\'" nil nil)))
>  
> +    ;; Outline minor mode
> +    ;; Restore default value for `treesit-outline-search'.
> +    (kill-local-variable 'outline-regexp)
> +    (kill-local-variable 'outline-heading-end-regexp)
> +    (kill-local-variable 'outline-level)

This mode seems to do something to support outline-minor-mode that is
not mentioned in the NEWS entry, and neither is treesit-outline-search.





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

* bug#68824: treesitter support for outline-minor-mode
  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
  0 siblings, 0 replies; 29+ messages in thread
From: Juri Linkov @ 2024-01-31  7:20 UTC (permalink / raw)
  To: john muhl; +Cc: Yuan Fu, 68824

>> As discussed on https://lists.gnu.org/archive/html/emacs-devel/2024-01/msg00916.html
>> here is the patch that adds the support for outline-minor-mode to treesit.el.
>> It has been tested on c-ts-mode, dockerfile-ts-mode, elixir-ts-mode, heex-ts-mode,
>> java-ts-mode, js-ts-mode, typescript-ts-mode, css-ts-mode, html-ts-mode, toml-ts-mode.
>
> Thanks for working on this. It works well with lua-ts-mode too.
> If you want you can add the diff to your patch.

Thanks, will add lua-ts-mode part to the final patch.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-01-30 19:21 ` Eli Zaretskii
@ 2024-01-31  7:32   ` Juri Linkov
  2024-02-01 17:12     ` Juri Linkov
  2024-02-08  7:40     ` Juri Linkov
  0 siblings, 2 replies; 29+ messages in thread
From: Juri Linkov @ 2024-01-31  7:32 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: casouri, 68824

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

>> As discussed on https://lists.gnu.org/archive/html/emacs-devel/2024-01/msg00916.html
>> here is the patch that adds the support for outline-minor-mode to treesit.el.
>> It has been tested on c-ts-mode, dockerfile-ts-mode, elixir-ts-mode, heex-ts-mode,
>> java-ts-mode, js-ts-mode, typescript-ts-mode, css-ts-mode, html-ts-mode, toml-ts-mode.
>
> Thanks, but shouldn't this minor mode be documented more prominently
> in the Emacs user manual, now that there are so many major modes that
> support it?  And since major modes that want to support it have to set
> up its support, I would say the ELisp manual should document that
> setup.
>
> IOW, it makes little sense to make this minor mode more and more
> prolific in Emacs, and still have almost nothing about it in our
> manuals.

I agree.  Also could add corresponding menu item to the global menu-bar,
or at least to menus of major modes deriving from prog-mode/text-mode.

>> +(defvar-local treesit-outline-predicate nil
>> +  "Predicate used to find outline headings in a sparse tree.
>
> How do "sparse trees" enter the scene here?  AFAICS, "sparse trees" is
> mentioned only once in the entire chapter dedicated to Tree Sitter
> support in the ELisp manual.  Is it important to mention here the fact
> that the tree is sparse?

Actually I realized now there is no need to use sparse trees here.
Below is a new implementation that directly traverses the syntax tree.
This implementation is much better than sparse trees because
sparse trees can be created only once when the file is visited,
but when the buffer is modified it takes too much time
to regenerate sparse trees completely after every change.
So a new implementation below uses the search on the syntax tree
that is much faster to do after every buffer modification.

>> diff --git a/lisp/progmodes/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el
>> +    ;; Outline minor mode
>> +    ;; Restore default value for `treesit-outline-search'.
>> +    (kill-local-variable 'outline-regexp)
>> +    (kill-local-variable 'outline-heading-end-regexp)
>> +    (kill-local-variable 'outline-level)
>
> This mode seems to do something to support outline-minor-mode that is
> not mentioned in the NEWS entry, and neither is treesit-outline-search.

This just disables outline settings inherited from html-mode.
I will add more explanations.

Here is a new patch with a shorter implementation.
And I will send a complete patch with documentation changes later.


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

diff --git a/lisp/treesit.el b/lisp/treesit.el
index 96222ed81cb..72c994c257c 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2836,6 +2836,48 @@ 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
+      (treesit-parent-until (treesit-node-at (pos-bol)) treesit-outline-predicate)
+    (let* ((current (treesit-node-at (pos-bol)))
+           (current (or (treesit-parent-until current treesit-outline-predicate)
+                        current))
+           (node (treesit-search-forward
+                   current treesit-outline-predicate backward))
+           (found (when node (treesit-node-start node))))
+      (if found
+          (if (or (not bound) (if backward (>= found bound) (<= found bound)))
+              (progn
+                (goto-char found)
+                (search-forward (or (treesit-defun-name node) ""))
+                (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 0))
+    (while node
+      (when (funcall treesit-outline-predicate node)
+        (setq level (1+ level)))
+      (setq node (treesit-node-parent node)))
+    level))
+
 ;;; Activating tree-sitter
 
 (defun treesit-ready-p (language &optional quiet)
@@ -2966,6 +3008,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)))

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

* bug#68824: treesitter support for outline-minor-mode
  2024-01-31  7:32   ` Juri Linkov
@ 2024-02-01 17:12     ` Juri Linkov
  2024-02-02  0:34       ` Yuan Fu
  2024-02-08  7:40     ` Juri Linkov
  1 sibling, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-01 17:12 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 68824

Yuan, do you think it makes sense to add a new optional arg to
'treesit-search-forward' that will define the traversal order?

The problem is that to search the next outline heading in
outline-minor-mode, there is a need to visit headings in
the same order as they appear in the buffer
where a parent is located before its children:

* Parent
** Child 1
** Child 2

Currently 'treesit-search-forward' returns "Child 1" before "Parent".

This means that 'treesit-search-forward' should return the
topmost matched parent instead of starting to traverse children
from the deepest leaf.

IOW, it's necessary to traverse nodes in this order:

                  o
                  |
                  о---------------------11
                  |                     |
         o--------S----------3          12
         |        |          |
    o--o-+--o  1--+--2    4--+-----7
    |  |                  |        |
    o  o                +-+-+   +--+--+
                        |   |   |  |  |
                        5   6   8  9  10

Please note there is no 11 or 12 at the parent of S node because
it should traverse only the remaining siblings of the parent.
This is because remaining parent siblings have treesit-node-start
positions greater than treesit-node-start of S node.

> +(defun treesit-outline-search (&optional bound move backward looking-at)
> +  (let* ((current (treesit-node-at (pos-bol)))
> +         (current (or (treesit-parent-until current treesit-outline-predicate)
> +                      current))
> +         (node (treesit-search-forward
> +                 current treesit-outline-predicate backward))





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-01 17:12     ` Juri Linkov
@ 2024-02-02  0:34       ` Yuan Fu
  2024-02-02  7:05         ` Eli Zaretskii
                           ` (2 more replies)
  0 siblings, 3 replies; 29+ messages in thread
From: Yuan Fu @ 2024-02-02  0:34 UTC (permalink / raw)
  To: Juri Linkov; +Cc: 68824



> On Feb 1, 2024, at 9:12 AM, Juri Linkov <juri@linkov.net> wrote:
> 
> Yuan, do you think it makes sense to add a new optional arg to
> 'treesit-search-forward' that will define the traversal order?
> 
> The problem is that to search the next outline heading in
> outline-minor-mode, there is a need to visit headings in
> the same order as they appear in the buffer
> where a parent is located before its children:
> 
> * Parent
> ** Child 1
> ** Child 2
> 
> Currently 'treesit-search-forward' returns "Child 1" before "Parent".
> 
> This means that 'treesit-search-forward' should return the
> topmost matched parent instead of starting to traverse children
> from the deepest leaf.
> 
> IOW, it's necessary to traverse nodes in this order:
> 
>                  o
>                  |
>                  о---------------------11
>                  |                     |
>         o--------S----------3          12
>         |        |          |
>    o--o-+--o  1--+--2    4--+-----7
>    |  |                  |        |
>    o  o                +-+-+   +--+--+
>                        |   |   |  |  |
>                        5   6   8  9  10
> 
> Please note there is no 11 or 12 at the parent of S node because
> it should traverse only the remaining siblings of the parent.
> This is because remaining parent siblings have treesit-node-start
> positions greater than treesit-node-start of S node.
> 
>> +(defun treesit-outline-search (&optional bound move backward looking-at)
>> +  (let* ((current (treesit-node-at (pos-bol)))
>> +         (current (or (treesit-parent-until current treesit-outline-predicate)
>> +                      current))
>> +         (node (treesit-search-forward
>> +                 current treesit-outline-predicate backward))

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.

Yuan




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

* bug#68824: treesitter support for outline-minor-mode
  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:53         ` Juri Linkov
  2024-02-04 17:15         ` Juri Linkov
  2 siblings, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-02  7:05 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 68824, juri

> Cc: 68824@debbugs.gnu.org
> From: Yuan Fu <casouri@gmail.com>
> Date: Thu, 1 Feb 2024 16:34:47 -0800
> 
> 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.

If the above helps Juri accomplish his goal, I think we should reflect
that somehow in the ELisp manual, at least.  I see that
treesit-beginning-of-thing is not even documented in the ELisp manual,
and treesit--navigate-thing is an internal function that is not
supposed to be called from outside of treesit.el.

Thanks.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-02  7:05         ` Eli Zaretskii
@ 2024-02-02  7:27           ` Yuan Fu
  2024-02-02  7:34             ` Eli Zaretskii
  0 siblings, 1 reply; 29+ messages in thread
From: Yuan Fu @ 2024-02-02  7:27 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 68824, Juri Linkov



> On Feb 1, 2024, at 11:05 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> Cc: 68824@debbugs.gnu.org
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Thu, 1 Feb 2024 16:34:47 -0800
>> 
>> 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.
> 
> If the above helps Juri accomplish his goal, I think we should reflect
> that somehow in the ELisp manual, at least.  I see that
> treesit-beginning-of-thing is not even documented in the ELisp manual,
> and treesit--navigate-thing is an internal function that is not
> supposed to be called from outside of treesit.el.
> 
> Thanks.

The whole “things” feature [1] is not documented right now, since I was experimenting with them. If we think it’s time we can make the functions public and document them. Personally I think we can wait for a bit longer.

[1] Includes
- treesit-thing-settings
- treesit-thing-definition
- treesit-beginning/end-of-thing
- treesit--things-around
- treesit--thing-sibling/prev/next
- treesit--thing-at
- treesit--navigate-thing

Plus you can pass thing definitions to search functions like treesit-search-forward.

Yuan




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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-02  7:27           ` Yuan Fu
@ 2024-02-02  7:34             ` Eli Zaretskii
  0 siblings, 0 replies; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-02  7:34 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 68824, juri

> From: Yuan Fu <casouri@gmail.com>
> Date: Thu, 1 Feb 2024 23:27:50 -0800
> Cc: Juri Linkov <juri@linkov.net>,
>  68824@debbugs.gnu.org
> 
> 
> 
> > On Feb 1, 2024, at 11:05 PM, Eli Zaretskii <eliz@gnu.org> wrote:
> > 
> >> Cc: 68824@debbugs.gnu.org
> >> From: Yuan Fu <casouri@gmail.com>
> >> Date: Thu, 1 Feb 2024 16:34:47 -0800
> >> 
> >> 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.
> > 
> > If the above helps Juri accomplish his goal, I think we should reflect
> > that somehow in the ELisp manual, at least.  I see that
> > treesit-beginning-of-thing is not even documented in the ELisp manual,
> > and treesit--navigate-thing is an internal function that is not
> > supposed to be called from outside of treesit.el.
> > 
> > Thanks.
> 
> The whole “things” feature [1] is not documented right now, since I was experimenting with them. If we think it’s time we can make the functions public and document them. Personally I think we can wait for a bit longer.

Doesn't the fact that you suggested to Juri to use that tell us it's
time to make these facilities public?





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-02  0:34       ` Yuan Fu
  2024-02-02  7:05         ` Eli Zaretskii
@ 2024-02-02  7:53         ` Juri Linkov
  2024-02-04 17:15         ` Juri Linkov
  2 siblings, 0 replies; 29+ messages in thread
From: Juri Linkov @ 2024-02-02  7:53 UTC (permalink / raw)
  To: Yuan Fu; +Cc: Eli Zaretskii, 68824

> 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.

I don't understand yet how this would change the traversal order
from post-order to pre-order.  But I will try to use
treesit-beginning-of-thing and treesit--navigate-thing
Thanks for suggestion.

Eli, currently I don't need documentation
since I will check source code anyway.
But I agree this should documented at some time.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-02  0:34       ` Yuan Fu
  2024-02-02  7:05         ` Eli Zaretskii
  2024-02-02  7:53         ` Juri Linkov
@ 2024-02-04 17:15         ` Juri Linkov
  2024-02-05  4:34           ` Yuan Fu
  2 siblings, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-04 17:15 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 68824

[-- 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)

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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-04 17:15         ` Juri Linkov
@ 2024-02-05  4:34           ` Yuan Fu
  2024-02-05  7:22             ` Juri Linkov
  0 siblings, 1 reply; 29+ messages in thread
From: Yuan Fu @ 2024-02-05  4:34 UTC (permalink / raw)
  To: Juri Linkov; +Cc: 68824



> On Feb 4, 2024, at 9:15 AM, Juri Linkov <juri@linkov.net> wrote:
> 
>> 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.

Yeah, it’s more of a “Emacs internal” than “treesit.el internal”. I wanted to give myself some freedom to change the api, and they’ve indeed went through big changes since created.

I can covert them to single dash.

> 
> 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.

Nothing other than my oversight :-) I pushed a change to master that fixes this.

As for the patch, I think treesit-outline-predicate could be set to a named function rather than a lambda generated on the fly, but I don’t have strong opinions about it (and maybe you have some reason to use a lambda?) Otherwise LGTM.

Yuan




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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-05  4:34           ` Yuan Fu
@ 2024-02-05  7:22             ` Juri Linkov
  2024-02-06  7:21               ` Yuan Fu
  0 siblings, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-05  7:22 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 68824

>> 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.
>
> Nothing other than my oversight :-)
> I pushed a change to master that fixes this.

Thanks.  It seems you forgot to replace 'funcall'
in 'treesit-parent-while' as well?  ;-)

> As for the patch, I think treesit-outline-predicate could be set to
> a named function rather than a lambda generated on the fly, but
> I don’t have strong opinions about it (and maybe you have some reason
> to use a lambda?) Otherwise LGTM.

I completely agree, so will move lambdas to the named functions.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-05  7:22             ` Juri Linkov
@ 2024-02-06  7:21               ` Yuan Fu
  0 siblings, 0 replies; 29+ messages in thread
From: Yuan Fu @ 2024-02-06  7:21 UTC (permalink / raw)
  To: Juri Linkov; +Cc: 68824



> On Feb 4, 2024, at 11:22 PM, Juri Linkov <juri@linkov.net> wrote:
> 
>>> 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.
>> 
>> Nothing other than my oversight :-)
>> I pushed a change to master that fixes this.
> 
> Thanks.  It seems you forgot to replace 'funcall'
> in 'treesit-parent-while' as well?  ;-)

Ah, yes. For some reason I thought it calls treesit-parent-until. Fixed it on master.

Yuan




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

* bug#68824: treesitter support for outline-minor-mode
  2024-01-31  7:32   ` Juri Linkov
  2024-02-01 17:12     ` Juri Linkov
@ 2024-02-08  7:40     ` Juri Linkov
  2024-02-08  8:30       ` Eli Zaretskii
  1 sibling, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-08  7:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: casouri, 68824

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

> Here is a new patch with a shorter implementation.
> And I will send a complete patch with documentation changes later.

So here is a complete patch with documentation changes:


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

diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 338bf014208..48a70c171dd 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -1097,6 +1097,11 @@ Outline Format
 chapters.  This works as long as no other command starts with
 @samp{@@chap}.
 
+@vindex outline-search-function
+  Instead of setting the variable @code{outline-regexp}, you can set
+the variable @code{outline-search-function} to a function that
+matches the current heading and searches for the next one.
+
 @vindex outline-level
   You can explicitly specify a rule for calculating the level of a
 heading line by setting the variable @code{outline-level}.  The value
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
index ac11f88ae4d..5911a11a074 100644
--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi
@@ -1895,6 +1895,10 @@ Tree-sitter Major Modes
 @item
 If @code{treesit-simple-imenu-settings} (@pxref{Imenu}) is
 non-@code{nil}, it sets up Imenu.
+
+@item
+If @code{treesit-outline-predicate} is non-@code{nil}, it sets up
+Outline Minor Mode (@pxref{Outline Minor Mode,,, emacs, The GNU Emacs Manual}).
 @end itemize
 
 @c TODO: Add treesit-thing-settings stuff once we finalize it.
diff --git a/etc/NEWS b/etc/NEWS
index f980d612a57..31415617c73 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -130,6 +130,12 @@ the signature) the automatically inferred function type as well.
 This user option controls outline visibility in the output buffer of
 'describe-bindings' when 'describe-bindings-outline' is non-nil.
 
+** Outline Mode
+
+*** 'outline-minor-mode' is supported in tree-sitter major modes.
+It can be used in all tree-sitter major modes that set either the
+variable 'treesit-simple-imenu-settings' or 'treesit-outline-predicate'.
+
 ** X selection requests are now handled much faster and asynchronously.
 This means it should be less necessary to disable the likes of
 'select-active-regions' when Emacs is running over a slow network
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 6a485ae591a..8b20c3eb0f6 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2860,6 +2860,68 @@ 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-predicate-from-imenu (node)
+  "Return an outline searching predicate created from Imenu.
+Return the value suitable to set `treesit-outline-predicate'.
+Create this predicate from the value `treesit-simple-imenu-settings'
+that major modes set to find Imenu entries.  The assumption here
+is that the positions of Imenu entries most of the time coincide
+with the lines of outline headings.  When this assumption fails,
+you can directly set a proper value to `treesit-outline-predicate'."
+  (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))
+
+(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)
@@ -2990,6 +3052,17 @@ 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
+            #'treesit-outline-predicate-from-imenu))
+    (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..c4b48f03d12 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -922,6 +922,17 @@ c-ts-mode--defun-name
         name)))
    t))
 
+;;; Outline minor mode
+
+(defun c-ts-mode--outline-predicate (node)
+  "Match outlines on lines with function names."
+  (and (treesit-node-match-p
+        node "\\`function_declarator\\'" t)
+       (when-let ((parent (treesit-node-parent node)))
+         (treesit-node-match-p
+          parent
+          "\\`function_definition\\'" t))))
+
 ;;; Defun navigation
 
 (defun c-ts-mode--defun-valid-p (node)
@@ -1259,6 +1270,10 @@ c-ts-base-mode
                                 eos)
                    c-ts-mode--defun-for-class-in-imenu-p nil))))
 
+  ;; Outline minor mode
+  (setq-local treesit-outline-predicate
+              #'c-ts-mode--outline-predicate)
+
   (setq-local treesit-font-lock-feature-list
               c-ts-mode--feature-list))
 
diff --git a/lisp/progmodes/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el
index 7b53a44deb2..22e8956661d 100644
--- a/lisp/progmodes/heex-ts-mode.el
+++ b/lisp/progmodes/heex-ts-mode.el
@@ -166,6 +166,16 @@ heex-ts-mode
                   ("Slot" "\\`slot\\'" nil nil)
                   ("Tag" "\\`tag\\'" nil nil)))
 
+    ;; Outline minor mode
+    ;; `heex-ts-mode' inherits from `html-mode' that sets
+    ;; regexp-based outline variables.  So need to restore
+    ;; the default values of outline variables to be able
+    ;; to use `treesit-outline-predicate' derived
+    ;; from `treesit-simple-imenu-settings' above.
+    (kill-local-variable 'outline-heading-end-regexp)
+    (kill-local-variable 'outline-regexp)
+    (kill-local-variable 'outline-level)
+
     (setq-local treesit-font-lock-settings heex-ts--font-lock-settings)
 
     (setq-local treesit-simple-indent-rules heex-ts--indent-rules)
diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 05a3ff6d7c6..dc2a8fcec1e 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -774,7 +774,7 @@ lua-ts-mode
                                       "vararg_expression"))))
                    (text "comment"))))
 
-    ;; Imenu.
+    ;; Imenu/Outline.
     (setq-local treesit-simple-imenu-settings
                 `(("Requires"
                    "\\`function_call\\'"
@@ -789,16 +789,6 @@ lua-ts-mode
     ;; Which-function.
     (setq-local which-func-functions (treesit-defun-at-point))
 
-    ;; Outline.
-    (setq-local outline-regexp
-                (rx (seq (0+ space)
-                         (or (seq "--[[" (0+ space) eol)
-                             (seq symbol-start
-                                  (or "do" "for" "if" "repeat" "while"
-                                      (seq (? (seq "local" (1+ space)))
-                                           "function"))
-                                  symbol-end)))))
-
     ;; Align.
     (setq-local align-indent-before-aligning t)
 
diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el
index 301f3e8791c..9af2aa6748f 100644
--- a/lisp/textmodes/html-ts-mode.el
+++ b/lisp/textmodes/html-ts-mode.el
@@ -121,6 +121,17 @@ html-ts-mode
   ;; Imenu.
   (setq-local treesit-simple-imenu-settings
               '(("Element" "\\`tag_name\\'" nil nil)))
+
+  ;; Outline minor mode.
+  (setq-local treesit-outline-predicate "\\`element\\'")
+  ;; `html-ts-mode' inherits from `html-mode' that sets
+  ;; regexp-based outline variables.  So need to restore
+  ;; the default values of outline variables to be able
+  ;; to use `treesit-outline-predicate' above.
+  (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)

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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-08  7:40     ` Juri Linkov
@ 2024-02-08  8:30       ` Eli Zaretskii
  2024-02-08 17:20         ` Juri Linkov
  0 siblings, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-08  8:30 UTC (permalink / raw)
  To: Juri Linkov; +Cc: casouri, 68824

> From: Juri Linkov <juri@linkov.net>
> Cc: casouri@gmail.com,  68824@debbugs.gnu.org
> Date: Thu, 08 Feb 2024 09:40:46 +0200
> 
> > Here is a new patch with a shorter implementation.
> > And I will send a complete patch with documentation changes later.
> 
> So here is a complete patch with documentation changes:

Thanks, some comments below.

> --- a/doc/emacs/text.texi
> +++ b/doc/emacs/text.texi
> @@ -1097,6 +1097,11 @@ Outline Format
>  chapters.  This works as long as no other command starts with
>  @samp{@@chap}.
>  
> +@vindex outline-search-function
> +  Instead of setting the variable @code{outline-regexp}, you can set
> +the variable @code{outline-search-function} to a function that
> +matches the current heading and searches for the next one.

This should tell which arguments will be passed to the function, and
what it is expected to do and return.

> --- a/doc/lispref/parsing.texi
> +++ b/doc/lispref/parsing.texi
> @@ -1895,6 +1895,10 @@ Tree-sitter Major Modes
>  @item
>  If @code{treesit-simple-imenu-settings} (@pxref{Imenu}) is
>  non-@code{nil}, it sets up Imenu.
> +
> +@item
> +If @code{treesit-outline-predicate} is non-@code{nil}, it sets up
> +Outline Minor Mode (@pxref{Outline Minor Mode,,, emacs, The GNU Emacs Manual}).
>  @end itemize

There should be an index entry somewhere, probably right here, for
treesit-outline-predicate.

> --- a/etc/NEWS
> +++ b/etc/NEWS
> @@ -130,6 +130,12 @@ the signature) the automatically inferred function type as well.
>  This user option controls outline visibility in the output buffer of
>  'describe-bindings' when 'describe-bindings-outline' is non-nil.
>  
> +** Outline Mode
> +
> +*** 'outline-minor-mode' is supported in tree-sitter major modes.
> +It can be used in all tree-sitter major modes that set either the
> +variable 'treesit-simple-imenu-settings' or 'treesit-outline-predicate'.

If we don't need any more documentation, this entry should be marked
with "+++".

> +(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.")

The doc string should explain with what arguments will the predicate
be called and when it is expected to return non-nil.

> +(defun treesit-outline-predicate-from-imenu (node)
> +  "Return an outline searching predicate created from Imenu.
> +Return the value suitable to set `treesit-outline-predicate'.
> +Create this predicate from the value `treesit-simple-imenu-settings'
> +that major modes set to find Imenu entries.  The assumption here
> +is that the positions of Imenu entries most of the time coincide
> +with the lines of outline headings.  When this assumption fails,
> +you can directly set a proper value to `treesit-outline-predicate'."

Likewise here: please describe in the doc string the signature and
expectations from the predicate returned by this function (or point to
the doc of some other variable/function where that information is
included).

> +(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'."

What about the return value (if it is important)?





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-08  8:30       ` Eli Zaretskii
@ 2024-02-08 17:20         ` Juri Linkov
  2024-02-08 18:56           ` Eli Zaretskii
  0 siblings, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-08 17:20 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: casouri, 68824

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

>> --- a/doc/emacs/text.texi
>> +++ b/doc/emacs/text.texi
>> @@ -1097,6 +1097,11 @@ Outline Format
>>  chapters.  This works as long as no other command starts with
>>  @samp{@@chap}.
>>
>> +@vindex outline-search-function
>> +  Instead of setting the variable @code{outline-regexp}, you can set
>> +the variable @code{outline-search-function} to a function that
>> +matches the current heading and searches for the next one.
>
> This should tell which arguments will be passed to the function, and
> what it is expected to do and return.

This is the Emacs User Manual, not the Emacs Lisp Reference Manual.
Are you sure that arguments should be described here?

>> +(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.")
>
> The doc string should explain with what arguments will the predicate
> be called and when it is expected to return non-nil.

Fixed.

>> +(defun treesit-outline-predicate-from-imenu (node)
>> +  "Return an outline searching predicate created from Imenu.
>> +Return the value suitable to set `treesit-outline-predicate'.
>> +Create this predicate from the value `treesit-simple-imenu-settings'
>> +that major modes set to find Imenu entries.  The assumption here
>> +is that the positions of Imenu entries most of the time coincide
>> +with the lines of outline headings.  When this assumption fails,
>> +you can directly set a proper value to `treesit-outline-predicate'."
>
> Likewise here: please describe in the doc string the signature and
> expectations from the predicate returned by this function (or point to
> the doc of some other variable/function where that information is
> included).

Actually, this is an internal function and doesn't need a docstring at all.
So I added two slashes to its name, and demoted its docstring to comments.

>> +(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'."
>
> What about the return value (if it is important)?

The return value is described in the docstring of
`outline-search-function' where the docstring above links to.


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

diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 338bf014208..48a70c171dd 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -1097,6 +1097,11 @@ Outline Format
 chapters.  This works as long as no other command starts with
 @samp{@@chap}.
 
+@vindex outline-search-function
+  Instead of setting the variable @code{outline-regexp}, you can set
+the variable @code{outline-search-function} to a function that
+matches the current heading and searches for the next one.
+
 @vindex outline-level
   You can explicitly specify a rule for calculating the level of a
 heading line by setting the variable @code{outline-level}.  The value
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
index ac11f88ae4d..7dd5784686b 100644
--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi
@@ -1895,6 +1895,11 @@ Tree-sitter Major Modes
 @item
 If @code{treesit-simple-imenu-settings} (@pxref{Imenu}) is
 non-@code{nil}, it sets up Imenu.
+
+@vindex treesit-outline-predicate
+@item
+If @code{treesit-outline-predicate} is non-@code{nil}, it sets up
+Outline Minor Mode (@pxref{Outline Minor Mode,,, emacs, The GNU Emacs Manual}).
 @end itemize
 
 @c TODO: Add treesit-thing-settings stuff once we finalize it.
diff --git a/etc/NEWS b/etc/NEWS
index f980d612a57..93e023b8ffc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -130,6 +130,13 @@ the signature) the automatically inferred function type as well.
 This user option controls outline visibility in the output buffer of
 'describe-bindings' when 'describe-bindings-outline' is non-nil.
 
+** Outline Mode
+
++++
+*** 'outline-minor-mode' is supported in tree-sitter major modes.
+It can be used in all tree-sitter major modes that set either the
+variable 'treesit-simple-imenu-settings' or 'treesit-outline-predicate'.
+
 ** X selection requests are now handled much faster and asynchronously.
 This means it should be less necessary to disable the likes of
 'select-active-regions' when Emacs is running over a slow network
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 6a485ae591a..25ac582276b 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2860,6 +2860,71 @@ 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.
+The predicate can be a function, a regexp matching node type,
+and more; see docstring of `treesit-thing-settings'.
+It matches the nodes located on lines with outline headings.
+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-predicate--from-imenu (node)
+  ;; Return an outline searching predicate created from Imenu.
+  ;; Return the value suitable to set `treesit-outline-predicate'.
+  ;; Create this predicate from the value `treesit-simple-imenu-settings'
+  ;; that major modes set to find Imenu entries.  The assumption here
+  ;; is that the positions of Imenu entries most of the time coincide
+  ;; with the lines of outline headings.  When this assumption fails,
+  ;; you can directly set a proper value to `treesit-outline-predicate'.
+  (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))
+
+(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)
@@ -2990,6 +3055,17 @@ 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
+            #'treesit-outline-predicate--from-imenu))
+    (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..c4b48f03d12 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -922,6 +922,17 @@ c-ts-mode--defun-name
         name)))
    t))
 
+;;; Outline minor mode
+
+(defun c-ts-mode--outline-predicate (node)
+  "Match outlines on lines with function names."
+  (and (treesit-node-match-p
+        node "\\`function_declarator\\'" t)
+       (when-let ((parent (treesit-node-parent node)))
+         (treesit-node-match-p
+          parent
+          "\\`function_definition\\'" t))))
+
 ;;; Defun navigation
 
 (defun c-ts-mode--defun-valid-p (node)
@@ -1259,6 +1270,10 @@ c-ts-base-mode
                                 eos)
                    c-ts-mode--defun-for-class-in-imenu-p nil))))
 
+  ;; Outline minor mode
+  (setq-local treesit-outline-predicate
+              #'c-ts-mode--outline-predicate)
+
   (setq-local treesit-font-lock-feature-list
               c-ts-mode--feature-list))
 
diff --git a/lisp/progmodes/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el
index 7b53a44deb2..22e8956661d 100644
--- a/lisp/progmodes/heex-ts-mode.el
+++ b/lisp/progmodes/heex-ts-mode.el
@@ -166,6 +166,16 @@ heex-ts-mode
                   ("Slot" "\\`slot\\'" nil nil)
                   ("Tag" "\\`tag\\'" nil nil)))
 
+    ;; Outline minor mode
+    ;; `heex-ts-mode' inherits from `html-mode' that sets
+    ;; regexp-based outline variables.  So need to restore
+    ;; the default values of outline variables to be able
+    ;; to use `treesit-outline-predicate' derived
+    ;; from `treesit-simple-imenu-settings' above.
+    (kill-local-variable 'outline-heading-end-regexp)
+    (kill-local-variable 'outline-regexp)
+    (kill-local-variable 'outline-level)
+
     (setq-local treesit-font-lock-settings heex-ts--font-lock-settings)
 
     (setq-local treesit-simple-indent-rules heex-ts--indent-rules)
diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 05a3ff6d7c6..dc2a8fcec1e 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -774,7 +774,7 @@ lua-ts-mode
                                       "vararg_expression"))))
                    (text "comment"))))
 
-    ;; Imenu.
+    ;; Imenu/Outline.
     (setq-local treesit-simple-imenu-settings
                 `(("Requires"
                    "\\`function_call\\'"
@@ -789,16 +789,6 @@ lua-ts-mode
     ;; Which-function.
     (setq-local which-func-functions (treesit-defun-at-point))
 
-    ;; Outline.
-    (setq-local outline-regexp
-                (rx (seq (0+ space)
-                         (or (seq "--[[" (0+ space) eol)
-                             (seq symbol-start
-                                  (or "do" "for" "if" "repeat" "while"
-                                      (seq (? (seq "local" (1+ space)))
-                                           "function"))
-                                  symbol-end)))))
-
     ;; Align.
     (setq-local align-indent-before-aligning t)
 
diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el
index 301f3e8791c..9af2aa6748f 100644
--- a/lisp/textmodes/html-ts-mode.el
+++ b/lisp/textmodes/html-ts-mode.el
@@ -121,6 +121,17 @@ html-ts-mode
   ;; Imenu.
   (setq-local treesit-simple-imenu-settings
               '(("Element" "\\`tag_name\\'" nil nil)))
+
+  ;; Outline minor mode.
+  (setq-local treesit-outline-predicate "\\`element\\'")
+  ;; `html-ts-mode' inherits from `html-mode' that sets
+  ;; regexp-based outline variables.  So need to restore
+  ;; the default values of outline variables to be able
+  ;; to use `treesit-outline-predicate' above.
+  (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)

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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-08 17:20         ` Juri Linkov
@ 2024-02-08 18:56           ` Eli Zaretskii
  2024-02-09  7:07             ` Juri Linkov
  0 siblings, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-08 18:56 UTC (permalink / raw)
  To: Juri Linkov; +Cc: casouri, 68824

> From: Juri Linkov <juri@linkov.net>
> Cc: casouri@gmail.com,  68824@debbugs.gnu.org
> Date: Thu, 08 Feb 2024 19:20:17 +0200
> 
> >> +@vindex outline-search-function
> >> +  Instead of setting the variable @code{outline-regexp}, you can set
> >> +the variable @code{outline-search-function} to a function that
> >> +matches the current heading and searches for the next one.
> >
> > This should tell which arguments will be passed to the function, and
> > what it is expected to do and return.
> 
> This is the Emacs User Manual, not the Emacs Lisp Reference Manual.
> Are you sure that arguments should be described here?

Sorry, I wasn't aware that was for the user manual.  But why are such
variables documented in the user manual to begin with? why not in the
ELisp manual?

Thanks.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-08 18:56           ` Eli Zaretskii
@ 2024-02-09  7:07             ` Juri Linkov
  2024-02-09 19:25               ` Eli Zaretskii
  0 siblings, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-09  7:07 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: casouri, 68824

>> >> +@vindex outline-search-function
>> >> +  Instead of setting the variable @code{outline-regexp}, you can set
>> >> +the variable @code{outline-search-function} to a function that
>> >> +matches the current heading and searches for the next one.
>> >
>> > This should tell which arguments will be passed to the function, and
>> > what it is expected to do and return.
>>
>> This is the Emacs User Manual, not the Emacs Lisp Reference Manual.
>> Are you sure that arguments should be described here?
>
> Sorry, I wasn't aware that was for the user manual.  But why are such
> variables documented in the user manual to begin with? why not in the
> ELisp manual?

The problem is that the Info node in the ELisp manual would be too small.
I have no idea what to write in that node more than 10 lines from the
docstring of 'outline-search-function'.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-09  7:07             ` Juri Linkov
@ 2024-02-09 19:25               ` Eli Zaretskii
  2024-02-10 17:29                 ` Juri Linkov
  0 siblings, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-09 19:25 UTC (permalink / raw)
  To: Juri Linkov; +Cc: casouri, 68824

> From: Juri Linkov <juri@linkov.net>
> Cc: casouri@gmail.com,  68824@debbugs.gnu.org
> Date: Fri, 09 Feb 2024 09:07:44 +0200
> 
> >> >> +@vindex outline-search-function
> >> >> +  Instead of setting the variable @code{outline-regexp}, you can set
> >> >> +the variable @code{outline-search-function} to a function that
> >> >> +matches the current heading and searches for the next one.
> >> >
> >> > This should tell which arguments will be passed to the function, and
> >> > what it is expected to do and return.
> >>
> >> This is the Emacs User Manual, not the Emacs Lisp Reference Manual.
> >> Are you sure that arguments should be described here?
> >
> > Sorry, I wasn't aware that was for the user manual.  But why are such
> > variables documented in the user manual to begin with? why not in the
> > ELisp manual?
> 
> The problem is that the Info node in the ELisp manual would be too small.
> I have no idea what to write in that node more than 10 lines from the
> docstring of 'outline-search-function'.

Why does it have to be a separate node?  Why cannot we have a
paragraph or two about this in "Tree-sitter Major Modes", for example,
or in "Minor Modes"?





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-09 19:25               ` Eli Zaretskii
@ 2024-02-10 17:29                 ` Juri Linkov
  2024-02-10 17:57                   ` Eli Zaretskii
  0 siblings, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-10 17:29 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: casouri, 68824

>> >> >> +@vindex outline-search-function
>> >> >> +  Instead of setting the variable @code{outline-regexp}, you can set
>> >> >> +the variable @code{outline-search-function} to a function that
>> >> >> +matches the current heading and searches for the next one.
>> >> >
>> >> > This should tell which arguments will be passed to the function, and
>> >> > what it is expected to do and return.
>> >>
>> >> This is the Emacs User Manual, not the Emacs Lisp Reference Manual.
>> >> Are you sure that arguments should be described here?
>> >
>> > Sorry, I wasn't aware that was for the user manual.  But why are such
>> > variables documented in the user manual to begin with? why not in the
>> > ELisp manual?
>>
>> The problem is that the Info node in the ELisp manual would be too small.
>> I have no idea what to write in that node more than 10 lines from the
>> docstring of 'outline-search-function'.
>
> Why does it have to be a separate node?  Why cannot we have a
> paragraph or two about this in "Tree-sitter Major Modes", for example,
> or in "Minor Modes"?

'outline-search-function' is an internal detail of implementation
for 'treesit-outline-predicate'.  Here is the relevant part from the
previous patch that shows there is no place for 'outline-search-function:

diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 338bf014208..48a70c171dd 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -1097,6 +1097,11 @@ Outline Format
 chapters.  This works as long as no other command starts with
 @samp{@@chap}.
 
+@vindex outline-search-function
+  Instead of setting the variable @code{outline-regexp}, you can set
+the variable @code{outline-search-function} to a function that
+matches the current heading and searches for the next one.
+
 @vindex outline-level
   You can explicitly specify a rule for calculating the level of a
 heading line by setting the variable @code{outline-level}.  The value
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
index ac11f88ae4d..7dd5784686b 100644
--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi
@@ -1895,6 +1895,11 @@ Tree-sitter Major Modes
 @item
 If @code{treesit-simple-imenu-settings} (@pxref{Imenu}) is
 non-@code{nil}, it sets up Imenu.
+
+@vindex treesit-outline-predicate
+@item
+If @code{treesit-outline-predicate} is non-@code{nil}, it sets up
+Outline Minor Mode (@pxref{Outline Minor Mode,,, emacs, The GNU Emacs Manual}).
 @end itemize





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-10 17:29                 ` Juri Linkov
@ 2024-02-10 17:57                   ` Eli Zaretskii
  2024-02-11 17:34                     ` Juri Linkov
  0 siblings, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-10 17:57 UTC (permalink / raw)
  To: Juri Linkov; +Cc: casouri, 68824

> From: Juri Linkov <juri@linkov.net>
> Cc: casouri@gmail.com,  68824@debbugs.gnu.org
> Date: Sat, 10 Feb 2024 19:29:20 +0200
> 
> >> The problem is that the Info node in the ELisp manual would be too small.
> >> I have no idea what to write in that node more than 10 lines from the
> >> docstring of 'outline-search-function'.
> >
> > Why does it have to be a separate node?  Why cannot we have a
> > paragraph or two about this in "Tree-sitter Major Modes", for example,
> > or in "Minor Modes"?
> 
> 'outline-search-function' is an internal detail of implementation
> for 'treesit-outline-predicate'.  Here is the relevant part from the
> previous patch that shows there is no place for 'outline-search-function:

Then maybe just move the text about outline-search-function to the
place where you describe treesit-outline-predicate?





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-10 17:57                   ` Eli Zaretskii
@ 2024-02-11 17:34                     ` Juri Linkov
  2024-02-11 19:22                       ` Eli Zaretskii
  0 siblings, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-11 17:34 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: casouri, 68824

>> >> The problem is that the Info node in the ELisp manual would be too small.
>> >> I have no idea what to write in that node more than 10 lines from the
>> >> docstring of 'outline-search-function'.
>> >
>> > Why does it have to be a separate node?  Why cannot we have a
>> > paragraph or two about this in "Tree-sitter Major Modes", for example,
>> > or in "Minor Modes"?
>> 
>> 'outline-search-function' is an internal detail of implementation
>> for 'treesit-outline-predicate'.  Here is the relevant part from the
>> previous patch that shows there is no place for 'outline-search-function:
>
> Then maybe just move the text about outline-search-function to the
> place where you describe treesit-outline-predicate?

'outline-search-function' is unsuitable for "Tree-sitter Major Modes".





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-11 17:34                     ` Juri Linkov
@ 2024-02-11 19:22                       ` Eli Zaretskii
  2024-02-12  1:07                         ` Yuan Fu
  0 siblings, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-11 19:22 UTC (permalink / raw)
  To: Juri Linkov; +Cc: casouri, 68824

> From: Juri Linkov <juri@linkov.net>
> Cc: casouri@gmail.com,  68824@debbugs.gnu.org
> Date: Sun, 11 Feb 2024 19:34:51 +0200
> 
> >> >> The problem is that the Info node in the ELisp manual would be too small.
> >> >> I have no idea what to write in that node more than 10 lines from the
> >> >> docstring of 'outline-search-function'.
> >> >
> >> > Why does it have to be a separate node?  Why cannot we have a
> >> > paragraph or two about this in "Tree-sitter Major Modes", for example,
> >> > or in "Minor Modes"?
> >> 
> >> 'outline-search-function' is an internal detail of implementation
> >> for 'treesit-outline-predicate'.  Here is the relevant part from the
> >> previous patch that shows there is no place for 'outline-search-function:
> >
> > Then maybe just move the text about outline-search-function to the
> > place where you describe treesit-outline-predicate?
> 
> 'outline-search-function' is unsuitable for "Tree-sitter Major Modes".

All of this thread clearly indicates otherwise.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-11 19:22                       ` Eli Zaretskii
@ 2024-02-12  1:07                         ` Yuan Fu
  2024-02-12 12:59                           ` Eli Zaretskii
  0 siblings, 1 reply; 29+ messages in thread
From: Yuan Fu @ 2024-02-12  1:07 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 68824, Juri Linkov



> On Feb 11, 2024, at 11:22 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Juri Linkov <juri@linkov.net>
>> Cc: casouri@gmail.com,  68824@debbugs.gnu.org
>> Date: Sun, 11 Feb 2024 19:34:51 +0200
>> 
>>>>>> The problem is that the Info node in the ELisp manual would be too small.
>>>>>> I have no idea what to write in that node more than 10 lines from the
>>>>>> docstring of 'outline-search-function'.
>>>>> 
>>>>> Why does it have to be a separate node?  Why cannot we have a
>>>>> paragraph or two about this in "Tree-sitter Major Modes", for example,
>>>>> or in "Minor Modes"?
>>>> 
>>>> 'outline-search-function' is an internal detail of implementation
>>>> for 'treesit-outline-predicate'.  Here is the relevant part from the
>>>> previous patch that shows there is no place for 'outline-search-function:
>>> 
>>> Then maybe just move the text about outline-search-function to the
>>> place where you describe treesit-outline-predicate?
>> 
>> 'outline-search-function' is unsuitable for "Tree-sitter Major Modes".
> 
> All of this thread clearly indicates otherwise.

The real problem is probably that outline-minor-mode’s api wasn’t described in the Elisp manual (Imenu, OTOH, has a node in Elisp manual). I think we can create a node for outline in (elisp)Modes (that’s where the node for Imenu is). We can start out with only outline-search-function in there, and over time populate it with outline-level, outline-regexp, etc.

If in the future we add language-agnostic features similar to outline and imenu, which needs major mode integration, (elisp)Modes would be a good place to document them.

Yuan




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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-12  1:07                         ` Yuan Fu
@ 2024-02-12 12:59                           ` Eli Zaretskii
  2024-02-12 18:16                             ` Juri Linkov
  0 siblings, 1 reply; 29+ messages in thread
From: Eli Zaretskii @ 2024-02-12 12:59 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 68824, juri

> From: Yuan Fu <casouri@gmail.com>
> Date: Sun, 11 Feb 2024 17:07:00 -0800
> Cc: Juri Linkov <juri@linkov.net>,
>  68824@debbugs.gnu.org
> 
> >>> Then maybe just move the text about outline-search-function to the
> >>> place where you describe treesit-outline-predicate?
> >> 
> >> 'outline-search-function' is unsuitable for "Tree-sitter Major Modes".
> > 
> > All of this thread clearly indicates otherwise.
> 
> The real problem is probably that outline-minor-mode’s api wasn’t described in the Elisp manual (Imenu, OTOH, has a node in Elisp manual). I think we can create a node for outline in (elisp)Modes (that’s where the node for Imenu is). We can start out with only outline-search-function in there, and over time populate it with outline-level, outline-regexp, etc.

That'd be fine by me.  But since Juri said he was uncomfortable with
such a short node, I tried to suggest alternative ideas.  If having a
short new node is deemed our best option, then let's do that.

We could also have a node for importand minor modes (as opposed to
having a separate node for each such minor mode).

> If in the future we add language-agnostic features similar to outline and imenu, which needs major mode integration, (elisp)Modes would be a good place to document them.

Right.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-12 12:59                           ` Eli Zaretskii
@ 2024-02-12 18:16                             ` Juri Linkov
  2024-02-13 17:02                               ` Juri Linkov
  0 siblings, 1 reply; 29+ messages in thread
From: Juri Linkov @ 2024-02-12 18:16 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Yuan Fu, 68824

>> >>> Then maybe just move the text about outline-search-function to the
>> >>> place where you describe treesit-outline-predicate?
>> >> 
>> >> 'outline-search-function' is unsuitable for "Tree-sitter Major Modes".
>> > 
>> > All of this thread clearly indicates otherwise.
>> 
>> The real problem is probably that outline-minor-mode’s api wasn’t
>> described in the Elisp manual (Imenu, OTOH, has a node in Elisp
>> manual). I think we can create a node for outline in (elisp)Modes (that’s
>> where the node for Imenu is). We can start out with only
>> outline-search-function in there, and over time populate it with
>> outline-level, outline-regexp, etc.
>
> That'd be fine by me.  But since Juri said he was uncomfortable with
> such a short node, I tried to suggest alternative ideas.  If having a
> short new node is deemed our best option, then let's do that.

Ok, let's add a new node.

This is now done and pushed to master.

Please check if everything is correct.





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

* bug#68824: treesitter support for outline-minor-mode
  2024-02-12 18:16                             ` Juri Linkov
@ 2024-02-13 17:02                               ` Juri Linkov
  0 siblings, 0 replies; 29+ messages in thread
From: Juri Linkov @ 2024-02-13 17:02 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Yuan Fu, 68824

close 68824 30.0.50
thanks

>>> >>> Then maybe just move the text about outline-search-function to the
>>> >>> place where you describe treesit-outline-predicate?
>>> >> 
>>> >> 'outline-search-function' is unsuitable for "Tree-sitter Major Modes".
>>> > 
>>> > All of this thread clearly indicates otherwise.
>>> 
>>> The real problem is probably that outline-minor-mode’s api wasn’t
>>> described in the Elisp manual (Imenu, OTOH, has a node in Elisp
>>> manual). I think we can create a node for outline in (elisp)Modes (that’s
>>> where the node for Imenu is). We can start out with only
>>> outline-search-function in there, and over time populate it with
>>> outline-level, outline-regexp, etc.
>>
>> That'd be fine by me.  But since Juri said he was uncomfortable with
>> such a short node, I tried to suggest alternative ideas.  If having a
>> short new node is deemed our best option, then let's do that.
>
> Ok, let's add a new node.
>
> This is now done and pushed to master.
>
> Please check if everything is correct.

I pushed more fixes, and added treesit-outline-predicate to ruby-ts-mode,
and everything works, so now closing this request.





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

end of thread, other threads:[~2024-02-13 17:02 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

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).