Subject: Re: bug#78402: treesit after-change-functions

From: Stefan Monnier <[email-masked]>
Date: Wed, 21 May 2025 13:47:09 -0400

Yuan Fu [2025-05-21 00:21:39] wrote:
>> On May 20, 2025, at 11:35 PM, Juri Linkov <[email-masked]> wrote:
>>>>> Btw, what’s the issue we’re trying to fix here? If some lisp want to have
>>>>> up-to-date ranges, it can just call treesit--pre-redisplay or
>>>>> treesit-update-ranges. Why do we get the node-outdated error?
>>>> 
>>>> Hmm... so what you're saying is that we should do something like the
>>>> patch below?
>>> 
>>> Maybe Yuan could confirm if `treesit-update-ranges`
>>> is already optimized for frequent calls on overlapping ranges
>>> that don't need updating and where 'beg' and 'end' fall back
>>> to (point-min) and (point-max).
>> 
>> It seems not optimized since in my tests outline-minor-mode
>> is much slower with this change.
>
> Hey, sorry for the delay. I've been busy with real life. As you said,
> `treesit-update-ranges` doesn’t have any optimization for repeated
> calls.

That seems like a potential problem for existing uses of that function
(we're just "lucky" that they're expected to be used once-per-command).

> OTOH, `treesit--pre-redisplay` has optimization for repeated calls
> while buffer content doesn’t change. Maybe the patch can use that instead?

Like in the patch below?

That sounds OK, but then `treesit--pre-redisplay` needs to be renamed to
something like `treesit--update` or `treesit--sync`, and maybe
`treesit-font-lock-fontify-region`, `treesit-indent`, and
`treesit-indent-region` should use that function instead of
`treesit-update-ranges`?

        Stefan

diff --git a/lisp/treesit.el b/lisp/treesit.el
index 5df8eb70cbf..89edc700f75 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -2615,6 +2615,7 @@ treesit--indent-1
 
 (defun treesit-indent ()
   "Indent according to the result of `treesit-indent-function'."
+  ;; FIXME: Use `prog-indent-line'?
   (treesit-update-ranges (line-beginning-position)
                          (line-end-position))
   ;; We don't return 'noindent even if no rules match, because
@@ -4064,6 +4065,7 @@ treesit-outline-search
   "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))
 
@@ -4150,11 +4152,6 @@ treesit-outline-level
 
     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 ()
@@ -4452,8 +4449,7 @@ treesit-major-mode-setup
       (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)))