From e149a4c007b2b9b85e5ee58bd1526fcabd97661a Mon Sep 17 00:00:00 2001 From: Theodor Thornhill Date: Fri, 19 Apr 2024 20:40:25 +0200 Subject: [PATCH] Make publishDiagnostics faster by using cached variable * lisp/progmodes/eglot.el (eglot--cached-tdi): Move variable. (eglot-handle-notification): Expose 'server' and search through managed buffers for a cached textDocumentIdentifier, which has a file-truename resolved path. * test/lisp/progmodes/eglot-tests.el (eglot-test-basic-symlink): Add regression test for symlink behavior (cherry picked from commit 49ef173b0287e17273e4476df16dca5f2196b11c) --- lisp/progmodes/eglot.el | 21 +++++++++++++++------ test/lisp/progmodes/eglot-tests.el | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 29a274fb338..b6cdb2fd51a 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -2382,8 +2382,11 @@ still unanswered LSP requests to the server\n"))) (lambda () (remhash token (eglot--progress-reporters server)))))))))) +(defvar-local eglot--cached-tdi nil + "A cached LSP TextDocumentIdentifier URI string.") + (cl-defmethod eglot-handle-notification - (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics + (server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode' "Handle notification publishDiagnostics." (cl-flet ((eglot--diag-type (sev) @@ -2392,9 +2395,18 @@ still unanswered LSP requests to the server\n"))) ((= sev 2) 'eglot-warning) (t 'eglot-note))) (mess (source code message) - (concat source (and code (format " [%s]" code)) ": " message))) + (concat source (and code (format " [%s]" code)) ": " message)) + (find-it (uri) + ;; Search the managed buffers for a buffer with the + ;; provided diagnostic from the server. We do this to + ;; avoid calling `file-truename' too often, gaining an + ;; increase in performance. + (cl-loop for b in (eglot--managed-buffers server) + when (with-current-buffer b + (equal eglot--cached-tdi uri)) + return b))) (if-let* ((path (expand-file-name (eglot-uri-to-path uri))) - (buffer (find-buffer-visiting path))) + (buffer (find-it uri))) (with-current-buffer buffer (cl-loop initially @@ -2519,9 +2531,6 @@ THINGS are either registrations or unregisterations (sic)." (t (setq success :json-false))) `(:success ,success))) -(defvar-local eglot--cached-tdi nil - "A cached LSP TextDocumentIdentifier URI string.") - (defun eglot--TextDocumentIdentifier () "Compute TextDocumentIdentifier object for current buffer." `(:uri ,(or eglot--cached-tdi diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index e501e24f5d2..28579ccde5c 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -436,6 +436,28 @@ directory hierarchy." (flymake-goto-next-error 1 '() t) (should (eq 'flymake-error (face-at-point))))))) +(ert-deftest eglot-test-basic-symlink () + "Test basic symlink support." + (skip-unless (executable-find "clangd")) + (eglot--with-fixture + `(("symlink-project" . + (("main.cpp" . "#include\"foo.h\"\nint main() { return foo(); }") + ("foo.h" . "int foo();")))) + (with-current-buffer + (find-file-noselect "symlink-project/main.cpp") + (make-symbolic-link "main.cpp" "mainlink.cpp") + (eglot--tests-connect) + (find-file-noselect "mainlink.cpp") + (with-current-buffer + (find-file-noselect "foo.h") + (goto-char 5) + (xref-find-references "foo") + (with-current-buffer (get-buffer "*xref*") + (end-of-buffer) + ;; Expect the xref buffer to not contain duplicate references to + ;; main.c and mainlink.c. If it did total lines would be 7. + (should (= (line-number-at-pos (point)) 5))))))) + (ert-deftest eglot-test-diagnostic-tags-unnecessary-code () "Test rendering of diagnostics tagged \"unnecessary\"." (skip-unless (executable-find "clangd")) -- 2.39.5