(progress-reporter-done reporter)))))
(defun eglot--confirm-server-edits (origin _prepared)
+ "Helper for `eglot--apply-workspace-edit.
+ORIGIN is a symbol designating a command. Reads the
+`eglot-confirm-server-edits' user option and returns a symbol
+like `diff', `summary' or nil."
(let (v)
(cond ((symbolp eglot-confirm-server-edits) eglot-confirm-server-edits)
((setq v (assoc origin eglot-confirm-server-edits)) (cdr v))
(defun eglot--propose-changes-as-diff (prepared)
"Helper for `eglot--apply-workspace-edit'.
-PREPARED is a list ((FILENAME EDITS VERSION)...)."
+Goal is to popup a `diff-mode' buffer containing all the changes
+of PREPARED, ready to apply with C-c C-a. PREPARED is a
+list ((FILENAME EDITS VERSION)...)."
(with-current-buffer (get-buffer-create "*EGLOT proposed server changes*")
(buffer-disable-undo (current-buffer))
(let ((buffer-read-only t))
(erase-buffer)
(pcase-dolist (`(,path ,edits ,_) prepared)
(with-temp-buffer
- (let ((diff (current-buffer)))
+ (let* ((diff (current-buffer))
+ (existing-buf (find-buffer-visiting path))
+ (existing-buf-label (prin1-to-string existing-buf)))
+ ;; `existing-buf' might be an unsaved buffer, so we do
+ ;; this complicated little dance to diff it with a
+ ;; temporary file with the proposed changes applied.
(with-temp-buffer
- (insert-file-contents path)
- (eglot--apply-text-edits edits)
- (diff-no-select path (current-buffer)
- nil t diff))
+ (if existing-buf
+ (let ((temp (current-buffer)))
+ (with-current-buffer existing-buf
+ (copy-to-buffer temp (point-min) (point-max))))
+ (insert-file-contents path))
+ (eglot--apply-text-edits edits nil t)
+ (diff-no-select (or existing-buf path) (current-buffer)
+ nil t diff)
+ (when existing-buf
+ ;; Here we have to pretend the label of the unsaved
+ ;; buffer is the actual file, just so that we can
+ ;; diff-apply without troubles. If there's a better
+ ;; way, it probably involves changes to `diff.el'.
+ (with-current-buffer diff
+ (goto-char (point-min))
+ (while (search-forward existing-buf-label nil t)
+ (replace-match (buffer-file-name existing-buf))))))
(with-current-buffer target
(insert-buffer-substring diff))))))
(setq-local buffer-read-only t)
(font-lock-ensure)))
(defun eglot--apply-workspace-edit (wedit origin)
- "Apply the workspace edit WEDIT.
+ "Apply (or offer to apply) the workspace edit WEDIT.
ORIGIN is a symbol designating the command that originated this
edit proposed by the server."
(eglot--dbind ((WorkspaceEdit) changes documentChanges) wedit
(cl-flet ((notevery-visited-p ()
(cl-notevery #'find-buffer-visiting
(mapcar #'car prepared)))
- (prompt ()
- (unless (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" "")))))
- (jsonrpc-error "User canceled server edit")))
+ (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" ""))))))
(apply ()
(cl-loop for edit in prepared
for (path edits version) = edit
((or (eq decision 'diff)
(and (eq decision 'maybe-diff) (notevery-visited-p)))
(eglot--propose-changes-as-diff prepared))
- ((or (eq decision 'summary)
+ ((or (memq decision '(t summary))
(and (eq decision 'maybe-summary) (notevery-visited-p)))
- (prompt)
- (apply))
+ (when (accept-p) (apply)))
(t
(apply))))))))