-
Notifications
You must be signed in to change notification settings - Fork 200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
eglot--apply-workspace-edit fails to rename a directory #1450
Comments
As far as I know, Eglot does not send any resourceOperations among its client capabilities, so it is a mistake from the server to send a rename workspace edit (e.g.: |
This is indeed because eglot doesn't support file rename yet. I've hit the same issue on clojure-lsp, and the below patch resolves it for me. Let me know if it helps @adonovan diff -u /var/folders/wk/_mbcwk1x7gn6bxqx97__h2c80000gp/T/buffer-content-gGFDOp /var/folders/wk/_mbcwk1x7gn6bxqx97__h2c80000gp/T/buffer-content-RYgFdZ
--- /var/folders/wk/_mbcwk1x7gn6bxqx97__h2c80000gp/T/buffer-content-gGFDOp 2024-12-27 13:35:01
+++ /var/folders/wk/_mbcwk1x7gn6bxqx97__h2c80000gp/T/buffer-content-RYgFdZ 2024-12-27 13:35:01
@@ -635,7 +635,9 @@
(ParameterInformation (:label) (:documentation))
(Position (:line :character))
(Range (:start :end))
+ (RenameFile (:oldUri) (:newUri) nil)
(Registration (:id :method) (:registerOptions))
+ (ResourceOperation (:kind))
(ResponseError (:code :message) (:data))
(ShowMessageParams (:type :message))
(ShowMessageRequestParams (:type :message) (:actions))
@@ -3662,36 +3664,56 @@
ORIGIN is a symbol designating the command that originated this
edit proposed by the server."
(eglot--dbind ((WorkspaceEdit) changes documentChanges) wedit
- (let ((prepared
- (mapcar (eglot--lambda ((TextDocumentEdit) textDocument edits)
- (eglot--dbind ((VersionedTextDocumentIdentifier) uri version)
- textDocument
- (list (eglot-uri-to-path uri) edits version)))
- documentChanges)))
- (unless (and changes documentChanges)
- ;; We don't want double edits, and some servers send both
- ;; changes and documentChanges. This unless ensures that we
- ;; prefer documentChanges over changes.
+ (let ((prepared-text-edits '())
+ (prepared-resource-ops '()))
+ ;; Handle documentChanges for file rename
+ (when (and documentChanges
+ (vectorp documentChanges)
+ (= (length documentChanges) 1)
+ (string= (plist-get (aref documentChanges 0) :kind) "rename"))
+ (let* ((rename-op (aref documentChanges 0))
+ (old-path (eglot-uri-to-path (plist-get rename-op :oldUri)))
+ (new-path (eglot-uri-to-path (plist-get rename-op :newUri))))
+ (push (list 'rename old-path new-path) prepared-resource-ops)))
+
+ ;; Handle changes for symbol updates
+ (when changes
(cl-loop for (uri edits) on changes by #'cddr
- do (push (list (eglot-uri-to-path uri) edits) prepared)))
+ do (push (list (eglot-uri-to-path uri) edits)
+ prepared-text-edits)))
+
(cl-flet ((notevery-visited-p ()
(cl-notevery #'find-buffer-visiting
- (mapcar #'car prepared)))
+ (mapcar #'car prepared-text-edits)))
(accept-p ()
(y-or-n-p
- (format "[eglot] Server wants to edit:\n%sProceed? "
- (cl-loop
- for (f eds _) in prepared
- concat (format
- " %s (%d change%s)\n"
- f (length eds)
- (if (> (length eds) 1) "s" ""))))))
+ (format "[eglot] Server wants to make the following changes:\n%s%sProceed? "
+ (cl-loop for (f eds) in prepared-text-edits
+ concat (format " %s (%d text change%s)\n"
+ f (length eds)
+ (if (> (length eds) 1) "s" "")))
+ (cl-loop for (op old new) in prepared-resource-ops
+ concat (format " %s: %s -> %s\n"
+ op old new)))))
(apply ()
- (cl-loop for edit in prepared
- for (path edits version) = edit
- do (with-current-buffer (find-file-noselect path)
- (eglot--apply-text-edits edits version))
- finally (eldoc) (eglot--message "Edit successful!"))))
- (let ((decision (eglot--confirm-server-edits origin prepared)))
+ ;; First apply text edits
+ (dolist (edit prepared-text-edits)
+ (let ((path (car edit))
+ (edits (cadr edit)))
+ (with-current-buffer (find-file-noselect path)
+ (eglot--apply-text-edits edits nil))))
+ ;; Then apply resource operations
+ (dolist (op prepared-resource-ops)
+ (pcase op
+ (`(rename ,old-path ,new-path)
+ (let ((buf (find-buffer-visiting old-path)))
+ (when buf
+ (with-current-buffer buf
+ (set-visited-file-name new-path)
+ (save-buffer)))
+ (rename-file old-path new-path t)))))
+ (eldoc)
+ (eglot--message "Edit successful!")))
+ (let ((decision (eglot--confirm-server-edits origin prepared-text-edits)))
(cond
((or (eq decision 'diff)
(and (eq decision 'maybe-diff) (notevery-visited-p)))
- (eglot--propose-changes-as-diff prepared))
+ (eglot--propose-changes-as-diff prepared-text-edits))
((or (memq decision '(t summary))
(and (eq decision 'maybe-summary) (notevery-visited-p)))
(when (accept-p) (apply)))
@@ -3712,11 +3734,28 @@
nil nil nil nil
(symbol-name (symbol-at-point)))))
(eglot-server-capable-or-lose :renameProvider)
- (eglot--apply-workspace-edit
- (eglot--request (eglot--current-server-or-lose)
- :textDocument/rename `(,@(eglot--TextDocumentPositionParams)
- :newName ,newname))
- this-command))
+ (let* ((old-name (symbol-name (symbol-at-point)))
+ (locations (eglot--request (eglot--current-server-or-lose)
+ :textDocument/references
+ `(:context (:includeDeclaration t)
+ ,@(eglot--TextDocumentPositionParams))))
+ (changes nil)
+ (rename-response (eglot--request (eglot--current-server-or-lose)
+ :textDocument/rename
+ `(,@(eglot--TextDocumentPositionParams)
+ :newName ,newname))))
+ ;; Process references for symbol updates
+ (dotimes (i (length locations))
+ (let* ((loc (aref locations i))
+ (uri (plist-get loc :uri))
+ (range (plist-get loc :range)))
+ (push `(:range ,range :newText ,newname) (plist-get changes uri))
+ (setq changes (plist-put changes uri (plist-get changes uri)))))
+ ;; Apply both the file rename and symbol updates
+ (eglot--apply-workspace-edit
+ `(:documentChanges ,(plist-get rename-response :documentChanges)
+ :changes ,changes)
+ this-command)))
(defun eglot--code-action-bounds ()
"Calculate appropriate bounds depending on region and point." |
In Go's LSP server, an
eglot-rename
operation on a package name causes the server to respond with a directory rename. In other words, the workspace-edit's document-change's old and new URIs both refer to directories. This causes eglot--apply-workspace-edit to fail.The text was updated successfully, but these errors were encountered: