New variable 'treesit-aggregated-outline-predicate' (bug#76398)
* doc/lispref/modes.texi (Outline Minor Mode): Add 'treesit-aggregated-outline-predicate'. * lisp/treesit.el (treesit-aggregated-outline-predicate): New buffer-local variable. (treesit-outline--at-point): Use 'treesit-aggregated-outline-predicate'. (treesit-closest-parser-boundary): New function. (treesit-outline-search): Use 'treesit-aggregated-outline-predicate' and 'treesit-closest-parser-boundary'. (treesit-outline-level): Use 'treesit-aggregated-outline-predicate'. (treesit-major-mode-setup): Add 'treesit-aggregated-outline-predicate'. * lisp/textmodes/html-ts-mode.el (html-ts-mode--outline-predicate): Improve. * lisp/textmodes/mhtml-ts-mode.el (mhtml-ts-mode): Set 'treesit-aggregated-outline-predicate'.
This commit is contained in:
parent
1f2e06283c
commit
840be8a7d8
5 changed files with 114 additions and 28 deletions
|
@ -3175,6 +3175,16 @@ This variable instructs Emacs how to find lines with outline headings.
|
|||
It should be a predicate that matches the node on the heading line.
|
||||
@end defvar
|
||||
|
||||
@defvar treesit-aggregated-outline-predicate
|
||||
This variable allows major modes to configure outlines for multiple
|
||||
languages. Its value is an alist mapping language symbols to outline
|
||||
headings of the form described above for the value of
|
||||
@code{treesit-outline-predicate}.
|
||||
|
||||
If this variable is non-@code{nil}, it overrides
|
||||
@code{treesit-outline-predicate} for setting up outline headings.
|
||||
@end defvar
|
||||
|
||||
@node Font Lock Mode
|
||||
@section Font Lock Mode
|
||||
@cindex Font Lock mode
|
||||
|
|
5
etc/NEWS
5
etc/NEWS
|
@ -1468,6 +1468,11 @@ at point to explore.
|
|||
*** New variable 'treesit-aggregated-simple-imenu-settings'.
|
||||
This variable allows major modes to setup Imenu for multiple languages.
|
||||
|
||||
+++
|
||||
*** New variable 'treesit-aggregated-outline-predicate'.
|
||||
This variable allows major modes to setup 'outline-minor-mode'
|
||||
for multiple languages.
|
||||
|
||||
*** New function 'treesit-simple-indent-add-rules'.
|
||||
This new function makes it easier to customize indent rules for
|
||||
tree-sitter modes.
|
||||
|
|
|
@ -126,14 +126,15 @@ Return nil if there is no name or if NODE is not a defun node."
|
|||
t)))
|
||||
|
||||
(defun html-ts-mode--outline-predicate (node)
|
||||
"Limit outlines to a few most meaningful elements."
|
||||
(let ((name (html-ts-mode--defun-name node)))
|
||||
(and name (string-match-p
|
||||
(rx bos (or "html" "head" "script" "style"
|
||||
"body" (and "h" (any "1-6"))
|
||||
"ol" "ul" "table")
|
||||
eos)
|
||||
name))))
|
||||
"Limit outlines to multi-line elements."
|
||||
(when (string-match-p "element" (treesit-node-type node))
|
||||
(< (save-excursion
|
||||
(goto-char (treesit-node-start node))
|
||||
(pos-bol))
|
||||
(save-excursion
|
||||
(goto-char (treesit-node-end node))
|
||||
(skip-chars-backward " \t\n")
|
||||
(pos-bol)))))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode html-ts-mode html-mode "HTML"
|
||||
|
|
|
@ -580,7 +580,11 @@ Powered by tree-sitter."
|
|||
(setq-local treesit-aggregated-simple-imenu-settings
|
||||
mhtml-ts-mode--treesit-aggregated-simple-imenu-settings)
|
||||
|
||||
;; (setq-local treesit-outline-predicate nil)
|
||||
(setq-local treesit-aggregated-outline-predicate
|
||||
`((html . ,#'html-ts-mode--outline-predicate)
|
||||
;; TODO: add a predicate like for html above
|
||||
(javascript . "\\`function_declaration\\'")
|
||||
(css . "\\`rule_set\\'")))
|
||||
|
||||
(treesit-major-mode-setup)
|
||||
|
||||
|
|
104
lisp/treesit.el
104
lisp/treesit.el
|
@ -3601,6 +3601,16 @@ 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-aggregated-outline-predicate nil
|
||||
"Settings that configure `treesit-outline-search' for multi-language modes.
|
||||
|
||||
The value should be an alist of (LANG . SETTINGS), where LANG is a
|
||||
language symbol, and SETTINGS has the same form as
|
||||
`treesit-outline-predicate'.
|
||||
|
||||
When both this variable and `treesit-outline-predicate' are non-nil,
|
||||
this variable takes priority.")
|
||||
|
||||
(defun treesit-outline-predicate--from-imenu (node)
|
||||
;; Return an outline searching predicate created from Imenu.
|
||||
;; Return the value suitable to set `treesit-outline-predicate'.
|
||||
|
@ -3618,7 +3628,10 @@ when a major mode sets it.")
|
|||
|
||||
(defun treesit-outline--at-point ()
|
||||
"Return the outline heading node at the current line."
|
||||
(let* ((pred treesit-outline-predicate)
|
||||
(let* ((pred (if treesit-aggregated-outline-predicate
|
||||
(alist-get (treesit-language-at (point))
|
||||
treesit-aggregated-outline-predicate)
|
||||
treesit-outline-predicate))
|
||||
(bol (pos-bol))
|
||||
(eol (pos-eol))
|
||||
(current (treesit-thing-at (point) pred))
|
||||
|
@ -3630,6 +3643,18 @@ when a major mode sets it.")
|
|||
(or (and current-valid current)
|
||||
(and next-valid (treesit-thing-at next pred)))))
|
||||
|
||||
(defun treesit-closest-parser-boundary (pos backward)
|
||||
"Get the closest boundary of a local parser."
|
||||
(when-let* ((ranges (mapcar #'treesit-parser-included-ranges
|
||||
(treesit-parser-list)))
|
||||
(ranges (delq nil (delete '((1 . 1)) ranges)))
|
||||
(bounds (seq-filter
|
||||
(lambda (p) (if backward (< p pos) (> p pos)))
|
||||
(flatten-list ranges)))
|
||||
(closest (when bounds
|
||||
(if backward (seq-max bounds) (seq-min bounds)))))
|
||||
closest))
|
||||
|
||||
(defun treesit-outline-search (&optional bound move backward looking-at)
|
||||
"Search for the next outline heading in the syntax tree.
|
||||
For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in
|
||||
|
@ -3649,28 +3674,66 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in
|
|||
(if (eq (point) (pos-bol))
|
||||
(if (bobp) (point) (1- (point)))
|
||||
(pos-eol))))
|
||||
(pred (if treesit-aggregated-outline-predicate
|
||||
(alist-get (treesit-language-at pos)
|
||||
treesit-aggregated-outline-predicate)
|
||||
treesit-outline-predicate))
|
||||
(found (or bob-pos
|
||||
(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))))
|
||||
(treesit-navigate-thing pos (if backward -1 1) 'beg pred)))
|
||||
(closest (treesit-closest-parser-boundary pos backward)))
|
||||
|
||||
;; Handle multi-language modes
|
||||
(if (and closest
|
||||
(or
|
||||
;; Possibly was inside the local parser, and when can't find
|
||||
;; more matches inside it then need to go over the closest
|
||||
;; parser boundary to the primary parser.
|
||||
(not found)
|
||||
;; Possibly skipped the local parser, either while navigating
|
||||
;; inside the primary parser, or inside a local parser
|
||||
;; interspersed by ranges of other local parsers, e.g.
|
||||
;; <html><script>|</script><style/><script/></html>
|
||||
(if backward (> closest found) (< closest found))))
|
||||
(progn
|
||||
(goto-char (if backward
|
||||
(max (point-min) (1- closest))
|
||||
(min (point-max) (1+ closest))))
|
||||
(treesit-outline-search bound move backward))
|
||||
|
||||
(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-outline--at-point))
|
||||
(level 1))
|
||||
(while (setq node (treesit-parent-until node treesit-outline-predicate))
|
||||
(level 1)
|
||||
(parser (when treesit-aggregated-outline-predicate
|
||||
(treesit-node-parser node)))
|
||||
(pred (if treesit-aggregated-outline-predicate
|
||||
(alist-get (treesit-language-at (point))
|
||||
treesit-aggregated-outline-predicate)
|
||||
treesit-outline-predicate)))
|
||||
(while (setq node (treesit-parent-until node pred))
|
||||
(setq level (1+ level)))
|
||||
(if (zerop level) 1 level)))
|
||||
(when-let* ((_ parser)
|
||||
(host-lang (treesit-parser-language treesit-primary-parser))
|
||||
(_ (not (eq (treesit-language-at (point)) host-lang)))
|
||||
(host-pred (alist-get host-lang treesit-aggregated-outline-predicate)))
|
||||
;; Now need to break out of embedded confinement
|
||||
;; and get the host node that contains the guest ranges
|
||||
(setq node (treesit-parser-root-node parser))
|
||||
(while (setq node (treesit-parent-until node host-pred))
|
||||
(setq level (1+ level))))
|
||||
level))
|
||||
|
||||
;;; Hideshow mode
|
||||
|
||||
|
@ -3955,11 +4018,14 @@ before calling this function."
|
|||
#'treesit-simple-imenu))
|
||||
|
||||
;; Outline minor mode.
|
||||
(when (and (or treesit-outline-predicate treesit-simple-imenu-settings)
|
||||
(when (and (or treesit-outline-predicate
|
||||
treesit-aggregated-outline-predicate
|
||||
treesit-simple-imenu-settings)
|
||||
(not (seq-some #'local-variable-p
|
||||
'(outline-search-function
|
||||
outline-regexp outline-level))))
|
||||
(unless treesit-outline-predicate
|
||||
(unless (or treesit-outline-predicate
|
||||
treesit-aggregated-outline-predicate)
|
||||
(setq treesit-outline-predicate
|
||||
#'treesit-outline-predicate--from-imenu))
|
||||
(setq-local outline-search-function #'treesit-outline-search
|
||||
|
|
Loading…
Add table
Reference in a new issue