From: Yuan Fu Date: Wed, 9 Jul 2025 06:15:58 +0000 (-0700) Subject: Ditch the async range update in tree-sitter (bug#78402) X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=e48d5744c3e8d0b10f0e5eebf49a6e10a54b9153;p=emacs.git 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. (cherry picked from commit 159ddd27ee6b6c8cd261c6ff495e66ddb6166685) --- diff --git a/lisp/treesit.el b/lisp/treesit.el index a137160b382..6e1e339bfc9 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -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)))