diff options
| author | Yuan Fu | 2025-07-08 23:15:58 -0700 |
|---|---|---|
| committer | Yuan Fu | 2025-07-08 23:39:39 -0700 |
| commit | 159ddd27ee6b6c8cd261c6ff495e66ddb6166685 (patch) | |
| tree | e30d2362d09e862144f679348c21302d73dc61ab | |
| parent | 564b947745b2a685edcd93eb8f0d8825352030b8 (diff) | |
| download | emacs-159ddd27ee6b6c8cd261c6ff495e66ddb6166685.tar.gz emacs-159ddd27ee6b6c8cd261c6ff495e66ddb6166685.zip | |
Ditch the async range update in tree-sitter (bug#78402)
Right now in treesit-outline-search -> treesit-navigate-thing, a
freshly created tree-sitter node becomes outdated within the
function. I'm not sure _exactly_ how it happend, but it might
look like this: we first get a node from, say, html parser, then
get another node from, say, liquid parser. Creating the node
from liquid parser causes a reparse which updated the range of
the html parser, which rendered the html node outdated.
There're several problems with the current design, let's start
with the most obvious one: we add
treesit--font-lock-mark-ranges-to-fontify as a notifier of the
primar parser in treesit-major-mode-setup. Now, if a ts major
mode inherits another major mode, treesit-major-mode-setup will
be called twice, once in the parent mode and once in the child
node, and two parsers will have the notifier. But
treesit--font-lock-mark-ranges-to-fontify is designed to run
only once.
I believe this bug, together with some mysterious async
execution order, led to the problems we saw in the bug report.
My solution is to just make everything synchronous.
So I added treesit-parser-changed-regions, and modified
treesit--font-lock-mark-ranges-to-fontify to use it. Now we
don't need to add the notifier to the primary parser anymore.
I also applied the tree-sitter-outline change we discussed in
the bug report. (Change to treesit-outline-search, and remove
treesit--after-change.)
* lisp/treesit.el:
(treesit--font-lock-mark-ranges-to-fontify): Remove the unused
PARSER arg.
(treesit--pre-redisplay): Make use of
treesit-parser-changed-regions.
(treesit-outline-search): Call treesit--pre-redisplay in the
beginning.
(treesit--after-change): Remove function.
(treesit-major-mode-setup): Don't add notifier to primary parser.
| -rw-r--r-- | lisp/treesit.el | 30 |
1 files changed, 10 insertions, 20 deletions
diff --git a/lisp/treesit.el b/lisp/treesit.el index 893e21ec0c5..aef61d2e68b 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el | |||
| @@ -2020,7 +2020,7 @@ Because `pre-redisplay-functions' could be called multiple times | |||
| 2020 | during a single command loop, we use this variable to debounce | 2020 | during a single command loop, we use this variable to debounce |
| 2021 | calls to `treesit--pre-redisplay'.") | 2021 | calls to `treesit--pre-redisplay'.") |
| 2022 | 2022 | ||
| 2023 | (defun treesit--font-lock-mark-ranges-to-fontify (ranges _parser) | 2023 | (defun treesit--font-lock-mark-ranges-to-fontify (ranges) |
| 2024 | "A notifier that marks ranges that needs refontification. | 2024 | "A notifier that marks ranges that needs refontification. |
| 2025 | 2025 | ||
| 2026 | For RANGES and PARSER see `treesit-parser-add-notifier'. | 2026 | For RANGES and PARSER see `treesit-parser-add-notifier'. |
| @@ -2074,17 +2074,15 @@ parser." | |||
| 2074 | (car (treesit-parser-list)))) | 2074 | (car (treesit-parser-list)))) |
| 2075 | 2075 | ||
| 2076 | (defun treesit--pre-redisplay (&rest _) | 2076 | (defun treesit--pre-redisplay (&rest _) |
| 2077 | "Force a reparse on the primary parser and mark regions to be fontified. | 2077 | "Force a reparse on primary parser and mark regions to be fontified." |
| 2078 | |||
| 2079 | The actual work is carried out by | ||
| 2080 | `treesit--font-lock-mark-ranges-to-fontify', which see." | ||
| 2081 | (unless (eq treesit--pre-redisplay-tick (buffer-chars-modified-tick)) | 2078 | (unless (eq treesit--pre-redisplay-tick (buffer-chars-modified-tick)) |
| 2082 | (when treesit-primary-parser | 2079 | (when treesit-primary-parser |
| 2083 | ;; Force a reparse on the primary parser, if everything is setup | 2080 | ;; Force a reparse on the primary parser and update embedded |
| 2084 | ;; correctly, the parser should call | 2081 | ;; parser ranges in the changed ranges. |
| 2085 | ;; `treesit--font-lock-mark-ranges-to-fontify' (which should be a | 2082 | (let ((affected-ranges (treesit-parser-changed-regions |
| 2086 | ;; notifier function of the primary parser). | 2083 | treesit-primary-parser))) |
| 2087 | (treesit-parser-root-node treesit-primary-parser)) | 2084 | (when affected-ranges |
| 2085 | (treesit--font-lock-mark-ranges-to-fontify affected-ranges)))) | ||
| 2088 | 2086 | ||
| 2089 | (setq treesit--pre-redisplay-tick (buffer-chars-modified-tick)))) | 2087 | (setq treesit--pre-redisplay-tick (buffer-chars-modified-tick)))) |
| 2090 | 2088 | ||
| @@ -4072,6 +4070,7 @@ this variable takes priority.") | |||
| 4072 | "Search for the next outline heading in the syntax tree. | 4070 | "Search for the next outline heading in the syntax tree. |
| 4073 | For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in | 4071 | For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in |
| 4074 | `outline-search-function'." | 4072 | `outline-search-function'." |
| 4073 | (treesit--pre-redisplay) | ||
| 4075 | (if looking-at | 4074 | (if looking-at |
| 4076 | (when (treesit-outline--at-point) (pos-bol)) | 4075 | (when (treesit-outline--at-point) (pos-bol)) |
| 4077 | 4076 | ||
| @@ -4158,11 +4157,6 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in | |||
| 4158 | 4157 | ||
| 4159 | level)) | 4158 | level)) |
| 4160 | 4159 | ||
| 4161 | (defun treesit--after-change (beg end _len) | ||
| 4162 | "Force updating the ranges in BEG...END. | ||
| 4163 | Expected to be called after each text change." | ||
| 4164 | (treesit-update-ranges beg end)) | ||
| 4165 | |||
| 4166 | ;;; Hideshow mode | 4160 | ;;; Hideshow mode |
| 4167 | 4161 | ||
| 4168 | (defun treesit-hs-block-end () | 4162 | (defun treesit-hs-block-end () |
| @@ -4374,9 +4368,6 @@ before calling this function." | |||
| 4374 | . treesit-font-lock-fontify-region))) | 4368 | . treesit-font-lock-fontify-region))) |
| 4375 | (treesit-font-lock-recompute-features) | 4369 | (treesit-font-lock-recompute-features) |
| 4376 | (add-hook 'pre-redisplay-functions #'treesit--pre-redisplay 0 t) | 4370 | (add-hook 'pre-redisplay-functions #'treesit--pre-redisplay 0 t) |
| 4377 | (when treesit-primary-parser | ||
| 4378 | (treesit-parser-add-notifier | ||
| 4379 | treesit-primary-parser #'treesit--font-lock-mark-ranges-to-fontify)) | ||
| 4380 | (treesit-validate-font-lock-rules treesit-font-lock-settings)) | 4371 | (treesit-validate-font-lock-rules treesit-font-lock-settings)) |
| 4381 | ;; Syntax | 4372 | ;; Syntax |
| 4382 | (add-hook 'syntax-propertize-extend-region-functions | 4373 | (add-hook 'syntax-propertize-extend-region-functions |
| @@ -4462,8 +4453,7 @@ before calling this function." | |||
| 4462 | (setq treesit-outline-predicate | 4453 | (setq treesit-outline-predicate |
| 4463 | #'treesit-outline-predicate--from-imenu)) | 4454 | #'treesit-outline-predicate--from-imenu)) |
| 4464 | (setq-local outline-search-function #'treesit-outline-search | 4455 | (setq-local outline-search-function #'treesit-outline-search |
| 4465 | outline-level #'treesit-outline-level) | 4456 | outline-level #'treesit-outline-level)) |
| 4466 | (add-hook 'outline-after-change-functions #'treesit--after-change nil t)) | ||
| 4467 | 4457 | ||
| 4468 | ;; Remove existing local parsers. | 4458 | ;; Remove existing local parsers. |
| 4469 | (dolist (ov (overlays-in (point-min) (point-max))) | 4459 | (dolist (ov (overlays-in (point-min) (point-max))) |