summaryrefslogtreecommitdiff
path: root/lisp/progmodes/eglot.el
diff options
context:
space:
mode:
authorJoão Távora <joaotavora@gmail.com>2023-02-24 14:48:01 +0000
committerJoão Távora <joaotavora@gmail.com>2023-02-24 14:48:01 +0000
commit55d29c9bacb6227bc8b3a6c0dd52c7085fe63aaf (patch)
treeddfedd061784100476fdd5b84ae489c8aa34e1b4 /lisp/progmodes/eglot.el
parent5db75ec7d30d5cf5dc610382ca25bd5a5c4f8fb6 (diff)
downloademacs-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.el31
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."