GNU bug report logs - #77256
Treesit language-at-point

Previous Next

Package: emacs;

Reported by: Juri Linkov <juri <at> linkov.net>

Date: Tue, 25 Mar 2025 18:44:02 UTC

Severity: normal

Fixed in version 31.0.50

Done: Juri Linkov <juri <at> linkov.net>

Bug is archived. No further changes may be made.

Full log


View this message in rfc822 format

From: Juri Linkov <juri <at> linkov.net>
To: Yuan Fu <casouri <at> gmail.com>
Cc: 77256 <at> debbugs.gnu.org, Vincenzo Pupillo <v.pupillo <at> gmail.com>
Subject: bug#77256: Treesit language-at-point
Date: Sun, 06 Apr 2025 09:51:27 +0300
>>> Unfortunately, I discovered that it causes treesit-node-outdated errors.
>>> 
>>> This is because overlays are updated by 'pre-redisplay-functions'
>>> later than outline-minor-mode uses them by 'after-change-functions',
>>> since 'after-change-functions' runs before 'pre-redisplay-functions'.
>> 
>> This fixes the problem:
>> 
>>   (defun outline--fix-buttons-after-change (beg end _len)
>>  +  (when (fboundp 'treesit-update-ranges)
>>  +    (treesit-update-ranges beg end))
>>     ;; Handle whole lines
>>     (save-excursion (goto-char beg) (setq beg (pos-bol)))
>>     (save-excursion (goto-char end) (setq end (pos-eol)))
>
> Using treesit-update-ranges is the right approach.  But is there no
> better place to put it?  I assume there should be a function that
> involves tree-sitter, in front of which you can put this call.

'treesit-outline-search' has no information about the positions of
the beginning and end of the range of changed text.  Only
'after-change-functions' has access to this information.

So what we could do is this, and I've tested that this approach works:

#+begin_src diff
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 07861603244..c77b28c4647 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -4044,6 +4112,10 @@ treesit-outline-level
 
     level))
 
+(defun treesit--after-change (beg end _len)
+  "Force updating the ranges after each text change."
+  (treesit-update-ranges beg end))
+
 ;;; Hideshow mode
 
 (defun treesit-hs-block-end ()
@@ -4338,7 +4410,16 @@ 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))
+                outline-level #'treesit-outline-level)
+    (add-hook 'outline-minor-mode-hook
+              (lambda ()
+                (if (bound-and-true-p outline-minor-mode)
+                    (add-hook 'after-change-functions
+                              #'treesit--after-change
+                              0 t)
+                  (remove-hook 'after-change-functions
+                               #'treesit--after-change t)))
+              nil t))
 
   ;; Remove existing local parsers.
   (dolist (ov (overlays-in (point-min) (point-max)))
#+end_src

>> But can we do better?  I see that 'treesit-major-mode-setup'
>> adds the notifier
>> 
>>      (treesit-parser-add-notifier
>>       treesit-primary-parser #'treesit--font-lock-mark-ranges-to-fontify)
>> 
>> to 'after_change_functions'.  But why the treesit
>> 'after_change_functions' notifier is called after the
>> 'after-change-functions' hook?  Can we change their order?
>
> Because we parse lazily. And I should’ve called them after-parse functions
> in the docstring (good thing I named them notifiers, not
> after-change-functions)—they get called after the parser re-parses, but the
> parser doesn’t have to re-parse immediately after a buffer change. It only
> re-parses when some Lisp asks for a node from the parse tree.
>
> In practice, that usually happens in pre-redisplay-function where we set
> ranges for embedded parsers, which causes the primary parser to re-parse
> and return a root node for querying. As a (intended) side-effect, the
> notifiers get called and mark relevant regions to be re-fontified.

Would it be possible on the first attempt of Lisp code to access a node
to detect such a situation that there are some outdated nodes and
to update their ranges automatically?




This bug report was last modified 91 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.