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