diff options
| author | Theodor Thornhill | 2024-03-30 20:52:41 +0100 |
|---|---|---|
| committer | Theodor Thornhill | 2024-04-03 15:02:16 +0200 |
| commit | 08c80c45ddea17df87fc768a39dff353ccc13d3b (patch) | |
| tree | a649606fee9322085ed24441a2c5a560a9889248 | |
| parent | 48dc75f2353152158fa325f5fbf154cdc75e8cfa (diff) | |
| download | emacs-08c80c45ddea17df87fc768a39dff353ccc13d3b.tar.gz emacs-08c80c45ddea17df87fc768a39dff353ccc13d3b.zip | |
Don't use file-truepath in Eglot (bug#70036)
`file-truepath' is slow because of recursive calls and being implemented
in lisp. It seems to not be needed in eglot, but it is used behind the
scenes in `find-buffer-visiting', thus appearing in profiles. Moving
the implementation to a hash map will yield similar performance
benefits, but wouldn't require us to rewrite `file-truename' in C.
* lisp/progmodes/eglot.el (eglot-lsp-server): Convert 'managed-buffers'
to a hashmap.
(eglot-uri-to-path): Don't use file-truepath, as it is too slow to be
included in the hot path.
(eglot--on-shutdown): Use buffers from buffer map.
(eglot--managed-mode): Add buffer to map, rather than list. Also remove
it from the map on deactivation.
(eglot-handle-notification): Expose server and get buffer from the
buffer map.
| -rw-r--r-- | lisp/progmodes/eglot.el | 42 |
1 files changed, 24 insertions, 18 deletions
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index f247c43203c..7f4284bf09d 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el | |||
| @@ -1053,8 +1053,8 @@ ACTION is an LSP object of either `CodeAction' or `Command' type." | |||
| 1053 | :documentation "Map (DIR -> (WATCH ID1 ID2...)) for `didChangeWatchedFiles'." | 1053 | :documentation "Map (DIR -> (WATCH ID1 ID2...)) for `didChangeWatchedFiles'." |
| 1054 | :initform (make-hash-table :test #'equal) :accessor eglot--file-watches) | 1054 | :initform (make-hash-table :test #'equal) :accessor eglot--file-watches) |
| 1055 | (managed-buffers | 1055 | (managed-buffers |
| 1056 | :initform nil | 1056 | :documentation "Map (PATH -> BUFFER) for buffers managed by server." |
| 1057 | :documentation "List of buffers managed by server." | 1057 | :initform (make-hash-table :test #'equal) |
| 1058 | :accessor eglot--managed-buffers) | 1058 | :accessor eglot--managed-buffers) |
| 1059 | (saved-initargs | 1059 | (saved-initargs |
| 1060 | :documentation "Saved initargs for reconnection purposes." | 1060 | :documentation "Saved initargs for reconnection purposes." |
| @@ -1085,12 +1085,12 @@ ACTION is an LSP object of either `CodeAction' or `Command' type." | |||
| 1085 | 1085 | ||
| 1086 | (defun eglot-path-to-uri (path) | 1086 | (defun eglot-path-to-uri (path) |
| 1087 | "Convert PATH, a file name, to LSP URI string and return it." | 1087 | "Convert PATH, a file name, to LSP URI string and return it." |
| 1088 | (let ((truepath (file-truename path))) | 1088 | (let ((expanded-path (expand-file-name path))) |
| 1089 | (if (and (url-type (url-generic-parse-url path)) | 1089 | (if (and (url-type (url-generic-parse-url path)) |
| 1090 | ;; It might be MS Windows path which includes a drive | 1090 | ;; It might be MS Windows path which includes a drive |
| 1091 | ;; letter that looks like a URL scheme (bug#59338) | 1091 | ;; letter that looks like a URL scheme (bug#59338) |
| 1092 | (not (and (eq system-type 'windows-nt) | 1092 | (not (and (eq system-type 'windows-nt) |
| 1093 | (file-name-absolute-p truepath)))) | 1093 | (file-name-absolute-p expanded-path)))) |
| 1094 | ;; Path is already a URI, so forward it to the LSP server | 1094 | ;; Path is already a URI, so forward it to the LSP server |
| 1095 | ;; untouched. The server should be able to handle it, since | 1095 | ;; untouched. The server should be able to handle it, since |
| 1096 | ;; it provided this URI to clients in the first place. | 1096 | ;; it provided this URI to clients in the first place. |
| @@ -1098,11 +1098,11 @@ ACTION is an LSP object of either `CodeAction' or `Command' type." | |||
| 1098 | (concat "file://" | 1098 | (concat "file://" |
| 1099 | ;; Add a leading "/" for local MS Windows-style paths. | 1099 | ;; Add a leading "/" for local MS Windows-style paths. |
| 1100 | (if (and (eq system-type 'windows-nt) | 1100 | (if (and (eq system-type 'windows-nt) |
| 1101 | (not (file-remote-p truepath))) | 1101 | (not (file-remote-p expanded-path))) |
| 1102 | "/") | 1102 | "/") |
| 1103 | (url-hexify-string | 1103 | (url-hexify-string |
| 1104 | ;; Again watch out for trampy paths. | 1104 | ;; Again watch out for trampy paths. |
| 1105 | (directory-file-name (file-local-name truepath)) | 1105 | (directory-file-name (file-local-name expanded-path)) |
| 1106 | eglot--uri-path-allowed-chars))))) | 1106 | eglot--uri-path-allowed-chars))))) |
| 1107 | 1107 | ||
| 1108 | (defun eglot-range-region (range &optional markers) | 1108 | (defun eglot-range-region (range &optional markers) |
| @@ -1187,7 +1187,7 @@ PRESERVE-BUFFERS as in `eglot-shutdown', which see." | |||
| 1187 | (defun eglot--on-shutdown (server) | 1187 | (defun eglot--on-shutdown (server) |
| 1188 | "Called by jsonrpc.el when SERVER is already dead." | 1188 | "Called by jsonrpc.el when SERVER is already dead." |
| 1189 | ;; Turn off `eglot--managed-mode' where appropriate. | 1189 | ;; Turn off `eglot--managed-mode' where appropriate. |
| 1190 | (dolist (buffer (eglot--managed-buffers server)) | 1190 | (dolist (buffer (map-values (eglot--managed-buffers server))) |
| 1191 | (let (;; Avoid duplicate shutdowns (github#389) | 1191 | (let (;; Avoid duplicate shutdowns (github#389) |
| 1192 | (eglot-autoshutdown nil)) | 1192 | (eglot-autoshutdown nil)) |
| 1193 | (eglot--when-live-buffer buffer (eglot--managed-mode-off)))) | 1193 | (eglot--when-live-buffer buffer (eglot--managed-mode-off)))) |
| @@ -1992,7 +1992,11 @@ Use `eglot-managed-p' to determine if current buffer is managed.") | |||
| 1992 | (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function | 1992 | (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function |
| 1993 | nil t) | 1993 | nil t) |
| 1994 | (eldoc-mode 1)) | 1994 | (eldoc-mode 1)) |
| 1995 | (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) | 1995 | |
| 1996 | (let ((buffer (current-buffer))) | ||
| 1997 | (puthash (expand-file-name (buffer-file-name buffer)) | ||
| 1998 | buffer | ||
| 1999 | (eglot--managed-buffers (eglot-current-server))))) | ||
| 1996 | (t | 2000 | (t |
| 1997 | (remove-hook 'after-change-functions #'eglot--after-change t) | 2001 | (remove-hook 'after-change-functions #'eglot--after-change t) |
| 1998 | (remove-hook 'before-change-functions #'eglot--before-change t) | 2002 | (remove-hook 'before-change-functions #'eglot--before-change t) |
| @@ -2020,10 +2024,10 @@ Use `eglot-managed-p' to determine if current buffer is managed.") | |||
| 2020 | (let ((server eglot--cached-server)) | 2024 | (let ((server eglot--cached-server)) |
| 2021 | (setq eglot--cached-server nil) | 2025 | (setq eglot--cached-server nil) |
| 2022 | (when server | 2026 | (when server |
| 2023 | (setf (eglot--managed-buffers server) | 2027 | (remhash (expand-file-name (buffer-file-name (current-buffer))) |
| 2024 | (delq (current-buffer) (eglot--managed-buffers server))) | 2028 | (eglot--managed-buffers server)) |
| 2025 | (when (and eglot-autoshutdown | 2029 | (when (and eglot-autoshutdown |
| 2026 | (null (eglot--managed-buffers server))) | 2030 | (null (map-values (eglot--managed-buffers server)))) |
| 2027 | (eglot-shutdown server))))))) | 2031 | (eglot-shutdown server))))))) |
| 2028 | 2032 | ||
| 2029 | (defun eglot--managed-mode-off () | 2033 | (defun eglot--managed-mode-off () |
| @@ -2346,7 +2350,7 @@ still unanswered LSP requests to the server\n"))) | |||
| 2346 | (remhash token (eglot--progress-reporters server)))))))))) | 2350 | (remhash token (eglot--progress-reporters server)))))))))) |
| 2347 | 2351 | ||
| 2348 | (cl-defmethod eglot-handle-notification | 2352 | (cl-defmethod eglot-handle-notification |
| 2349 | (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics | 2353 | (server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics |
| 2350 | &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode' | 2354 | &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode' |
| 2351 | "Handle notification publishDiagnostics." | 2355 | "Handle notification publishDiagnostics." |
| 2352 | (cl-flet ((eglot--diag-type (sev) | 2356 | (cl-flet ((eglot--diag-type (sev) |
| @@ -2357,7 +2361,7 @@ still unanswered LSP requests to the server\n"))) | |||
| 2357 | (mess (source code message) | 2361 | (mess (source code message) |
| 2358 | (concat source (and code (format " [%s]" code)) ": " message))) | 2362 | (concat source (and code (format " [%s]" code)) ": " message))) |
| 2359 | (if-let* ((path (expand-file-name (eglot-uri-to-path uri))) | 2363 | (if-let* ((path (expand-file-name (eglot-uri-to-path uri))) |
| 2360 | (buffer (find-buffer-visiting path))) | 2364 | (buffer (gethash path (eglot--managed-buffers server)))) |
| 2361 | (with-current-buffer buffer | 2365 | (with-current-buffer buffer |
| 2362 | (cl-loop | 2366 | (cl-loop |
| 2363 | initially | 2367 | initially |
| @@ -2842,7 +2846,7 @@ may be called multiple times (respecting the protocol of | |||
| 2842 | Try to visit the target file for a richer summary line." | 2846 | Try to visit the target file for a richer summary line." |
| 2843 | (pcase-let* | 2847 | (pcase-let* |
| 2844 | ((file (eglot-uri-to-path uri)) | 2848 | ((file (eglot-uri-to-path uri)) |
| 2845 | (visiting (or (find-buffer-visiting file) | 2849 | (visiting (or (gethash file (eglot--managed-buffers (eglot-current-server))) |
| 2846 | (gethash uri eglot--temp-location-buffers))) | 2850 | (gethash uri eglot--temp-location-buffers))) |
| 2847 | (collect (lambda () | 2851 | (collect (lambda () |
| 2848 | (eglot--widening | 2852 | (eglot--widening |
| @@ -3542,13 +3546,14 @@ list ((FILENAME EDITS VERSION)...)." | |||
| 3542 | (with-current-buffer (get-buffer-create "*EGLOT proposed server changes*") | 3546 | (with-current-buffer (get-buffer-create "*EGLOT proposed server changes*") |
| 3543 | (buffer-disable-undo (current-buffer)) | 3547 | (buffer-disable-undo (current-buffer)) |
| 3544 | (let ((inhibit-read-only t) | 3548 | (let ((inhibit-read-only t) |
| 3545 | (target (current-buffer))) | 3549 | (target (current-buffer)) |
| 3550 | (managed-buffers (eglot--managed-buffers (eglot-current-server)))) | ||
| 3546 | (diff-mode) | 3551 | (diff-mode) |
| 3547 | (erase-buffer) | 3552 | (erase-buffer) |
| 3548 | (pcase-dolist (`(,path ,edits ,_) prepared) | 3553 | (pcase-dolist (`(,path ,edits ,_) prepared) |
| 3549 | (with-temp-buffer | 3554 | (with-temp-buffer |
| 3550 | (let* ((diff (current-buffer)) | 3555 | (let* ((diff (current-buffer)) |
| 3551 | (existing-buf (find-buffer-visiting path)) | 3556 | (existing-buf (gethash path (gethash path managed-buffers))) |
| 3552 | (existing-buf-label (prin1-to-string existing-buf))) | 3557 | (existing-buf-label (prin1-to-string existing-buf))) |
| 3553 | (with-temp-buffer | 3558 | (with-temp-buffer |
| 3554 | (if existing-buf | 3559 | (if existing-buf |
| @@ -3583,7 +3588,8 @@ edit proposed by the server." | |||
| 3583 | (eglot--dbind ((VersionedTextDocumentIdentifier) uri version) | 3588 | (eglot--dbind ((VersionedTextDocumentIdentifier) uri version) |
| 3584 | textDocument | 3589 | textDocument |
| 3585 | (list (eglot-uri-to-path uri) edits version))) | 3590 | (list (eglot-uri-to-path uri) edits version))) |
| 3586 | documentChanges))) | 3591 | documentChanges)) |
| 3592 | (managed-buffers (eglot--managed-buffers (eglot-current-server)))) | ||
| 3587 | (unless (and changes documentChanges) | 3593 | (unless (and changes documentChanges) |
| 3588 | ;; We don't want double edits, and some servers send both | 3594 | ;; We don't want double edits, and some servers send both |
| 3589 | ;; changes and documentChanges. This unless ensures that we | 3595 | ;; changes and documentChanges. This unless ensures that we |
| @@ -3591,7 +3597,7 @@ edit proposed by the server." | |||
| 3591 | (cl-loop for (uri edits) on changes by #'cddr | 3597 | (cl-loop for (uri edits) on changes by #'cddr |
| 3592 | do (push (list (eglot-uri-to-path uri) edits) prepared))) | 3598 | do (push (list (eglot-uri-to-path uri) edits) prepared))) |
| 3593 | (cl-flet ((notevery-visited-p () | 3599 | (cl-flet ((notevery-visited-p () |
| 3594 | (cl-notevery #'find-buffer-visiting | 3600 | (cl-notevery (lambda (p) (gethash p managed-buffers)) |
| 3595 | (mapcar #'car prepared))) | 3601 | (mapcar #'car prepared))) |
| 3596 | (accept-p () | 3602 | (accept-p () |
| 3597 | (y-or-n-p | 3603 | (y-or-n-p |