From b60b01f4183eef05f3c48891ba53201230154963 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Tue, 4 Feb 2025 14:43:48 +0000 Subject: [PATCH] Eglot: protect against nil jit-lock-context-unfontify-pos * lisp/progmodes/eglot.el (eglot-shutdown, eglot--update-hints): Rework. Github-reference: https://github.com/joaotavora/eglot/pull/1370 (cherry picked from commit b685b21a33423018f3da7695919a4d1b1ef2c9cd) --- lisp/progmodes/eglot.el | 70 ++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index f22add70a76..1329903d647 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -4320,47 +4320,47 @@ If NOERROR, return predicate, else erroring function." (defvar-local eglot--outstanding-inlay-regions-timer nil "Helper timer for `eglot--update-hints'.") -(defun eglot--update-hints (from to) +(cl-defun eglot--update-hints (from to) "Jit-lock function for Eglot inlay hints." + ;; XXX: We're relying on knowledge of jit-lock internals here. + ;; Comparing `jit-lock-context-unfontify-pos' (if non-nil) to + ;; `point-max' tells us whether this call to `jit-lock-functions' + ;; happens after `jit-lock-context-timer' has just run. + (when (and jit-lock-context-unfontify-pos + (/= jit-lock-context-unfontify-pos (point-max))) + (cl-return-from eglot--update-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)) - ;; HACK: We're relying on knowledge of jit-lock internals here. The - ;; condition comparing `jit-lock-context-unfontify-pos' to - ;; `point-max' is a heuristic for telling whether this call to - ;; `jit-lock-functions' happens after `jit-lock-context-timer' has - ;; just run. Only after this delay should we start the smoothing - ;; timer that will eventually call `eglot--update-hints-1' with the - ;; coalesced region. I wish we didn't need the timer, but sometimes - ;; a lot of "non-contextual" calls come in all at once and do verify - ;; the condition. Notice it is a 0 second timer though, so we're - ;; not introducing any more delay over jit-lock's timers. - (when (= jit-lock-context-unfontify-pos (point-max)) - (if timer (cancel-timer timer)) - (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))))))))) + ;; XXX: Then there is a smoothing timer. I wish we didn't need it, + ;; but sometimes a lot of calls come in all at once and do make it + ;; past the check above. Notice it is a 0 second timer though, so + ;; we're not introducing any more delay over jit-lock's timers. + (when timer (cancel-timer timer)) + (setq timer (run-at-time + 0 nil + (lambda (buf) + (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))) + (current-buffer))))) (defun eglot--update-hints-1 (from to) "Do most work for `eglot--update-hints', including LSP request." -- 2.39.5