1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-03 10:31:37 -08:00

Add overlays for non-local tree-sitter parsers too

* lisp/treesit.el (treesit-local-parsers-at):
(treesit-local-parsers-on): Exclude non-local parsers.
(treesit--cleanup-local-range-overlays): Don't delete non-local
parsers (because those are managed by the major mode).
(treesit--update-ranges-non-local): Apply overlay for each ranges.
(treesit--update-ranges-local): Ignore
overlays with non-local parsers, and set
'treesit-parser-local-p' property to t.
(treesit--update-range-1): Additionally pass modified-tick to
treesit--update-ranges-non-local.
(treesit-major-mode-setup): Don't delete non-local parsers.
This commit is contained in:
Yuan Fu 2025-03-11 00:44:49 -07:00
parent 03a6d4256f
commit 57a213c91c
No known key found for this signature in database
GPG key ID: 56E19BC57664A442

View file

@ -629,6 +629,20 @@ If none are valid, return nil."
;;; Range API supplement
;; (ref:local-parser-overlay) Regarding local parser overlays, we store
;; the local parser in a overlay spanning across the code block that the
;; parser is responsible of. The `treesit-parser' property stores the
;; parser, the `treesit-host-parser' property stores the host parser,
;; the `treesit-parser-ov-timestamp' property stores the buffer's tick
;; counter (`buffer-modified-tick') when we last updated this overlay,
;; it's used for garbage-collecting stale ranges and local parsers.
;;
;; Besides local parsers, we also create overlays for non-local parsers,
;; just to mark the start and end of each range it parses, so that other
;; functions can make use of this information. To differentiate the
;; overlay for local and non-local parsers, local parsers' overlay has
;; the `treesit-parser-local-p' property set to non-nil.
(defvar-local treesit-range-settings nil
"A list of range settings.
@ -849,12 +863,16 @@ If WITH-HOST is non-nil, return a list of (PARSER . HOST-PARSER)
instead. HOST-PARSER is the host parser which created the local
PARSER."
(let ((res nil))
;; Refer to (ref:local-parser-overlay) for more explanation of local
;; parser overlays.
(dolist (ov (overlays-at (or pos (point))))
(when-let* ((parser (overlay-get ov 'treesit-parser))
(host-parser (overlay-get ov 'treesit-host-parser)))
(when (or (null language)
(eq (treesit-parser-language parser)
language))
(let ((parser (overlay-get ov 'treesit-parser))
(host-parser (overlay-get ov 'treesit-host-parser))
(local-p (overlay-get ov 'treesit-parser-local-p)))
(when (and parser host-parser local-p
(or (null language)
(eq (treesit-parser-language parser)
language)))
(push (if with-host (cons parser host-parser) parser) res))))
(nreverse res)))
@ -872,12 +890,16 @@ If WITH-HOST is non-nil, return a list of (PARSER . HOST-PARSER)
instead. HOST-PARSER is the host parser which created the local
PARSER."
(let ((res nil))
;; Refer to (ref:local-parser-overlay) for more explanation of local
;; parser overlays.
(dolist (ov (overlays-in (or beg (point-min)) (or end (point-max))))
(when-let* ((parser (overlay-get ov 'treesit-parser))
(host-parser (overlay-get ov 'treesit-host-parser)))
(when (or (null language)
(eq (treesit-parser-language parser)
language))
(let ((parser (overlay-get ov 'treesit-parser))
(host-parser (overlay-get ov 'treesit-host-parser))
(local-p (overlay-get ov 'treesit-parser-local-p)))
(when (and parser host-parser local-p
(or (null language)
(eq (treesit-parser-language parser)
language)))
(push (if with-host (cons parser host-parser) parser) res))))
(nreverse res)))
@ -887,12 +909,16 @@ PARSER."
For every local parser overlay between BEG and END, if its
`treesit-parser-ov-timestamp' is smaller than MODIFIED-TICK, delete
it."
;; Refer to (ref:local-parser-overlay) for more explanation of local
;; parser overlays.
(dolist (ov (overlays-in beg end))
(when-let* ((ov-timestamp
(overlay-get ov 'treesit-parser-ov-timestamp)))
(when (< ov-timestamp modified-tick)
(when-let* ((local-parser (overlay-get ov 'treesit-parser)))
(treesit-parser-delete local-parser))
(let ((local-parser (overlay-get ov 'treesit-parser))
(local-p (overlay-get ov 'treesit-parser-local-p)))
(when (and local-p local-parser)
(treesit-parser-delete local-parser)))
(delete-overlay ov)))))
(defsubst treesit--parser-at-level (parsers level &optional include-null)
@ -910,7 +936,7 @@ is nil."
(declare-function treesit-parser-embed-level "treesit.c")
(defun treesit--update-ranges-non-local
( host-parser query embed-lang embed-level
( host-parser query embed-lang modified-tick embed-level
&optional beg end offset range-fn)
"Update range for non-local parsers between BEG and END under HOST-PARSER.
@ -923,6 +949,11 @@ those ranges. HOST-PARSER and QUERY must match.
EMBED-LANG is either a language symbol or a function that takes a node
and returns a language symbol.
When this function touches an overlay, it sets the
`treesit-parser-ov-timestamp' property of the overlay to MODIFIED-TICK.
This will help Emacs garbage-collect overlays that aren't in use
anymore.
EMBED-LEVEL is the embed level for the local parsers being created or
updated. When looking for existing local parsers, only look for parsers
of this level; when creating new local parsers, set their level to this
@ -954,6 +985,29 @@ Return updated parsers as a list."
(treesit-parser-list nil resolved-embed-lang)
embed-level 'include-null)))))
(when embed-parser
;; Lay an overlay over each range to mark the start & end of
;; it for other functions to access (e.g., outline wants to
;; know this). Refer to (ref:local-parser-overlay) for more
;; explanation of local parser overlays.
(dolist (range new-ranges)
(let ((has-existing-ov nil))
(setq has-existing-ov
(catch 'done
(dolist (ov (overlays-in (car range) (cdr range)))
(when (eq (overlay-get ov 'treesit-parser)
embed-parser)
(move-overlay ov (car range) (cdr range))
(overlay-put ov 'treesit-parser-ov-timestamp
modified-tick)
(throw 'done t)))))
(unless has-existing-ov
(let ((ov (make-overlay (car range) (cdr range))))
(overlay-put ov 'treesit-parser embed-parser)
(overlay-put ov 'treesit-parser-local-p nil)
(overlay-put ov 'treesit-host-parser host-parser)
(overlay-put ov 'treesit-parser-ov-timestamp
modified-tick)))))
;; Set ranges for the embed parser.
(let* ((old-ranges (treesit-parser-included-ranges
embed-parser))
(set-ranges (treesit--clip-ranges
@ -1025,7 +1079,8 @@ Return the created local parsers as a list."
embedded-parser))
(parser-level (treesit-parser-embed-level
embedded-parser)))
(when (and (eq parser-lang embedded-lang)
(when (and (overlay-get ov 'treesit-parser-local-p)
(eq parser-lang embedded-lang)
(eq embed-level parser-level))
(treesit-parser-set-included-ranges
embedded-parser `((,beg . ,end)))
@ -1035,12 +1090,15 @@ Return the created local parsers as a list."
(throw 'done embedded-parser)))))))
(if existing-local-parser
(push existing-local-parser touched-parsers)
;; Create overlay and local parser.
;; Create overlay and local parser. Refer to
;; (ref:local-parser-overlay) for more explanation of
;; local parser overlays.
(let ((embedded-parser (treesit-parser-create
embedded-lang nil t 'embedded))
(ov (make-overlay beg end nil nil t)))
(treesit-parser-set-embed-level embedded-parser embed-level)
(overlay-put ov 'treesit-parser embedded-parser)
(overlay-put ov 'treesit-parser-local-p t)
(overlay-put ov 'treesit-host-parser host-parser)
(overlay-put ov 'treesit-parser-ov-timestamp
modified-tick)
@ -1093,8 +1151,8 @@ Function range settings in SETTINGS are ignored."
(t (setq touched-parsers
(append touched-parsers
(treesit--update-ranges-non-local
host-parser query embed-lang embed-level
beg end offset range-fn))))))))
host-parser query embed-lang modified-tick
embed-level beg end offset range-fn))))))))
touched-parsers))
(defun treesit-update-ranges (&optional beg end)
@ -4274,8 +4332,10 @@ before calling this function."
;; Remove existing local parsers.
(dolist (ov (overlays-in (point-min) (point-max)))
(when-let* ((parser (overlay-get ov 'treesit-parser)))
(treesit-parser-delete parser)
(let ((parser (overlay-get ov 'treesit-parser))
(local-p (overlay-get ov 'treesit-parser-local-p)))
(when (and parser local-p)
(treesit-parser-delete parser))
(delete-overlay ov))))
;;; Helpers