[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#68824: treesitter support for outline-minor-mode
From: |
Juri Linkov |
Subject: |
bug#68824: treesitter support for outline-minor-mode |
Date: |
Sun, 04 Feb 2024 19:15:20 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/30.0.50 (x86_64-pc-linux-gnu) |
> 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.
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)
- bug#68824: treesitter support for outline-minor-mode, Juri Linkov, 2024/02/08
- bug#68824: treesitter support for outline-minor-mode, Eli Zaretskii, 2024/02/08
- bug#68824: treesitter support for outline-minor-mode, Juri Linkov, 2024/02/08
- bug#68824: treesitter support for outline-minor-mode, Eli Zaretskii, 2024/02/08
- bug#68824: treesitter support for outline-minor-mode, Juri Linkov, 2024/02/09
- bug#68824: treesitter support for outline-minor-mode, Eli Zaretskii, 2024/02/09
- bug#68824: treesitter support for outline-minor-mode, Juri Linkov, 2024/02/10