all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Dmitry Gutov <dmitry@gutov.dev>
To: Yuan Fu <casouri@gmail.com>, Eli Zaretskii <eliz@gnu.org>,
	Stefan Monnier <monnier@IRO.UMontreal.CA>
Cc: 66732@debbugs.gnu.org, dominik@honnef.co
Subject: bug#66732: tree-sitter fontification doesn't update multi-line syntax reliably
Date: Mon, 11 Dec 2023 17:53:11 +0200	[thread overview]
Message-ID: <50920549-006c-0153-2471-02e41a3dada7@gutov.dev> (raw)
In-Reply-To: <2ce274aa-6d01-4d0a-b10c-07f821343fed@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 3458 bytes --]

On 11/12/2023 06:16, Yuan Fu wrote:
> The way we achieves this is thought parser notifiers. When tree-sitter 
> parser reparses, it notifies us of which part of the buffer was affected 
> by the reparse. For font-lock, we have this font-lock notifier that 
> marks the affected buffer region as "not fontified", so redisplay will 
> refontify those areas.
> 
> (defun treesit--font-lock-notifier (ranges parser)
>    "Ensures updated parts of the parse-tree are refontified.
> RANGES is a list of (BEG . END) ranges, PARSER is the tree-sitter
> parser notifying of the change."
>    (with-current-buffer (treesit-parser-buffer parser)
>      (dolist (range ranges)
>        (when treesit--font-lock-verbose
>          (message "Notifier received range: %s-%s"
>                   (car range) (cdr range)))
>        (with-silent-modifications
>          (put-text-property (car range) (cdr range) 'fontified nil)))))
> 
> This notifier function will be called during redisplay [1]. I suspect 
> that because of this timing, redisplay doesn't refontify the marked 
> region immediately. So I added a timer, I think that ensures we mark the 
> affected region in the next command loop?
> 
> (defun treesit--font-lock-notifier (ranges parser)
>    "Ensures updated parts of the parse-tree are refontified.
> RANGES is a list of (BEG . END) ranges, PARSER is the tree-sitter
> parser notifying of the change."
>    (with-current-buffer (treesit-parser-buffer parser)
>      (dolist (range ranges)
>        (when treesit--font-lock-verbose
>          (message "Notifier received range: %s-%s"
>                   (car range) (cdr range)))
>        (run-with-timer
>         0 nil
>         (lambda ()
>           (with-silent-modifications
>             (put-text-property (car range) (cdr range)
>                                'fontified nil)))))))
> 
> This seems to work. Eli, do you see any problem using run-with-timer 
> this way? What's the correct way to mark some region unfontified?
> 
> [1] The chain of events if roughly: user types the last "/" -> redisplay 
> -> fontify that character -> access parser -> parser reparses -> calls 
> notifier.

Note that it's not just font-lock. syntax-propertize has the same 
problem (I've described it in https://debbugs.gnu.org/67262#23).

And a timer wouldn't help because syntax-ppss needs to have up-to-date 
information whenever it's called, not later.

Here's a draft solution based on *-extend-region-functions, attached.

Alas, while it works fine in python-ts-mode (for both syntax and 
font-lock), making it behave better than python-mode, in c-ts-mode it 
doesn't quite have the same effect: when you backspace over the closing 
"/", the highlighting is properly updated only after you make the next 
edit (any edit), or select another window. I'm not sure, though, if it's 
due to my own problems with Emacs's failure to redisplay (reported 
elsewhere), so more testing is welcome.

But that might also be related to the use of 
c-ts-mode--emacs-set-ranges: printing a backtrace calls inside 
treesit--font-lock-notifier shows that the last notification comes also 
during font-lock but after treesit--font-lock-extend-region, inside 
c-ts-mode--emacs-set-ranges. I don't quite understand this design where 
the ranges are applied inside the font-lock code.

[-- Attachment #2: treesit--notifier-context.diff --]
[-- Type: text/x-patch, Size: 3485 bytes --]

diff --git a/lisp/treesit.el b/lisp/treesit.el
index 962a6fc3cf8..f5d30be52bb 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -1075,17 +1075,56 @@ treesit-font-lock-fontify-region
                                face (treesit-node-type node))))))))))))
   `(jit-lock-bounds ,start . ,end))
 
+(defvar treesit--notifier-ranges nil)
+(defvar treesit--notifier-context 'direct)
+
 (defun treesit--font-lock-notifier (ranges parser)
   "Ensures updated parts of the parse-tree are refontified.
+And re-syntax-propertized.
 RANGES is a list of (BEG . END) ranges, PARSER is the tree-sitter
 parser notifying of the change."
   (with-current-buffer (treesit-parser-buffer parser)
-    (dolist (range ranges)
-      (when treesit--font-lock-verbose
-        (message "Notifier received range: %s-%s"
-                 (car range) (cdr range)))
-      (with-silent-modifications
-        (put-text-property (car range) (cdr range) 'fontified nil)))))
+    (unless (eq treesit--notifier-context 'direct)
+      (setq treesit--notifier-ranges (append treesit--notifier-ranges ranges)))
+    (message "notifier context %s ranges %s" treesit--notifier-context ranges)
+    (when ranges
+      (unless (eq treesit--notifier-context 'font-lock)
+        (when treesit-font-lock-settings
+          (with-silent-modifications
+            (dolist (range ranges)
+              (when treesit--font-lock-verbose
+                (message "Notifier received range: %s-%s"
+                         (car range) (cdr range)))
+              ;; (backtrace)
+              (put-text-property (car range) (cdr range) 'fontified nil)))))
+      (unless (eq treesit--notifier-context 'syntax)
+        (when syntax-propertize-function
+          (syntax-ppss-flush-cache (cl-loop for r in ranges
+                                            minimize (car r))))))))
+
+(defun treesit--syntax-extend-region (beg end &optional font-lock-p)
+  (let (treesit--notifier-ranges
+        (treesit--notifier-context (if font-lock-p 'font-lock 'syntax)))
+    (treesit-buffer-root-node (treesit-language-at (point)))
+    (when treesit--notifier-ranges
+      ;; (message "some ranges received %S while beg end %s %s" treesit--notifier-ranges beg end)
+      ;; Some updates received from `treesit_ensure_parsed'.
+      (cl-loop for range in treesit--notifier-ranges
+               if (or (<= (car range) beg (cdr range))
+                      (<= (car range) end (cdr range)))
+               return (cons (min (car range) beg)
+                            (max (cdr range) end))))))
+
+(defun treesit--font-lock-extend-region ()
+  (defvar font-lock-beg)
+  (defvar font-lock-end)
+  ;; (message "called treesit--font-lock-extend-region")
+  (let ((new (treesit--syntax-extend-region font-lock-beg font-lock-end t)))
+    (when new
+      ;; (message "eeextended to %S" new)
+      (setq font-lock-beg (car new))
+      (setq font-lock-end (cdr new))
+      t)))
 
 ;;; Indent
 
@@ -2391,7 +2430,9 @@ treesit-major-mode-setup
     (treesit-font-lock-recompute-features)
     (dolist (parser (treesit-parser-list))
       (treesit-parser-add-notifier
-       parser #'treesit--font-lock-notifier)))
+       parser #'treesit--font-lock-notifier))
+    (push #'treesit--font-lock-extend-region font-lock-extend-region-functions)
+    (push #'treesit--syntax-extend-region syntax-propertize-extend-region-functions))
   ;; Indent.
   (when treesit-simple-indent-rules
     (setq-local treesit-simple-indent-rules

  parent reply	other threads:[~2023-12-11 15:53 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-24 14:22 bug#66732: tree-sitter fontification doesn't update multi-line syntax reliably Dominik Honnef
2023-10-24 23:15 ` Dmitry Gutov
2023-10-29 12:22   ` Eli Zaretskii
2023-11-18  8:37     ` Eli Zaretskii
2023-12-11  4:16       ` Yuan Fu
2023-12-11 12:05         ` Eli Zaretskii
2023-12-11 14:35           ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-11 15:53         ` Dmitry Gutov [this message]
2023-12-12  7:50           ` Yuan Fu
2023-12-12 12:43             ` Dmitry Gutov
2023-12-13  3:28               ` Yuan Fu
2023-12-13  3:45                 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-13  7:12                   ` Yuan Fu
2023-12-13 14:30                     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-14  1:43                 ` Dmitry Gutov
2023-12-14  8:29                   ` Yuan Fu
2023-12-15  1:01                     ` Dmitry Gutov
2023-12-15  7:12                       ` Yuan Fu
2023-12-16  5:56                         ` Yuan Fu
2023-12-16 15:22                           ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-16 17:11                             ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-16 17:23                               ` Dmitry Gutov
2023-12-16 17:43                                 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-16 19:18                                   ` Yuan Fu
2023-12-16 19:57                                     ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-16 23:09                                     ` Dmitry Gutov
2023-12-17  1:16                                       ` Yuan Fu
2023-12-17 18:32                                         ` Dmitry Gutov
2023-12-19  3:12                                           ` Yuan Fu
2023-12-20  1:52                                             ` Dmitry Gutov
2023-12-20  5:43                                               ` Yuan Fu
2023-12-20 11:31                                                 ` Dmitry Gutov
2023-12-16 23:02                                   ` Dmitry Gutov
2023-12-20  2:01                               ` Dmitry Gutov
2023-12-20  3:08                                 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-16 22:56                           ` Dmitry Gutov
2023-12-18 18:27                             ` Dmitry Gutov
2023-12-18 19:12                               ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-18 19:33                                 ` Eli Zaretskii
2023-12-18 23:10                                   ` Dmitry Gutov
2023-12-19  3:22                                     ` Eli Zaretskii
2023-12-19  3:40                                       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-19 12:41                                         ` Eli Zaretskii
2023-12-19 12:44                                           ` Dmitry Gutov
2023-12-20 20:50                                           ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-23 10:17                                             ` Eli Zaretskii
2023-12-23 18:02                                               ` Yuan Fu
2023-12-23 20:46                                                 ` Dmitry Gutov
2023-12-23 20:51                                                   ` Dmitry Gutov
2023-12-23 23:07                                                     ` Yuan Fu
2023-12-24  2:10                                                       ` Dmitry Gutov
2023-12-24  3:02                                                         ` Yuan Fu
2023-12-23 20:55                                               ` Dmitry Gutov
2023-12-24  6:03                                                 ` Eli Zaretskii
2024-02-08  1:40                                                   ` Yuan Fu
2023-12-18 23:08                                 ` Dmitry Gutov
2023-12-20 20:50                                   ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-12-12 15:34             ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=50920549-006c-0153-2471-02e41a3dada7@gutov.dev \
    --to=dmitry@gutov.dev \
    --cc=66732@debbugs.gnu.org \
    --cc=casouri@gmail.com \
    --cc=dominik@honnef.co \
    --cc=eliz@gnu.org \
    --cc=monnier@IRO.UMontreal.CA \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.