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'.
(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))
(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
(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
#'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