mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-08 07:20:28 -08:00
Fix tree-sitter local parser overlay cleanup routine
Sorry for sneaking in a sizable commit so late. But I just found out about this bug and it has to be fixed. Before this change, we weren't properly cleaning up overlays that store local parsers. And in the case of doxygen local parser in C files, the doxygen local parser overlay sometimes bleeds out of comments and into other code, and interferes with font-lock and indentation. This commit adds a cleanup function that'll cleanup any overlays that aren't being used. I tested with doxygen in C files and everything works smoothly now, including tricky tests like removing the ending "*/" of a doxygen comment and adding it back. The idea is simple, at the end of each call to (treesit-update-ranges BEG END), we remove any overlay within BEG and END that wasn't touched by the range setting code. * lisp/treesit.el (treesit--cleanup-local-range-overlays): New function. (treesit--update-ranges-local): Remove code for cleaning up zero-length overlays since we have the cleanup function now. (treesit-update-ranges): Wrap the function body inside a let form, which defines modified-tick; and add a call to treesit--cleanup-local-range-overlays at the very end.
This commit is contained in:
parent
aa6ed7f67e
commit
b8c05d73a1
1 changed files with 74 additions and 45 deletions
119
lisp/treesit.el
119
lisp/treesit.el
|
|
@ -700,32 +700,55 @@ PARSER."
|
||||||
(push (if with-host (cons parser host-parser) parser) res))))
|
(push (if with-host (cons parser host-parser) parser) res))))
|
||||||
(nreverse res)))
|
(nreverse res)))
|
||||||
|
|
||||||
|
(defun treesit--cleanup-local-range-overlays (modified-tick beg end)
|
||||||
|
"Cleanup overlays used to mark local parsers between BEG and END.
|
||||||
|
|
||||||
|
For every local parser overlay between BEG and END, if its
|
||||||
|
`treesit-parser-ov-timestamp' is smaller than MODIFIED-TICK, delete
|
||||||
|
it."
|
||||||
|
(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))
|
||||||
|
(delete-overlay ov)))))
|
||||||
|
|
||||||
(defun treesit--update-ranges-local
|
(defun treesit--update-ranges-local
|
||||||
(query embedded-lang &optional beg end)
|
(query embedded-lang modified-tick &optional beg end)
|
||||||
"Update range for local parsers between BEG and END.
|
"Update range for local parsers between BEG and END.
|
||||||
Use QUERY to get the ranges, and make sure each range has a local
|
Use QUERY to get the ranges, and make sure each range has a local
|
||||||
parser for EMBEDDED-LANG."
|
parser for EMBEDDED-LANG.
|
||||||
;; Clean up.
|
|
||||||
(dolist (ov (overlays-in (or beg (point-min)) (or end (point-max))))
|
The local parser is stored in an overlay, in the `treesit-parser'
|
||||||
(when-let ((parser (overlay-get ov 'treesit-parser)))
|
property, the host parser is stored in the `treesit-host-parser'
|
||||||
(when (eq (overlay-start ov) (overlay-end ov))
|
property.
|
||||||
(delete-overlay ov)
|
|
||||||
(treesit-parser-delete parser))))
|
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."
|
||||||
;; Update range.
|
;; Update range.
|
||||||
(let* ((host-lang (treesit-query-language query))
|
(let* ((host-lang (treesit-query-language query))
|
||||||
(host-parser (treesit-parser-create host-lang))
|
(host-parser (treesit-parser-create host-lang))
|
||||||
(ranges (treesit-query-range host-parser query beg end)))
|
(ranges (treesit-query-range host-parser query beg end)))
|
||||||
(pcase-dolist (`(,beg . ,end) ranges)
|
(pcase-dolist (`(,beg . ,end) ranges)
|
||||||
(let ((has-parser nil))
|
(let ((has-parser nil))
|
||||||
(dolist (ov (overlays-in beg end))
|
(setq
|
||||||
;; Update range of local parser.
|
has-parser
|
||||||
(let ((embedded-parser (overlay-get ov 'treesit-parser)))
|
(catch 'done
|
||||||
(when (and (treesit-parser-p embedded-parser)
|
(dolist (ov (overlays-in beg end) nil)
|
||||||
(eq (treesit-parser-language embedded-parser)
|
;; Update range of local parser.
|
||||||
embedded-lang))
|
(when-let* ((embedded-parser (overlay-get ov 'treesit-parser))
|
||||||
(treesit-parser-set-included-ranges
|
(parser-lang (treesit-parser-language
|
||||||
embedded-parser `((,beg . ,end)))
|
embedded-parser)))
|
||||||
(setq has-parser t))))
|
(when (eq parser-lang embedded-lang)
|
||||||
|
(treesit-parser-set-included-ranges
|
||||||
|
embedded-parser `((,beg . ,end)))
|
||||||
|
(move-overlay ov beg end)
|
||||||
|
(overlay-put ov 'treesit-parser-ov-timestamp
|
||||||
|
modified-tick)
|
||||||
|
(throw 'done t))))))
|
||||||
;; Create overlay and local parser.
|
;; Create overlay and local parser.
|
||||||
(when (not has-parser)
|
(when (not has-parser)
|
||||||
(let ((embedded-parser (treesit-parser-create
|
(let ((embedded-parser (treesit-parser-create
|
||||||
|
|
@ -733,6 +756,8 @@ parser for EMBEDDED-LANG."
|
||||||
(ov (make-overlay beg end nil nil t)))
|
(ov (make-overlay beg end nil nil t)))
|
||||||
(overlay-put ov 'treesit-parser embedded-parser)
|
(overlay-put ov 'treesit-parser embedded-parser)
|
||||||
(overlay-put ov 'treesit-host-parser host-parser)
|
(overlay-put ov 'treesit-host-parser host-parser)
|
||||||
|
(overlay-put ov 'treesit-parser-ov-timestamp
|
||||||
|
modified-tick)
|
||||||
(treesit-parser-set-included-ranges
|
(treesit-parser-set-included-ranges
|
||||||
embedded-parser `((,beg . ,end)))))))))
|
embedded-parser `((,beg . ,end)))))))))
|
||||||
|
|
||||||
|
|
@ -740,40 +765,44 @@ parser for EMBEDDED-LANG."
|
||||||
"Update the ranges for each language in the current buffer.
|
"Update the ranges for each language in the current buffer.
|
||||||
If BEG and END are non-nil, only update parser ranges in that
|
If BEG and END are non-nil, only update parser ranges in that
|
||||||
region."
|
region."
|
||||||
;; When updating ranges, we want to avoid querying the whole buffer
|
(let ((modified-tick (buffer-chars-modified-tick))
|
||||||
;; which could be slow in very large buffers. Instead, we only
|
(beg (or beg (point-min)))
|
||||||
;; query for nodes that intersect with the region between BEG and
|
(end (or end (point-max))))
|
||||||
;; END. Also, we only update the ranges intersecting BEG and END;
|
;; When updating ranges, we want to avoid querying the whole buffer
|
||||||
;; outside of that region we inherit old ranges.
|
;; which could be slow in very large buffers. Instead, we only
|
||||||
(dolist (setting treesit-range-settings)
|
;; query for nodes that intersect with the region between BEG and
|
||||||
(let ((query (nth 0 setting))
|
;; END. Also, we only update the ranges intersecting BEG and END;
|
||||||
(language (nth 1 setting))
|
;; outside of that region we inherit old ranges.
|
||||||
(local (nth 2 setting))
|
(dolist (setting treesit-range-settings)
|
||||||
(offset (nth 3 setting))
|
(let ((query (nth 0 setting))
|
||||||
(beg (or beg (point-min)))
|
(language (nth 1 setting))
|
||||||
(end (or end (point-max))))
|
(local (nth 2 setting))
|
||||||
(cond
|
(offset (nth 3 setting)))
|
||||||
((functionp query) (funcall query beg end))
|
(cond
|
||||||
(local
|
((functionp query) (funcall query beg end))
|
||||||
(treesit--update-ranges-local query language beg end))
|
(local
|
||||||
(t
|
(treesit--update-ranges-local
|
||||||
(let* ((host-lang (treesit-query-language query))
|
query language modified-tick beg end))
|
||||||
(parser (treesit-parser-create language))
|
(t
|
||||||
(old-ranges (treesit-parser-included-ranges parser))
|
(let* ((host-lang (treesit-query-language query))
|
||||||
(new-ranges (treesit-query-range
|
(parser (treesit-parser-create language))
|
||||||
host-lang query beg end offset))
|
(old-ranges (treesit-parser-included-ranges parser))
|
||||||
(set-ranges (treesit--clip-ranges
|
(new-ranges (treesit-query-range
|
||||||
(treesit--merge-ranges
|
host-lang query beg end offset))
|
||||||
old-ranges new-ranges beg end)
|
(set-ranges (treesit--clip-ranges
|
||||||
(point-min) (point-max))))
|
(treesit--merge-ranges
|
||||||
(dolist (parser (treesit-parser-list nil language))
|
old-ranges new-ranges beg end)
|
||||||
|
(point-min) (point-max))))
|
||||||
|
(dolist (parser (treesit-parser-list nil language))
|
||||||
(treesit-parser-set-included-ranges
|
(treesit-parser-set-included-ranges
|
||||||
parser (or set-ranges
|
parser (or set-ranges
|
||||||
;; When there's no range for the embedded
|
;; When there's no range for the embedded
|
||||||
;; language, set it's range to a dummy (1
|
;; language, set it's range to a dummy (1
|
||||||
;; . 1), otherwise it would be set to the
|
;; . 1), otherwise it would be set to the
|
||||||
;; whole buffer, which is not what we want.
|
;; whole buffer, which is not what we want.
|
||||||
`((,(point-min) . ,(point-min))))))))))))
|
`((,(point-min) . ,(point-min)))))))))))
|
||||||
|
|
||||||
|
(treesit--cleanup-local-range-overlays modified-tick beg end)))
|
||||||
|
|
||||||
(defun treesit-parser-range-on (parser beg &optional end)
|
(defun treesit-parser-range-on (parser beg &optional end)
|
||||||
"Check if PARSER's range covers the portion between BEG and END.
|
"Check if PARSER's range covers the portion between BEG and END.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue