emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: outline-mode treesitter support?


From: Juri Linkov
Subject: Re: outline-mode treesitter support?
Date: Wed, 24 Jan 2024 19:23:31 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/30.0.50 (x86_64-pc-linux-gnu)

>>> (defun outline-search-imenu (&optional bound move backward looking-at)
>>>  (unless imenu--index-alist
>>>    (imenu--make-index-alist))
>>>  (let* ((imenu-index (cdar imenu--index-alist))
>
> After trying to use this, I came to conclusion that imenu--index-alist
> is unsuitable for outline-minor-mode because imenu has a different
> hierarchy than the source files, so outline-level can be only 1.
> But without different levels for outline-level it's quite useless.

Many ts-modes are already defining treesit-simple-imenu-settings that
can be reused or outlines.  We just need to add levels to all entries
found for imenu.

So here is a complete working implementation.  Then
with e.g. `M-x c-ts-mode` and `M-x outline-minor-mode`
it will put outlines on declarations and definitions.

#+begin_src emacs-lisp
(add-hook
 'prog-mode-hook
 (lambda ()
   (when (bound-and-true-p treesit-simple-imenu-settings)
     (setq-local treesit-outline-predicate
                 (lambda (node)
                   (seq-some
                    (lambda (setting)
                      (and (string-match-p (nth 1 setting) (treesit-node-type 
node))
                           (funcall (nth 2 setting) node)))
                    treesit-simple-imenu-settings)))
     (setq-local treesit-outline-levels
                 (treesit-outline-levels
                  (treesit-induce-sparse-tree
                   (treesit-buffer-root-node)
                   treesit-outline-predicate)
                  0))
     (setq-local outline-search-function #'treesit-search-outline
                 outline-level
                 (lambda ()
                   (or (alist-get (point) treesit-outline-levels nil nil
                                  (lambda (m k) (eq (marker-position m) k)))
                       1))))))

(defun treesit-outline-levels (node level)
  (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-search-outline (&optional bound move backward looking-at)
  (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 (if backward
                       (seq-find (lambda (p) (< p (pos-bol))) (nreverse 
positions))
                     (seq-find (lambda (p) (> p (pos-eol))) 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)))))
#+end_src



reply via email to

[Prev in Thread] Current Thread [Next in Thread]