aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodor Thornhill2024-03-30 20:52:41 +0100
committerTheodor Thornhill2024-04-03 15:02:16 +0200
commit08c80c45ddea17df87fc768a39dff353ccc13d3b (patch)
treea649606fee9322085ed24441a2c5a560a9889248
parent48dc75f2353152158fa325f5fbf154cdc75e8cfa (diff)
downloademacs-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.el42
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
2842Try to visit the target file for a richer summary line." 2846Try 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