]> git.eshelyaron.com Git - emacs.git/commitdiff
Eglot: improve diagnostic-reporting performance
authorJoão Távora <joaotavora@gmail.com>
Tue, 29 Apr 2025 11:20:51 +0000 (12:20 +0100)
committerEshel Yaron <me@eshelyaron.com>
Thu, 1 May 2025 05:39:54 +0000 (07:39 +0200)
After a change in the buffer has occured, it is often the case
that Flymake is quicker to ask for diagnostics than the server
is to supply them to us.  If we're still stuck with old outdated
diagnostics, don't forward them to Flymake, even if it eagerly
asks us for them.

* etc/EGLOT-NEWS (Changes in upcoming Eglot): Announce changes.

* lisp/progmodes/eglot.el
(eglot--diagnostics): Rework.
(eglot--report-to-flymake): Also take version.
(eglot-handle-notification textDocument/publishDiagnostics)
(eglot--managed-mode)
(eglot-flymake-backend): Tweak call to eglot--report-to-flymake.

(cherry picked from commit 7ae275f04c46e1724fafd8c8aa2f3eed5771df1c)

etc/EGLOT-NEWS
lisp/progmodes/eglot.el

index 7b53d5943ba00a8e3212ac8137b5fedf3182ce90..6505a6d85678878e96319c3641b061c2dce0d9c0 100644 (file)
@@ -47,6 +47,13 @@ The composition of Eglot's mode line can be fully customized by adding
 or removing symbols and strings from the customizable variable
 'eglot-mode-line-format'
 
+** Improved diagnostic-reporting performance and bugfixes (bug#77588)
+
+Eglot remembers the LSP document version to which diagonstics reported
+by the LSP server pertain.  This helps in skipping useless or harmful
+updates, avoiding flakiness with code actions and flickering overlays
+when the buffer is changed.
+
 \f
 * Changes in Eglot 1.18 (20/1/2025)
 
index 8baa662346963962a6479256a30d1cdb487b4ac7..5556dcb9cc6edfad6c52320c814a9d459d329496 100644 (file)
@@ -2221,7 +2221,7 @@ Use `eglot-managed-p' to determine if current buffer is managed.")
              do (set (make-local-variable var) saved-binding))
     (remove-function (local 'imenu-create-index-function) #'eglot-imenu)
     (when eglot--current-flymake-report-fn
-      (eglot--report-to-flymake nil)
+      (eglot--report-to-flymake nil nil)
       (setq eglot--current-flymake-report-fn nil))
     (run-hooks 'eglot-managed-mode-hook)
     (let ((server eglot--cached-server))
@@ -2261,7 +2261,10 @@ Use `eglot-managed-p' to determine if current buffer is managed.")
       (jsonrpc-error "No current JSON-RPC connection")))
 
 (defvar-local eglot--diagnostics nil
-  "Flymake diagnostics for this buffer.")
+  "A cons (DIAGNOSTICS . VERSION) for current buffer.
+DIAGNOSTICS is a list of Flymake diagnostics objects.  VERSION is the
+LSP Document version reported for DIAGNOSTICS (comparable to
+`eglot--versioned-identifier') or nil if server didn't bother.")
 
 (defvar revert-buffer-preserve-modes)
 (defun eglot--after-revert-hook ()
@@ -2733,8 +2736,11 @@ expensive cached value of `file-truename'.")
            initially
            (if (and version (/= version eglot--versioned-identifier))
                (cl-return))
-           (setq flymake-list-only-diagnostics
-                 (assoc-delete-all path flymake-list-only-diagnostics))
+           (setq
+            ;; if no explicit version received, assume it's current.
+            version eglot--versioned-identifier
+            flymake-list-only-diagnostics
+            (assoc-delete-all path flymake-list-only-diagnostics))
            for diag-spec across diagnostics
            collect (eglot--dbind ((Diagnostic) range code message severity source tags)
                        diag-spec
@@ -2758,9 +2764,9 @@ expensive cached value of `file-truename'.")
                            ;; starts on idle-timer (github#958)
                            (not (null flymake-no-changes-timeout))
                            eglot--current-flymake-report-fn)
-                          (eglot--report-to-flymake diags))
+                          (eglot--report-to-flymake diags version))
                          (t
-                          (setq eglot--diagnostics diags)))))
+                          (setq eglot--diagnostics (cons diags version))))))
       (cl-loop
        for diag-spec across diagnostics
        collect (eglot--dbind ((Diagnostic) code range message severity source) diag-spec
@@ -3152,22 +3158,24 @@ may be called multiple times (respecting the protocol of
 `flymake-diagnostic-functions')."
   (cond (eglot--managed-mode
          (setq eglot--current-flymake-report-fn report-fn)
-         (eglot--report-to-flymake eglot--diagnostics))
+         (eglot--report-to-flymake (car eglot--diagnostics)
+                                   (cdr eglot--diagnostics)))
         (t
          (funcall report-fn nil))))
 
-(defun eglot--report-to-flymake (diags)
+(defun eglot--report-to-flymake (diags version)
   "Internal helper for `eglot-flymake-backend'."
-  (save-restriction
-    (widen)
-    (funcall eglot--current-flymake-report-fn diags
-             ;; If the buffer hasn't changed since last
-             ;; call to the report function, flymake won't
-             ;; delete old diagnostics.  Using :region
-             ;; keyword forces flymake to delete
-             ;; them (github#159).
-             :region (cons (point-min) (point-max))))
-  (setq eglot--diagnostics diags))
+  (when (or (null version) (= version eglot--versioned-identifier))
+    (save-restriction
+      (widen)
+      (funcall eglot--current-flymake-report-fn diags
+               ;; If the buffer hasn't changed since last
+               ;; call to the report function, flymake won't
+               ;; delete old diagnostics.  Using :region
+               ;; keyword forces flymake to delete
+               ;; them (github#159).
+               :region (cons (point-min) (point-max)))))
+  (setq eglot--diagnostics (cons diags version)))
 
 (defun eglot-xref-backend () "Eglot xref backend." 'eglot)