Package: emacs;
Reported by: Juri Linkov <juri <at> linkov.net>
Date: Tue, 18 Feb 2025 17:36:01 UTC
Severity: normal
Fixed in version 31.0.50
Done: Juri Linkov <juri <at> linkov.net>
Bug is archived. No further changes may be made.
Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Juri Linkov <juri <at> linkov.net> To: bug-gnu-emacs <at> gnu.org Subject: treesit-aggregated-outline-predicate Date: Tue, 18 Feb 2025 19:27:17 +0200
[Message part 1 (text/plain, inline)]
As discussed in bug#74610, in multi-language modes treesit-outline-predicate ends abruptly after the first embedded range since it can't find more matches in its range, it can't go out back to the primary parser. So this patch helps 'treesit-outline-search' to get out of the local parser to the primary parser to continue search for the next outline predicate. 'treesit-outline-level' should do the same, but currently I can't find a suitable function to break out of embedded confinement and get the host node that contains the guest ranges. I mean that e.g. (treesit-parser-root-node (treesit-node-parser node)) can get the root node of the local parser, but how to get its parent node in the primary parser? It's understandable that treesit-node-parent doesn't go out of its parser. But maybe there is another function? If such function doesn't exist, this is fine, then could find that node manually by calculating from treesit-parser-included-ranges.
[treesit-aggregated-outline-predicate.patch (text/x-diff, inline)]
diff --git a/lisp/textmodes/mhtml-ts-mode.el b/lisp/textmodes/mhtml-ts-mode.el index 83f8879f427..7a481599310 100644 --- a/lisp/textmodes/mhtml-ts-mode.el +++ b/lisp/textmodes/mhtml-ts-mode.el @@ -580,7 +580,10 @@ mhtml-ts-mode (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) + (javascript . "\\`function_declaration\\'") + (css . "\\`rule_set\\'"))) (treesit-major-mode-setup) diff --git a/lisp/treesit.el b/lisp/treesit.el index 30efd4d4599..ab9bfc33d3d 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -3601,6 +3601,16 @@ treesit-outline-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 @@ treesit-outline-predicate--from-imenu (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)) @@ -3649,9 +3662,35 @@ treesit-outline-search (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)))) + (treesit-navigate-thing pos (if backward -1 1) 'beg pred)))) + + ;; Handle multi-language modes + (when-let* ((ranges (mapcar #'treesit-parser-included-ranges + (treesit-parser-list))) + (ranges (delq nil (delete '((1 . 1)) ranges)))) + (if found + nil + ;; Possibly was inside the local range, and when can't find + ;; more matches inside the local range then need to go out + (when-let* ((bounds (seq-filter + (lambda (p) (if backward (< p pos) (> p pos))) + (flatten-list + (mapcar (lambda (rr) + (mapcar (if backward #'car #'cdr) rr)) + ranges)))) + (closest (when bounds (if backward (seq-max bounds) (seq-min bounds))))) + (setq found (treesit-navigate-thing + closest (if backward -1 1) 'beg + (if treesit-aggregated-outline-predicate + (alist-get (treesit-language-at closest) + treesit-aggregated-outline-predicate) + treesit-outline-predicate)))))) + (if found (if (or (not bound) (if backward (>= found bound) (<= found bound))) (progn @@ -3667,10 +3706,25 @@ treesit-outline-search (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 +4009,14 @@ treesit-major-mode-setup #'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
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.