emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

emacs-29 55d29c9bacb: Eglot: fix jit-lock inlay hint bugs


From: João Távora
Subject: emacs-29 55d29c9bacb: Eglot: fix jit-lock inlay hint bugs
Date: Fri, 24 Feb 2023 09:49:29 -0500 (EST)

branch: emacs-29
commit 55d29c9bacb6227bc8b3a6c0dd52c7085fe63aaf
Author: João Távora <joaotavora@gmail.com>
Commit: João Távora <joaotavora@gmail.com>

    Eglot: fix jit-lock inlay hint bugs
    
    One of the bugs was straightforward.  The timer function of
    eglot--update-hints must set the correct buffer.
    
    The other is much more odd.  When using Eglot on Emacs's own
    src/coding.c, the jit-lock code starts calling its jit-functions over
    and over again with the same sequence of arguments, like so:
    
    ======================================================================
    1 -> (eglot--update-hints 63551 65051)
    1 <- eglot--update-hints: [nil 25592 52026 4
    ======================================================================
    1 -> (eglot--update-hints 65051 66551)
    1 <- eglot--update-hints: [nil 25592 52026 4
    ======================================================================
    1 -> (eglot--update-hints-1 63551 66551)
    1 <- eglot--update-hints-1: nil
    ======================================================================
    1 -> (eglot--update-hints 63551 65051)
    1 <- eglot--update-hints: [nil 25592 52026 4
    ======================================================================
    1 -> (eglot--update-hints 65051 66551)
    1 <- eglot--update-hints: [nil 25592 52026 5
    ======================================================================
    1 -> (eglot--update-hints-1 63551 66551)
    1 <- eglot--update-hints-1: nil
    
    This continues forever at a very fast rate and saturates the LSP
    channel.
    
    At first I thought that it was because eglot--update-hints-1 is
    actually causing the buffer to be modified with overlays sometime in
    the future, but it is not so!  It seems that merely calling
    
       (goto-char (eglot--lsp-position-to-point position))
    
    (from the LSP request handler in eglot--update-hints-1) will cause
    this bug.
    
    * lisp/progmodes/eglot.el (eglot--update-hints): Fix bugs.
---
 lisp/progmodes/eglot.el | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index e20d209332d..2b9d44f84e6 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -3492,12 +3492,15 @@ If NOERROR, return predicate, else erroring function."
 (defvar-local eglot--outstanding-inlay-hints-region (cons nil nil)
   "Jit-lock-calculated (FROM . TO) region with potentially outdated hints")
 
+(defvar-local eglot--outstanding-inlay-hints-last-region nil)
+
 (defvar-local eglot--outstanding-inlay-regions-timer nil
   "Helper timer for `eglot--update-hints'")
 
 (defun eglot--update-hints (from to)
   "Jit-lock function for Eglot inlay hints."
   (cl-symbol-macrolet ((region eglot--outstanding-inlay-hints-region)
+                       (last-region eglot--outstanding-inlay-hints-last-region)
                        (timer eglot--outstanding-inlay-regions-timer))
     (setcar region (min (or (car region) (point-max)) from))
     (setcdr region (max (or (cdr region) (point-min)) to))
@@ -3513,12 +3516,28 @@ If NOERROR, return predicate, else erroring function."
     ;; not introducing any more delay over jit-lock's timers.
     (when (= jit-lock-context-unfontify-pos (point-max))
       (if timer (cancel-timer timer))
-      (setq timer (run-at-time
-                   0 nil
-                   (lambda ()
-                     (eglot--update-hints-1 (max (car region) (point-min))
-                                            (min (cdr region) (point-max)))
-                     (setq region (cons nil nil) timer nil)))))))
+      (let ((buf (current-buffer)))
+        (setq timer (run-at-time
+                     0 nil
+                     (lambda ()
+                       (eglot--when-live-buffer buf
+                         ;; HACK: In some pathological situations
+                         ;; (Emacs's own coding.c, for example),
+                         ;; jit-lock is calling `eglot--update-hints'
+                         ;; repeatedly with same sequence of
+                         ;; arguments, which leads to
+                         ;; `eglot--update-hints-1' being called with
+                         ;; the same region repeatedly.  This happens
+                         ;; even if the hint-painting code does
+                         ;; nothing else other than widen, narrow,
+                         ;; move point then restore these things.
+                         ;; Possible Emacs bug, but this fixes it.
+                         (unless (equal last-region region)
+                           (eglot--update-hints-1 (max (car region) 
(point-min))
+                                                  (min (cdr region) 
(point-max)))
+                           (setq last-region region))
+                         (setq region (cons nil nil)
+                               timer nil)))))))))
 
 (defun eglot--update-hints-1 (from to)
   "Do most work for `eglot--update-hints', including LSP request."



reply via email to

[Prev in Thread] Current Thread [Next in Thread]