diff options
author | João Távora <joaotavora@gmail.com> | 2023-02-24 14:48:01 +0000 |
---|---|---|
committer | João Távora <joaotavora@gmail.com> | 2023-02-24 14:48:01 +0000 |
commit | 55d29c9bacb6227bc8b3a6c0dd52c7085fe63aaf (patch) | |
tree | ddfedd061784100476fdd5b84ae489c8aa34e1b4 /lisp/progmodes/eglot.el | |
parent | 5db75ec7d30d5cf5dc610382ca25bd5a5c4f8fb6 (diff) | |
download | emacs-55d29c9bacb6227bc8b3a6c0dd52c7085fe63aaf.tar.gz emacs-55d29c9bacb6227bc8b3a6c0dd52c7085fe63aaf.tar.bz2 emacs-55d29c9bacb6227bc8b3a6c0dd52c7085fe63aaf.zip |
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.
Diffstat (limited to 'lisp/progmodes/eglot.el')
-rw-r--r-- | lisp/progmodes/eglot.el | 31 |
1 files 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." |