[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 82f45b95806 1/2: Eglot: handle unsaved buffers in new eglot--prop
From: |
João Távora |
Subject: |
master 82f45b95806 1/2: Eglot: handle unsaved buffers in new eglot--propose-changes-as-diff |
Date: |
Fri, 1 Sep 2023 17:58:00 -0400 (EDT) |
branch: master
commit 82f45b958063e1128f183f4a54628428408910dc
Author: João Távora <joaotavora@gmail.com>
Commit: João Távora <joaotavora@gmail.com>
Eglot: handle unsaved buffers in new eglot--propose-changes-as-diff
* lisp/progmodes/eglot.el (eglot--confirm-server-edits): Add docstring.
(eglot--propose-changes-as-diff): Rework. Handle unsaved buffers.
(eglot--apply-workspace-edit): Rework.
---
lisp/progmodes/eglot.el | 62 +++++++++++++++++++++++++++++++++----------------
1 file changed, 42 insertions(+), 20 deletions(-)
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 8d95019c3ed..810c6cb4638 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -3459,6 +3459,10 @@ If SILENT, don't echo progress in mode-line."
(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))
@@ -3466,7 +3470,9 @@ If SILENT, don't echo progress in mode-line."
(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))
@@ -3476,12 +3482,30 @@ PREPARED is a list ((FILENAME EDITS VERSION)...)."
(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)
@@ -3491,7 +3515,7 @@ PREPARED is a list ((FILENAME EDITS VERSION)...)."
(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
@@ -3510,16 +3534,15 @@ edit proposed by the server."
(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
@@ -3531,10 +3554,9 @@ edit proposed by the server."
((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))))))))