all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Philip Kaludercic <philipk@posteo.net>
To: 60338@debbugs.gnu.org, "João Távora" <joaotavora@gmail.com>
Subject: bug#60338: [PATCH] Add option to present server changes as diffs
Date: Sun, 11 Jun 2023 21:33:40 +0000	[thread overview]
Message-ID: <877cs97kgb.fsf@posteo.net> (raw)
In-Reply-To: <87ilhy1dub.fsf@posteo.net> (Philip Kaludercic's message of "Mon,  26 Dec 2022 13:42:04 +0000")

[-- Attachment #1: Type: text/plain, Size: 156 bytes --]


The previous patch couldn't be applied onto master anymore, so I have
adjusted it and merged the user option into 'eglot-confirm-server-initiated-edits'.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Allow-for-LSP-edits-to-be-generated-as-diffs.patch --]
[-- Type: text/x-diff, Size: 6524 bytes --]

From 7263d821c2d217c5748965aad57df8c0311f7e95 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <philipk@posteo.net>
Date: Sun, 11 Jun 2023 23:18:12 +0200
Subject: [PATCH 1/2] Allow for LSP edits to be generated as diffs

* doc/misc/eglot.texi (Eglot Variables): Document the new behaviour.
* lisp/progmodes/eglot.el (eglot-confirm-server-initiated-edits): Add
new value 'diff'.
(eglot-execute): Check if 'eglot-confirm-server-initiated-edits' is
set to 'diff'.
(eglot--apply-workspace-edit): Respect the new 'diff' value.
(eglot-rename): Pass 'eglot-confirm-server-initiated-edits' instead of
just the value 'current-prefix-arg'.  (Bug#60338)
---
 doc/misc/eglot.texi     |  6 ++--
 lisp/progmodes/eglot.el | 62 +++++++++++++++++++++++++++++++----------
 2 files changed, 51 insertions(+), 17 deletions(-)

diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index 962e6c914ce..305feda43a6 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -833,9 +833,11 @@ Eglot Variables
 @item eglot-confirm-server-initiated-edits
 Various Eglot commands and code actions result in the language server
 sending editing commands to Emacs.  If this option's value is
-non-@code{nil} (the default), Eglot will ask for confirmation before
+@code{confirm} (the default), Eglot will ask for confirmation before
 performing edits initiated by the server or edits whose scope affects
-buffers other than the one where the user initiated the request.
+buffers other than the one where the user initiated the request.  If
+set to @code{diff}, Eglot will generate and display a diff to display
+the changes.
 
 @item eglot-ignored-server-capabilities
 This variable's value is a list of language server capabilities that
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 7d5d786dea3..9c9c3152472 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -108,6 +108,7 @@
 (require 'filenotify)
 (require 'ert)
 (require 'text-property-search nil t)
+(require 'diff)
 
 ;; These dependencies are also GNU ELPA core packages.  Because of
 ;; bug#62576, since there is a risk that M-x package-install, despite
@@ -386,9 +387,14 @@ eglot-events-buffer-size
   :type '(choice (const :tag "No limit" nil)
                  (integer :tag "Number of characters")))
 
 (defcustom eglot-confirm-server-initiated-edits 'confirm
-  "Non-nil if server-initiated edits should be confirmed with user."
+  "Control how server edits are applied.
+If set to `confirm', a prompt is presented to confirm server
+changes.  If set to `diff', a buffer will pop up with changes
+that can be applied manually.  If set to nil, modifications are
+made automatically."
   :type '(choice (const :tag "Don't show confirmation prompt" nil)
+                 (const :tag "Present as diffs in a buffer" diff)
                  (const :tag "Show confirmation prompt" confirm)))
 
 (defcustom eglot-extend-to-xref nil
@@ -740,7 +746,8 @@ eglot-execute
    (eglot--dcase action
      (((Command)) (eglot--request server :workspace/executeCommand action))
      (((CodeAction) edit command)
-      (when edit (eglot--apply-workspace-edit edit))
+      (when edit
+        (eglot--apply-workspace-edit edit (eq eglot-confirm-server-initiated-edits t)))
       (when command (eglot--request server :workspace/executeCommand command))))))
 
 (cl-defgeneric eglot-initialization-options (server)
@@ -3408,18 +3415,43 @@ eglot--apply-workspace-edit
         ;; prefer documentChanges over changes.
         (cl-loop for (uri edits) on changes by #'cddr
                  do (push (list (eglot--uri-to-path uri) edits) prepared)))
-      (if (or confirm
-              (cl-notevery #'find-buffer-visiting
-                           (mapcar #'car prepared)))
-          (unless (y-or-n-p
+      (cond
+       ((eq confirm 'diff)
+        (with-current-buffer (get-buffer-create " *Changes*")
+          (buffer-disable-undo (current-buffer))
+          (let ((buffer-read-only t))
+            (diff-mode))
+          (let ((inhibit-read-only t)
+                (target (current-buffer)))
+            (erase-buffer)
+            (pcase-dolist (`(,path ,edits ,_) prepared)
+              (with-temp-buffer
+                (let ((diff (current-buffer)))
+                  (with-temp-buffer
+                    (insert-file-contents path)
+                    (eglot--apply-text-edits edits)
+                    (diff-no-select path (current-buffer)
+                                    nil t diff))
+                  (with-current-buffer target
+                    (insert-buffer-substring diff))))))
+          (setq-local buffer-read-only t)
+          (buffer-enable-undo (current-buffer))
+          (goto-char (point-min))
+          (pop-to-buffer (current-buffer))
+          (font-lock-ensure)))
+       ((and (or (eq confirm 'confirm)
+                 (cl-find-if-not #'find-buffer-visiting prepared
+                                 :key #'car))
+             ;; Should the list of buffers be popped up temporarily in
+             ;; a buffer instead of resizing the minibufffer?
+             (not (y-or-n-p
                    (format "[eglot] Server wants to edit:\n  %s\n Proceed? "
-                           (mapconcat #'identity (mapcar #'car prepared) "\n  ")))
-            (jsonrpc-error "User canceled server edit")))
-      (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!")))))
+                           (mapconcat #'identity (mapcar #'car prepared) "\n  ")))))
+        (jsonrpc-error "User canceled server edit"))
+       ((cl-loop for (path edits version) in prepared
+                 do (with-current-buffer (find-file-noselect path)
+                      (eglot--apply-text-edits edits version))
+                 finally (eldoc) (eglot--message "Edit successful!")))))))
 
 (defun eglot-rename (newname)
   "Rename the current symbol to NEWNAME."
@@ -3434,7 +3466,7 @@ eglot-rename
    (eglot--request (eglot--current-server-or-lose)
                    :textDocument/rename `(,@(eglot--TextDocumentPositionParams)
                                           :newName ,newname))
-   current-prefix-arg))
+   (and current-prefix-arg eglot-confirm-server-initiated-edits)))
 
 (defun eglot--region-bounds ()
   "Region bounds if active, else bounds of things at point."
-- 
2.39.2


[-- Attachment #3: Type: text/plain, Size: 183 bytes --]


In addition to that, here is a cheap command that would allow for a
patch to be applied with a single command.  I've bound it to C-c C-x,
but perhaps there is a more intuitive key?


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0002-Add-command-to-apply-an-entire-diff-at-once.patch --]
[-- Type: text/x-diff, Size: 1351 bytes --]

From 1cf6f09318860a8232f5850c0d1ca497d8c621f9 Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <philipk@posteo.net>
Date: Sun, 11 Jun 2023 23:30:03 +0200
Subject: [PATCH 2/2] Add command to apply an entire diff at once

* lisp/vc/diff-mode.el (diff-mode-map): Bind 'diff-apply-everything'.
(diff-apply-everything): Add new command.
---
 lisp/vc/diff-mode.el | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index d776375d681..d81f0af365e 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -216,6 +216,7 @@ diff-mode-map
   "C-x 4 A" #'diff-add-change-log-entries-other-window
   ;; Misc operations.
   "C-c C-a" #'diff-apply-hunk
+  "C-c C-x" #'diff-apply-everything
   "C-c C-e" #'diff-ediff-patch
   "C-c C-n" #'diff-restrict-view
   "C-c C-s" #'diff-split-hunk
@@ -2033,6 +2034,16 @@ diff-apply-hunk
       (when diff-advance-after-apply-hunk
 	(diff-hunk-next))))))
 
+(defun diff-apply-everything ()
+  "Apply the entire diff."
+  (interactive)
+  (save-excursion
+    (goto-char (point-min))
+    (while (let ((inhibit-message t)
+                 (start (point)))
+             (diff-hunk-next)
+             (/= start (point)))
+      (diff-apply-hunk))))
 
 (defun diff-test-hunk (&optional reverse)
   "See whether it's possible to apply the current hunk.
-- 
2.39.2


[-- Attachment #5: Type: text/plain, Size: 774 bytes --]


Philip Kaludercic <philipk@posteo.net> writes:

> X-CC-Debbugs: 
>
> I'd like to propose adding an option that makes server modifications by
> Eglot less invasive.  The current behaviour is to make the changes
> directly in a buffer and open the remaining files to make the
> modifications in those as well (?).  If `eglot-use-diffs' is enabled,
> all confirmations are prepared as patches in a pop-up buffer that the
> user can review and apply at will.  To my knowledge there is no general
> `diff-apply-hunk' that will apply all the changes from a buffer, but
> that is a separate issue that can be fixed in a separate patch.
>
> (Note, I'm still testing emacs-29, so the patch was developed on that
> branch.  But it should be applied to master)

-- 
Philip Kaludercic

  parent reply	other threads:[~2023-06-11 21:33 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-26 13:42 bug#60338: [PATCH] Add option to present server changes as diffs Philip Kaludercic
2022-12-29  0:01 ` Yuan Fu
2022-12-29 14:28   ` Philip Kaludercic
2022-12-29 14:36     ` João Távora
2022-12-29 14:39       ` Philip Kaludercic
2022-12-30 13:13         ` João Távora
2022-12-30 15:09           ` Philip Kaludercic
2023-01-04 20:56             ` Felician Nemeth
2023-06-09  7:55               ` Philip Kaludercic
2023-06-11 21:33 ` Philip Kaludercic [this message]
2023-06-12 11:56   ` Eli Zaretskii
2023-06-12 12:35     ` Philip Kaludercic
2023-06-12 12:52       ` Eli Zaretskii
2023-06-12 13:29         ` Philip Kaludercic
2023-06-12 13:41           ` Eli Zaretskii
2023-06-13 21:34             ` Philip Kaludercic
2023-06-14  6:00               ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-06-14 11:27                 ` Philip Kaludercic
2023-06-18 11:38                   ` Philip Kaludercic
2023-06-18 15:18                     ` João Távora
2023-06-18 22:37                       ` João Távora
2023-06-24 16:53                       ` Philip Kaludercic
2023-09-01  0:06                         ` João Távora
2023-09-01  5:18                           ` Philip Kaludercic
2023-09-01 21:12                           ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-01 21:19                             ` João Távora
2023-09-01 22:01                             ` João Távora
2023-09-02  6:13                               ` Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-02  9:55                                 ` João Távora
2023-09-07  1:00                                   ` Dmitry Gutov
2023-09-07  6:28                                     ` Juri Linkov
2023-09-07 12:41                                       ` Dmitry Gutov
2023-09-07 12:45                                       ` Dmitry Gutov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=877cs97kgb.fsf@posteo.net \
    --to=philipk@posteo.net \
    --cc=60338@debbugs.gnu.org \
    --cc=joaotavora@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.