]> git.eshelyaron.com Git - emacs.git/commitdiff
Ditch the async range update in tree-sitter (bug#78402)
authorYuan Fu <casouri@gmail.com>
Wed, 9 Jul 2025 06:15:58 +0000 (23:15 -0700)
committerEshel Yaron <me@eshelyaron.com>
Thu, 24 Jul 2025 07:52:18 +0000 (09:52 +0200)
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.

(cherry picked from commit 159ddd27ee6b6c8cd261c6ff495e66ddb6166685)

lisp/treesit.el

index a137160b382694e1f68a38c60b8441602fd746e4..6e1e339bfc92e01f586652c9c279b242da4296b6 100644 (file)
@@ -2016,7 +2016,7 @@ Because `pre-redisplay-functions' could be called multiple times
 during a single command loop, we use this variable to debounce
 calls to `treesit--pre-redisplay'.")
 
-(defun treesit--font-lock-mark-ranges-to-fontify (ranges _parser)
+(defun treesit--font-lock-mark-ranges-to-fontify (ranges)
   "A notifier that marks ranges that needs refontification.
 
 For RANGES and PARSER see `treesit-parser-add-notifier'.
@@ -2070,17 +2070,15 @@ parser."
     (car (treesit-parser-list))))
 
 (defun treesit--pre-redisplay (&rest _)
-  "Force a reparse on the primary parser and mark regions to be fontified.
-
-The actual work is carried out by
-`treesit--font-lock-mark-ranges-to-fontify', which see."
+  "Force a reparse on primary parser and mark regions to be fontified."
   (unless (eq treesit--pre-redisplay-tick (buffer-chars-modified-tick))
     (when treesit-primary-parser
-      ;; Force a reparse on the primary parser, if everything is setup
-      ;; correctly, the parser should call
-      ;; `treesit--font-lock-mark-ranges-to-fontify' (which should be a
-      ;; notifier function of the primary parser).
-      (treesit-parser-root-node treesit-primary-parser))
+      ;; Force a reparse on the primary parser and update embedded
+      ;; parser ranges in the changed ranges.
+      (let ((affected-ranges (treesit-parser-changed-regions
+                              treesit-primary-parser)))
+        (when affected-ranges
+          (treesit--font-lock-mark-ranges-to-fontify affected-ranges))))
 
     (setq treesit--pre-redisplay-tick (buffer-chars-modified-tick))))
 
@@ -4065,6 +4063,7 @@ this variable takes priority.")
   "Search for the next outline heading in the syntax tree.
 For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in
 `outline-search-function'."
+  (treesit--pre-redisplay)
   (if looking-at
       (when (treesit-outline--at-point) (pos-bol))
 
@@ -4151,11 +4150,6 @@ For BOUND, MOVE, BACKWARD, LOOKING-AT, see the descriptions in
 
     level))
 
-(defun treesit--after-change (beg end _len)
-  "Force updating the ranges in BEG...END.
-Expected to be called after each text change."
-  (treesit-update-ranges beg end))
-
 ;;; Hideshow mode
 
 (defun treesit-hs-block-end ()
@@ -4367,9 +4361,6 @@ before calling this function."
                     . treesit-font-lock-fontify-region)))
     (treesit-font-lock-recompute-features)
     (add-hook 'pre-redisplay-functions #'treesit--pre-redisplay 0 t)
-    (when treesit-primary-parser
-      (treesit-parser-add-notifier
-       treesit-primary-parser #'treesit--font-lock-mark-ranges-to-fontify))
     (treesit-validate-font-lock-rules treesit-font-lock-settings))
   ;; Syntax
   (add-hook 'syntax-propertize-extend-region-functions
@@ -4455,8 +4446,7 @@ before calling this function."
       (setq treesit-outline-predicate
             #'treesit-outline-predicate--from-imenu))
     (setq-local outline-search-function #'treesit-outline-search
-                outline-level #'treesit-outline-level)
-    (add-hook 'outline-after-change-functions #'treesit--after-change nil t))
+                outline-level #'treesit-outline-level))
 
   ;; Remove existing local parsers.
   (dolist (ov (overlays-in (point-min) (point-max)))