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.
This commit is contained in:
parent
7d60d1652f
commit
82f45b9580
1 changed files with 42 additions and 20 deletions
|
@ -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))))))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue