unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#60338: [PATCH] Add option to present server changes as diffs
@ 2022-12-26 13:42 Philip Kaludercic
  2022-12-29  0:01 ` Yuan Fu
  2023-06-11 21:33 ` Philip Kaludercic
  0 siblings, 2 replies; 33+ messages in thread
From: Philip Kaludercic @ 2022-12-26 13:42 UTC (permalink / raw)
  To: 60338

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

X-CC-Debbugs: João Távora <joaotavora@gmail.com>

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)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: [PATCH] Add option to present server changes as diffs --]
[-- Type: text/x-patch, Size: 4834 bytes --]

From 88ba620b47b7987d203fc5b42716fa08be3b1d8f Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <philipk@posteo.net>
Date: Mon, 26 Dec 2022 11:06:09 +0100
Subject: [PATCH] Add option to present server changes as diffs

* lisp/progmodes/eglot.el (eglot-use-diffs): Add new user option.
(eglot--apply-workspace-edit): Respect 'eglot-use-diffs'.
* doc/misc/eglot.texi (Customizing Eglot): Document 'eglot-use-diffs'.
---
 doc/misc/eglot.texi     |  7 ++++++
 lisp/progmodes/eglot.el | 54 ++++++++++++++++++++++++++++++++---------
 2 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index 2aff038b9a..9150e1a879 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -955,6 +955,13 @@ Customizing Eglot
 to use Eglot in your @code{eglot-managed-mode-hook} or via some other
 mechanism.
 
+@vindex eglot-use-diffs
+@item eglot-use-diffs
+If this option is enabled, any server modifications (renames, code
+actions, refactoring, @dots{}) will be presented as diffs that the
+user can selectively apply.  The default is that all modifications are
+made in place.
+
 @vindex eglot-report-progress
 @cindex progress
 @item eglot-report-progress
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 15cb1b6fad..74472b25ce 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -111,6 +111,8 @@
 (require 'ert)
 (require 'array)
 (require 'external-completion)
+(require 'diff-mode)
+(require 'diff)
 
 ;; ElDoc is preloaded in Emacs, so `require'-ing won't guarantee we are
 ;; using the latest version from GNU Elpa when we load eglot.el.  Use an
@@ -3160,6 +3162,11 @@ eglot--apply-text-edits
       (undo-amalgamate-change-group change-group)
       (progress-reporter-done reporter))))
 
+(defcustom eglot-use-diffs nil
+  "Non-nil means that server changes are presented as diffs."
+  :type 'boolean
+  :version "30.1")
+
 (defun eglot--apply-workspace-edit (wedit &optional confirm)
   "Apply the workspace edit WEDIT.  If CONFIRM, ask user first."
   (eglot--dbind ((WorkspaceEdit) changes documentChanges) wedit
@@ -3175,18 +3182,41 @@ 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
-                   (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!")))))
+      (if eglot-use-diffs
+          (with-current-buffer (get-buffer-create " *Server 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))
+        (if (or confirm
+                (cl-notevery #'find-buffer-visiting
+                             (mapcar #'car prepared)))
+            (unless (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!"))))))
 
 (defun eglot-rename (newname)
   "Rename the current symbol to NEWNAME."
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  2023-06-11 21:33 ` Philip Kaludercic
  1 sibling, 1 reply; 33+ messages in thread
From: Yuan Fu @ 2022-12-29  0:01 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: 60338


Philip Kaludercic <philipk@posteo.net> writes:

> X-CC-Debbugs: João Távora <joaotavora@gmail.com>
>
> 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)

This seems really nice :-)

Yuan





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2022-12-29  0:01 ` Yuan Fu
@ 2022-12-29 14:28   ` Philip Kaludercic
  2022-12-29 14:36     ` João Távora
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2022-12-29 14:28 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 60338, João Távora

Yuan Fu <casouri@gmail.com> writes:

> 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)
>
> This seems really nice :-)

Have you tried it out?  My worry is that there are some simple changes
that don't warrant a diff, but I don't know how these can be
distinguished.





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2022-12-29 14:28   ` Philip Kaludercic
@ 2022-12-29 14:36     ` João Távora
  2022-12-29 14:39       ` Philip Kaludercic
  0 siblings, 1 reply; 33+ messages in thread
From: João Távora @ 2022-12-29 14:36 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: Yuan Fu, 60338

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

The idea is good, but how does this play along with the existing
eglot-confirm-server-initiated-edits?

João

[-- Attachment #2: Type: text/html, Size: 182 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2022-12-29 14:39 UTC (permalink / raw)
  To: João Távora; +Cc: Yuan Fu, 60338

João Távora <joaotavora@gmail.com> writes:

> The idea is good, but how does this play along with the existing
> eglot-confirm-server-initiated-edits?

It takes precedence, since the diff is regarded as a kind of prompt.  If
you want to, I can also update the patch to use that option instead of a
custom one (e.g. present a diff if
`eglot-confirm-server-initiated-edits' is set to `diff').

> João





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2022-12-29 14:39       ` Philip Kaludercic
@ 2022-12-30 13:13         ` João Távora
  2022-12-30 15:09           ` Philip Kaludercic
  0 siblings, 1 reply; 33+ messages in thread
From: João Távora @ 2022-12-30 13:13 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: Yuan Fu, 60338

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

On Thu, Dec 29, 2022 at 2:39 PM Philip Kaludercic <philipk@posteo.net>
wrote:

> João Távora <joaotavora@gmail.com> writes:
>
> > The idea is good, but how does this play along with the existing
> > eglot-confirm-server-initiated-edits?
>
> It takes precedence, since the diff is regarded as a kind of prompt.  If
> you want to, I can also update the patch to use that option instead of a
> custom one (e.g. present a diff if
> `eglot-confirm-server-initiated-edits' is set to `diff').


This is an improvement, but it's not enough, unfortunately.

The current semantics of eglot-confirm-server-initiated-edits must first be
investigated, then potentially expanded/consolidated, even before the
augmentation
with the new 'diff value.   Only then should 'diff be added.  If we do this
some other
way, we only increase inconsistency and confusion.

Here is some information to get started with the investigation.

The function responsible for applying edits, eglot--apply-workspace-edit is
called currently from 3 places:

1. eglot-rename (this ignores eglot-c-s-initiated-edits, but confirms with
a prefix
argument).  The reasoning is that this is such a common action that by
default
we shouldn't bother the user with confirmation.

2. When the user chooses a code action from a list of code actions presented
as a menu and that code action happens to contain an edit.  This also
ignores
eglot-c-s-initiated-edits.  The locus of the call is
eglot--read-execute-code-action.
The reasoning here is, I believe, that the user is already being presented
with
an interactive prompt, and presenting a second follow-up seemed too much.

3. When applying a code action that makes the server initiate a code
action.
The locus is eglot--handle-request (for workspace/applyEdit).  Here
eglot-confirm-server-initiated-edits is read honoured.  The reasoning here
is
that the user might not be aware of the breadth of the code action that the
server is about to propose.

Note that the differences between 2 and 3 are subtle, and perhaps
conceptually
non-existent, but technically they do exist.  In 2, the edit is immediately
available whereas in 3, a further server trip is required to get the edit
to apply.

Moreover, the current method of  "confirmation" is just a prompt that lists
the
files about to be changed.  This is what should change, perhaps even by
default,  to a presentation of a diff in a separate buffer.

Currently, the confirmation happens also (regardless of the value of
eglot-confirm-server-initiated-edits) if any of the files about to be
changed
is not yet visited in a buffer.

So to summarize, I would like to hear proposals on how to make
user confirmation of edits more consistent and predictable across the
various use cases.

Then, as I've already said, the "diff" method of confirmation sounds in
principle very robust  and in-line with Emacs' principles. It could
eventually
even be made the default.

João

[-- Attachment #2: Type: text/html, Size: 4092 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2022-12-30 13:13         ` João Távora
@ 2022-12-30 15:09           ` Philip Kaludercic
  2023-01-04 20:56             ` Felician Nemeth
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2022-12-30 15:09 UTC (permalink / raw)
  To: João Távora; +Cc: Yuan Fu, 60338

João Távora <joaotavora@gmail.com> writes:

> On Thu, Dec 29, 2022 at 2:39 PM Philip Kaludercic <philipk@posteo.net>
> wrote:
>
>> João Távora <joaotavora@gmail.com> writes:
>>
>> > The idea is good, but how does this play along with the existing
>> > eglot-confirm-server-initiated-edits?
>>
>> It takes precedence, since the diff is regarded as a kind of prompt.  If
>> you want to, I can also update the patch to use that option instead of a
>> custom one (e.g. present a diff if
>> `eglot-confirm-server-initiated-edits' is set to `diff').
>
> This is an improvement, but it's not enough, unfortunately.
>
> The current semantics of eglot-confirm-server-initiated-edits must first be
> investigated, then potentially expanded/consolidated, even before the
> augmentation
> with the new 'diff value.   Only then should 'diff be added.  If we do this
> some other
> way, we only increase inconsistency and confusion.
>
> Here is some information to get started with the investigation.
>
> The function responsible for applying edits, eglot--apply-workspace-edit is
> called currently from 3 places:
>
> 1. eglot-rename (this ignores eglot-c-s-initiated-edits, but confirms with
> a prefix
> argument).  The reasoning is that this is such a common action that by
> default
> we shouldn't bother the user with confirmation.

This is also one of the situations where I believe that a diff is not
necessary, since the change is predictable.

> 2. When the user chooses a code action from a list of code actions presented
> as a menu and that code action happens to contain an edit.  This also
> ignores
> eglot-c-s-initiated-edits.  The locus of the call is
> eglot--read-execute-code-action.
> The reasoning here is, I believe, that the user is already being presented
> with
> an interactive prompt, and presenting a second follow-up seemed too much.
>
> 3. When applying a code action that makes the server initiate a code
> action.
> The locus is eglot--handle-request (for workspace/applyEdit).  Here
> eglot-confirm-server-initiated-edits is read honoured.  The reasoning here
> is
> that the user might not be aware of the breadth of the code action that the
> server is about to propose.
>
> Note that the differences between 2 and 3 are subtle, and perhaps
> conceptually
> non-existent, but technically they do exist.  In 2, the edit is immediately
> available whereas in 3, a further server trip is required to get the edit
> to apply.

I am afraid I can't conceptualise the difference between the two.  The
2. is probably what I am familiar with, but when does the server
initiate a code action.  Are we talking about an unprompted change?  Or
something like code formatting?

> Moreover, the current method of  "confirmation" is just a prompt that lists
> the
> files about to be changed.  This is what should change, perhaps even by
> default,  to a presentation of a diff in a separate buffer.

Would you say that this means we should always generate a diff and only
apply it when the user confirms the change, or is the cost of doubling
the communication acceptable?

> Currently, the confirmation happens also (regardless of the value of
> eglot-confirm-server-initiated-edits) if any of the files about to be
> changed
> is not yet visited in a buffer.
>
> So to summarize, I would like to hear proposals on how to make
> user confirmation of edits more consistent and predictable across the
> various use cases.
>
> Then, as I've already said, the "diff" method of confirmation sounds in
> principle very robust  and in-line with Emacs' principles. It could
> eventually
> even be made the default.

So in the sense that a diff is presented as a kind of confirmation,
along with the option to apply all the changes immediately.   All in
all, this makes me less certain that merging the change into
`eglot-confirm-server-initiated-edits' is the right approach.

> João





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2022-12-30 15:09           ` Philip Kaludercic
@ 2023-01-04 20:56             ` Felician Nemeth
  2023-06-09  7:55               ` Philip Kaludercic
  0 siblings, 1 reply; 33+ messages in thread
From: Felician Nemeth @ 2023-01-04 20:56 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: Yuan Fu, 60338, João Távora

This is going to be a bit off-topic, but I'm guessing that users (after
a while) get used to what they can expect from a specific language
server when it comes to code-actions.  And with a well written server,
users should never want to partially apply a server initiated text-edit.

Therefore it might be a better UI to apply every text-edit without
questions, display a message when the changes are not visible ("Changed
100 lines in 10 files"), provide a command to view the last text-edit as
a diff, and allow the users to undo the change with a single undo
command.

This UI wouldn't slow down experienced users, and it would allow them to
quickly correct rare mistakes.  Eglot could also teach inexperienced
users with messages like "Changed 2 lines in 1 file.  `undo-view-last'
shows the change."

WDT?





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-01-04 20:56             ` Felician Nemeth
@ 2023-06-09  7:55               ` Philip Kaludercic
  0 siblings, 0 replies; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-09  7:55 UTC (permalink / raw)
  To: Felician Nemeth; +Cc: Yuan Fu, 60338, João Távora

(Sorry for the delay in answering, I forgot about this thread)

Felician Nemeth <felician.nemeth@gmail.com> writes:

> This is going to be a bit off-topic, but I'm guessing that users (after
> a while) get used to what they can expect from a specific language
> server when it comes to code-actions.  And with a well written server,
> users should never want to partially apply a server initiated text-edit.
>
> Therefore it might be a better UI to apply every text-edit without
> questions, display a message when the changes are not visible ("Changed
> 100 lines in 10 files"), provide a command to view the last text-edit as
> a diff, and allow the users to undo the change with a single undo
> command.
>
> This UI wouldn't slow down experienced users, and it would allow them to
> quickly correct rare mistakes.  Eglot could also teach inexperienced
> users with messages like "Changed 2 lines in 1 file.  `undo-view-last'
> shows the change."
>
> WDT?

In principle this should be ok, but there are users who for whatever
reason do not want the language server to modify the files (e.g.
because they don't like it in principle, or because the language is
/not/ well written).  I tend towards that camp, and which is why I wrote
the patch in the first place.

The other issue is that the patch would have to be generated before any
changes are applied, so that the user can inspect it after the server
made the changes, regardless of whether or not they will be interested
or not.  I am not sure how much waste this will generate, and if it is
really worth it, compared to having a diff as a "prompt".





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
@ 2023-06-11 21:33 ` Philip Kaludercic
  2023-06-12 11:56   ` Eli Zaretskii
  1 sibling, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-11 21:33 UTC (permalink / raw)
  To: 60338, João Távora

[-- 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

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-06-11 21:33 ` Philip Kaludercic
@ 2023-06-12 11:56   ` Eli Zaretskii
  2023-06-12 12:35     ` Philip Kaludercic
  0 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2023-06-12 11:56 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: 60338, joaotavora

> From: Philip Kaludercic <philipk@posteo.net>
> Date: Sun, 11 Jun 2023 21:33:40 +0000
> 
> +       ((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))

Isn't there a better way of presenting the edits in human-readable
form than apply them and then run Diff?  What format is used by the
server itself to send the edits?

Besides, this assumes the 'diff' command is available, which is not
portable -- one more reason not to go that way, or at leas provide an
alternative.





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-06-12 11:56   ` Eli Zaretskii
@ 2023-06-12 12:35     ` Philip Kaludercic
  2023-06-12 12:52       ` Eli Zaretskii
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-12 12:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 60338, joaotavora

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Philip Kaludercic <philipk@posteo.net>
>> Date: Sun, 11 Jun 2023 21:33:40 +0000
>> 
>> +       ((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))
>
> Isn't there a better way of presenting the edits in human-readable
> form than apply them and then run Diff?  What format is used by the
> server itself to send the edits?

The server sends a JSON message.  Here is an example from a little toy
project of mine in C, where I intentionally uncommented a #include
directive:

--8<---------------cut here---------------start------------->8---
(:id 51 :jsonrpc "2.0" :result
     [(:diagnostics
       [(:code "-Wimplicit-function-declaration" :message "Implicitly declaring library function 'strcmp' with type 'int (const char *, const char *)' (fix available)" :range
	       (:end
		(:character 21 :line 63)
		:start
		(:character 15 :line 63))
	       :severity 2 :source "clang")]
       :edit
       (:changes
	(:file:///home/philip/Source/sgo/sgo.c
	 [(:newText "#include <string.h>\n" :range
		    (:end
		     (:character 0 :line 25)
		     :start
		     (:character 0 :line 25)))]))
       :isPreferred t :kind "quickfix" :title "Include <string.h> for symbol strcmp")])
--8<---------------cut here---------------end--------------->8---

The server gives me a diagnostic and suggests a change.  The change is
just a replacement of a text range with a string:

https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit

> Besides, this assumes the 'diff' command is available, which is not
> portable -- one more reason not to go that way, or at leas provide an
> alternative.

I am personally fond of this approach, because I get a regular text
buffer that I can store or send someone.  The fact that diff is not
available everywhere (even though I would guess any system with a
language server /should/ have diff installed), is an argument against
enabling this behaviour by default, not against providing this interface.

-- 
Philip Kaludercic





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-06-12 12:35     ` Philip Kaludercic
@ 2023-06-12 12:52       ` Eli Zaretskii
  2023-06-12 13:29         ` Philip Kaludercic
  0 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2023-06-12 12:52 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: 60338, joaotavora

> From: Philip Kaludercic <philipk@posteo.net>
> Cc: 60338@debbugs.gnu.org,  joaotavora@gmail.com
> Date: Mon, 12 Jun 2023 12:35:09 +0000
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Isn't there a better way of presenting the edits in human-readable
> > form than apply them and then run Diff?  What format is used by the
> > server itself to send the edits?
> 
> The server sends a JSON message.  Here is an example from a little toy
> project of mine in C, where I intentionally uncommented a #include
> directive:

And we cannot generate the Diff format from this?

> > Besides, this assumes the 'diff' command is available, which is not
> > portable -- one more reason not to go that way, or at leas provide an
> > alternative.
> 
> I am personally fond of this approach, because I get a regular text
> buffer that I can store or send someone.  The fact that diff is not
> available everywhere (even though I would guess any system with a
> language server /should/ have diff installed), is an argument against
> enabling this behaviour by default, not against providing this interface.

What do we suggest to people who don't have Diff, though?  Nothing?





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-06-12 12:52       ` Eli Zaretskii
@ 2023-06-12 13:29         ` Philip Kaludercic
  2023-06-12 13:41           ` Eli Zaretskii
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-12 13:29 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 60338, joaotavora

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Philip Kaludercic <philipk@posteo.net>
>> Cc: 60338@debbugs.gnu.org,  joaotavora@gmail.com
>> Date: Mon, 12 Jun 2023 12:35:09 +0000
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> > Isn't there a better way of presenting the edits in human-readable
>> > form than apply them and then run Diff?  What format is used by the
>> > server itself to send the edits?
>> 
>> The server sends a JSON message.  Here is an example from a little toy
>> project of mine in C, where I intentionally uncommented a #include
>> directive:
>
> And we cannot generate the Diff format from this?

Certainly we /could/, but I don't think that would be worth the effort
to generate a proper diff manually.

>> > Besides, this assumes the 'diff' command is available, which is not
>> > portable -- one more reason not to go that way, or at leas provide an
>> > alternative.
>> 
>> I am personally fond of this approach, because I get a regular text
>> buffer that I can store or send someone.  The fact that diff is not
>> available everywhere (even though I would guess any system with a
>> language server /should/ have diff installed), is an argument against
>> enabling this behaviour by default, not against providing this interface.
>
> What do we suggest to people who don't have Diff, though?  Nothing?

They still have two different options for
`eglot-confirm-server-initiated-edits', 'confirm (the default) and nil.
This is the current state.

-- 
Philip Kaludercic





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-06-12 13:29         ` Philip Kaludercic
@ 2023-06-12 13:41           ` Eli Zaretskii
  2023-06-13 21:34             ` Philip Kaludercic
  0 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2023-06-12 13:41 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: 60338, joaotavora

> From: Philip Kaludercic <philipk@posteo.net>
> Cc: 60338@debbugs.gnu.org,  joaotavora@gmail.com
> Date: Mon, 12 Jun 2023 13:29:21 +0000
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Philip Kaludercic <philipk@posteo.net>
> >> Cc: 60338@debbugs.gnu.org,  joaotavora@gmail.com
> >> Date: Mon, 12 Jun 2023 12:35:09 +0000
> >> 
> >> Eli Zaretskii <eliz@gnu.org> writes:
> >> 
> >> > Isn't there a better way of presenting the edits in human-readable
> >> > form than apply them and then run Diff?  What format is used by the
> >> > server itself to send the edits?
> >> 
> >> The server sends a JSON message.  Here is an example from a little toy
> >> project of mine in C, where I intentionally uncommented a #include
> >> directive:
> >
> > And we cannot generate the Diff format from this?
> 
> Certainly we /could/, but I don't think that would be worth the effort
> to generate a proper diff manually.

It doesn't necessarily have to be the full-fledged diffs, it could be
something approximate.  After all, this is for human consumption.

> > What do we suggest to people who don't have Diff, though?  Nothing?
> 
> They still have two different options for
> `eglot-confirm-server-initiated-edits', 'confirm (the default) and nil.
> This is the current state.

IOW, we offer them nothing for this feature.

Look, I cannot force you to do anything else, if you don't feel like
it, but I don't like this solution, and think we should try harder.  I
urge you to try to find some way of presenting the edits in
human-readable form that doesn't need running Diff.

Thanks in advance.





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-13 21:34 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 60338, joaotavora

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Philip Kaludercic <philipk@posteo.net>
>> Cc: 60338@debbugs.gnu.org,  joaotavora@gmail.com
>> Date: Mon, 12 Jun 2023 13:29:21 +0000
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> >> From: Philip Kaludercic <philipk@posteo.net>
>> >> Cc: 60338@debbugs.gnu.org,  joaotavora@gmail.com
>> >> Date: Mon, 12 Jun 2023 12:35:09 +0000
>> >> 
>> >> Eli Zaretskii <eliz@gnu.org> writes:
>> >> 
>> >> > Isn't there a better way of presenting the edits in human-readable
>> >> > form than apply them and then run Diff?  What format is used by the
>> >> > server itself to send the edits?
>> >> 
>> >> The server sends a JSON message.  Here is an example from a little toy
>> >> project of mine in C, where I intentionally uncommented a #include
>> >> directive:
>> >
>> > And we cannot generate the Diff format from this?
>> 
>> Certainly we /could/, but I don't think that would be worth the effort
>> to generate a proper diff manually.
>
> It doesn't necessarily have to be the full-fledged diffs, it could be
> something approximate.  After all, this is for human consumption.

Not really, the point is that you can apply these diffs directly.  Also,
I would not say that diffs are not for human consumption.

>> > What do we suggest to people who don't have Diff, though?  Nothing?
>> 
>> They still have two different options for
>> `eglot-confirm-server-initiated-edits', 'confirm (the default) and nil.
>> This is the current state.
>
> IOW, we offer them nothing for this feature.

In the sense of a preview?  Yes, but I would still argue, that that
would be a different feature.  I can take a look at that as well, but I
know of at least a few people who would be explicitly interested in this
kind of a user interface.

> Look, I cannot force you to do anything else, if you don't feel like
> it, but I don't like this solution, and think we should try harder.  I
> urge you to try to find some way of presenting the edits in
> human-readable form that doesn't need running Diff.

I respect your perspective, so I don't want to insist on anything
against your or Joao's will.

> Thanks in advance.





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 1 reply; 33+ messages in thread
From: Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-06-14  6:00 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: Eli Zaretskii, 60338, joaotavora


FWIW I'd definitely want to use this feature when it lands.

>> It doesn't necessarily have to be the full-fledged diffs, it could be
>> something approximate.  After all, this is for human consumption.
>
> Not really, the point is that you can apply these diffs directly.  Also,
> I would not say that diffs are not for human consumption.

I agree, it'd be nice to get a proper diff buffer with all the benefits
of `diff-mode`.

I also see how it would be useful for Emacs to be able to generate such
a diff buffer directly from the change description that the LSP server
provides, without applying the change and running the diff program, but
it doesn't seem quite trivial so I hope that can be implemented as an
enhancement down the line.  (I'd be happy to help with that, BTW.)


-- 
Best,

Eshel





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-14 11:27 UTC (permalink / raw)
  To: Eshel Yaron; +Cc: Eli Zaretskii, 60338, joaotavora

Eshel Yaron <me@eshelyaron.com> writes:

> FWIW I'd definitely want to use this feature when it lands.
>
>>> It doesn't necessarily have to be the full-fledged diffs, it could be
>>> something approximate.  After all, this is for human consumption.
>>
>> Not really, the point is that you can apply these diffs directly.  Also,
>> I would not say that diffs are not for human consumption.
>
> I agree, it'd be nice to get a proper diff buffer with all the benefits
> of `diff-mode`.

Right.

> I also see how it would be useful for Emacs to be able to generate such
> a diff buffer directly from the change description that the LSP server
> provides, without applying the change and running the diff program, but
> it doesn't seem quite trivial so I hope that can be implemented as an
> enhancement down the line.  (I'd be happy to help with that, BTW.)

Yes, and we were to do this, I'd rather add it to a pure-elisp `diff' to
diff.el so that it can be transparently reused anywhere where the "diff"
program is missing.





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-06-14 11:27                 ` Philip Kaludercic
@ 2023-06-18 11:38                   ` Philip Kaludercic
  2023-06-18 15:18                     ` João Távora
  0 siblings, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-18 11:38 UTC (permalink / raw)
  To: Eshel Yaron; +Cc: Eli Zaretskii, 60338, joaotavora

João, do you have anything to add to this discussion?

Philip Kaludercic <philipk@posteo.net> writes:

> Eshel Yaron <me@eshelyaron.com> writes:
>
>> FWIW I'd definitely want to use this feature when it lands.
>>
>>>> It doesn't necessarily have to be the full-fledged diffs, it could be
>>>> something approximate.  After all, this is for human consumption.
>>>
>>> Not really, the point is that you can apply these diffs directly.  Also,
>>> I would not say that diffs are not for human consumption.
>>
>> I agree, it'd be nice to get a proper diff buffer with all the benefits
>> of `diff-mode`.
>
> Right.
>
>> I also see how it would be useful for Emacs to be able to generate such
>> a diff buffer directly from the change description that the LSP server
>> provides, without applying the change and running the diff program, but
>> it doesn't seem quite trivial so I hope that can be implemented as an
>> enhancement down the line.  (I'd be happy to help with that, BTW.)
>
> Yes, and we were to do this, I'd rather add it to a pure-elisp `diff' to
> diff.el so that it can be transparently reused anywhere where the "diff"
> program is missing.

-- 
Philip Kaludercic





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 2 replies; 33+ messages in thread
From: João Távora @ 2023-06-18 15:18 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: Eli Zaretskii, 60338, Eshel Yaron

Philip Kaludercic <philipk@posteo.net> writes:

> João, do you have anything to add to this discussion?

Yes, a bit.

To the "should it use diff" objection, I think it's OK to keep it as is.
Especially if we frame this new 'diff' value to Eglot's
eglot-confirm-server-initiated-edits as a "Present changes as 'diff',
and user takes it from there".  I'd say most programmers can read diffs
and apply them by hand if required.

Anyway this usage of the entirely separate 'diff-mode' is the strongest
point of this feature anyway, it keeps the concerns "propose changes"
and "apply changes" neatly separated.

I also think the second patch adding 'diff-apply-everything' is fine.

Now I've tried your patch very briefly and I like it, in general.  But I
have a few comments after my sig.

João

> @@ -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
> @@ -387,8 +388,13 @@ eglot-events-buffer-size
>                   (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)))

This is the most important comment.  Later on, you add a condition

   (eq eglot-confirm-server-initiated-edits t)

Which would seem to mean "confirm unconditionally".  But 't' is not
presented as an option in the defcustom.  What meaning should it have?
Should it use a prompt or a diff?

>  
>  (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))))))


This is where the 't' value is used.  These types of edits are usually
automatically confirmed.  Why?  Not easy to say, but normally when the
server suggests them they are more localized.  Also by this point the
client already knows what is about to change, whereas we don't know what
diff the server will provide with the ones of "Command"-type, which
require a further round trip.

Anyway it would be very helpful to describe this in the manual and in
the docstrings.  So that a user can say: "even in those simple edits I
want a diff-style presentation of what is about to happen".

This may require another custom option of more added syntax to the
existing eglot-confirm-server-initiated-edits option.

Though I would appreciate if you could take this opportunity to address
them, you can also choose to simply not answer these tough questions.
But in this case, this hunk should be reverted

>  (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))

Why bind buffer-read-only to t here?  Why not re-enter diff-mode at the end
of cosntruction?

> +          (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))

pop-to-buffer is fine, I guess, though I wonder if some users wouldn't
prefer display-buffer.

> +          (font-lock-ensure)))

I think this logic should be in a new helper function.

And the generated buffer name should be more Eglotish, and not hidden.
It should read something like "*EGLOT (<project-name>/<modes>) proposed
changes*".  Ideally the logic for generating the usual prefix "*EGLOT
(<project-name>/<modes>)" should also be extracted, so that we can
switch to something less ugly in the fugure.  In a different commit or
patch, of course.  

> -      (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!")))))))

This last bit of cl-loop change, though better, doesn't seem related to
the new feature being developed.  It's not very important, but it does
complicate reading the patch.  You can always provide such improvements 

>  (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)))

I get the idea and I like it.  Now one can preview the renames.  But if
'eglot-confirm-server-initiated-edits' is nil, now I can't use C-u to
override this decision.  Not necessarily something we need to fix in
your patch, but it should be taken in consideration with the first
comment about documentating the semantics of this variable.
  
>  (defun eglot--region-bounds ()
>    "Region bounds if active, else bounds of things at point."





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  1 sibling, 0 replies; 33+ messages in thread
From: João Távora @ 2023-06-18 22:37 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: Eli Zaretskii, 60338, Eshel Yaron

On Sun, Jun 18, 2023 at 4:15 PM João Távora <joaotavora@gmail.com> wrote:
>
> Philip Kaludercic <philipk@posteo.net> writes:
>
> > João, do you have anything to add to this discussion?
>
> Yes, a bit.
>
> To the "should it use diff" objection, I think it's OK to keep it as is.
> Especially if we frame this new 'diff' value to Eglot's
> eglot-confirm-server-initiated-edits as a "Present changes as 'diff',
> and user takes it from there".  I'd say most programmers can read diffs
> and apply them by hand if required.

After thinking a bit, I realized I missed a point of Eli's objection
to the implementation of this.  It's that the 'diff' program is required
just to generate the presentation, not just to apply it.

But I don't see any other better ways to summarily present changes to
files.  Ediff maybe?  But that's not so good, and it requires splitting the
window and entering a transient mode.  And I'm not sure it doesn't rely
on the diff program itself (but haven't checked).

Anyway, who works with Emacs in a programming context -- which is
what LSP is mainly for -- that also doesn't have the diff program
for patches, vc-diff, etc?

João





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  1 sibling, 1 reply; 33+ messages in thread
From: Philip Kaludercic @ 2023-06-24 16:53 UTC (permalink / raw)
  To: João Távora; +Cc: Eli Zaretskii, 60338, Eshel Yaron

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

João Távora <joaotavora@gmail.com> writes:

> Philip Kaludercic <philipk@posteo.net> writes:
>
>> João, do you have anything to add to this discussion?
>
> Yes, a bit.
>
> To the "should it use diff" objection, I think it's OK to keep it as is.
> Especially if we frame this new 'diff' value to Eglot's
> eglot-confirm-server-initiated-edits as a "Present changes as 'diff',
> and user takes it from there".  I'd say most programmers can read diffs
> and apply them by hand if required.
>
> Anyway this usage of the entirely separate 'diff-mode' is the strongest
> point of this feature anyway, it keeps the concerns "propose changes"
> and "apply changes" neatly separated.
>
> I also think the second patch adding 'diff-apply-everything' is fine.
>
> Now I've tried your patch very briefly and I like it, in general.  But I
> have a few comments after my sig.
>
> João
>
>> @@ -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
>> @@ -387,8 +388,13 @@ eglot-events-buffer-size
>>                   (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)))
>
> This is the most important comment.  Later on, you add a condition
>
>    (eq eglot-confirm-server-initiated-edits t)
>
> Which would seem to mean "confirm unconditionally".  But 't' is not
> presented as an option in the defcustom.  What meaning should it have?
> Should it use a prompt or a diff?

No, that was a thinko, the check has to be replaced with something else.

>>  
>>  (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))))))
>
>
> This is where the 't' value is used.  These types of edits are usually
> automatically confirmed.  Why?  Not easy to say, but normally when the
> server suggests them they are more localized.  Also by this point the
> client already knows what is about to change, whereas we don't know what
> diff the server will provide with the ones of "Command"-type, which
> require a further round trip.
>
> Anyway it would be very helpful to describe this in the manual and in
> the docstrings.  So that a user can say: "even in those simple edits I
> want a diff-style presentation of what is about to happen".
>
> This may require another custom option of more added syntax to the
> existing eglot-confirm-server-initiated-edits option.

> Though I would appreciate if you could take this opportunity to address
> them, you can also choose to simply not answer these tough questions.
> But in this case, this hunk should be reverted

I've gone ahead and fixed the check, to pass 'diff if
`eglot-confirm-server-initiated-edits' was set to 'diff, otherwise nil.
That seems like the most sensible option to me.

>>  (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))
>
> Why bind buffer-read-only to t here?  Why not re-enter diff-mode at the end
> of cosntruction?

I am not sure what you mean by re-enter?  The point of binding
`buffer-read-only' is just to enable `diff-mode-read-only' on enabling
the mode, but we can also set if afterwards.  That would require adding
a defvar though.

>> +          (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))
>
> pop-to-buffer is fine, I guess, though I wonder if some users wouldn't
> prefer display-buffer.

I guess that this is a persistent disagreement between users and their
expectations.  I don't expect it to be resolved here, not at least
because I cannot put my own preferences into words.

>> +          (font-lock-ensure)))
>
> I think this logic should be in a new helper function.
>
> And the generated buffer name should be more Eglotish, and not hidden.
> It should read something like "*EGLOT (<project-name>/<modes>) proposed
> changes*".  

Oh, I was under the impression that this was just the name for
"internal" or hidden buffers, that don't interest the user directory.

>             Ideally the logic for generating the usual prefix "*EGLOT
> (<project-name>/<modes>)" should also be extracted, so that we can
> switch to something less ugly in the fugure.  In a different commit or
> patch, of course.  

Should we then keep it the way this is for now, and when this function
exists make use of it?

>> -      (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!")))))))
>
> This last bit of cl-loop change, though better, doesn't seem related to
> the new feature being developed.  It's not very important, but it does
> complicate reading the patch.  You can always provide such improvements 

Reverted.

>>  (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)))
>
> I get the idea and I like it.  Now one can preview the renames.  But if
> 'eglot-confirm-server-initiated-edits' is nil, now I can't use C-u to
> override this decision.  Not necessarily something we need to fix in
> your patch, but it should be taken in consideration with the first
> comment about documentating the semantics of this variable.

I've modified it, see below.

>>  (defun eglot--region-bounds ()
>>    "Region bounds if active, else bounds of things at point."


[-- Attachment #2: Type: text/plain, Size: 2313 bytes --]

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 9c9c3152472..9c76d4abdef 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -747,7 +747,8 @@ eglot-execute
      (((Command)) (eglot--request server :workspace/executeCommand action))
      (((CodeAction) edit command)
       (when edit
-        (eglot--apply-workspace-edit edit (eq eglot-confirm-server-initiated-edits t)))
+        (let ((confirm (and (eq eglot-confirm-server-initiated-edits 'diff) 'diff)))
+          (eglot--apply-workspace-edit edit confirm)))
       (when command (eglot--request server :workspace/executeCommand command))))))
 
 (cl-defgeneric eglot-initialization-options (server)
@@ -3401,7 +3402,9 @@ eglot--apply-text-edits
       (progress-reporter-done reporter))))
 
 (defun eglot--apply-workspace-edit (wedit &optional confirm)
-  "Apply the workspace edit WEDIT.  If CONFIRM, ask user first."
+  "Apply the workspace edit WEDIT.
+CONFIRM should have a value as specified in the user option
+`eglot-confirm-server-initiated-edits'."
   (eglot--dbind ((WorkspaceEdit) changes documentChanges) wedit
     (let ((prepared
            (mapcar (eglot--lambda ((TextDocumentEdit) textDocument edits)
@@ -3448,7 +3451,8 @@ eglot--apply-workspace-edit
                    (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 (path edits version) in prepared
+       ((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!")))))))
@@ -3466,7 +3470,7 @@ eglot-rename
    (eglot--request (eglot--current-server-or-lose)
                    :textDocument/rename `(,@(eglot--TextDocumentPositionParams)
                                           :newName ,newname))
-   (and current-prefix-arg eglot-confirm-server-initiated-edits)))
+   (and current-prefix-arg (or eglot-confirm-server-initiated-edits 'confirm))))
 
 (defun eglot--region-bounds ()
   "Region bounds if active, else bounds of things at point."

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


-- 
Philip Kaludercic

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 2 replies; 33+ messages in thread
From: João Távora @ 2023-09-01  0:06 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: dmitry, Eli Zaretskii, 60338, Eshel Yaron

Hello,

I've finally pushed this to master, after working on mostly on the user
confirmation logic.  I deprecated `eglot-confirm-server-initiated-edits'
(had crap semantics anyway) and replaced it with a shiny new
'eglot-confirm-server-edits', with a simpler name but much more powerful
(see its docstring).

I left in Philip's new implementation of the diff confirmation but I
didn't make it the default (although it's my preferred one) because it
might appeal to everyone used to the "summary" prompt.

Please have a look, give it some testing, and tell me if I missed
anything.

Thanks to Philip for the original idea.

João





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  1 sibling, 0 replies; 33+ messages in thread
From: Philip Kaludercic @ 2023-09-01  5:18 UTC (permalink / raw)
  To: João Távora; +Cc: dmitry, Eli Zaretskii, 60338, Eshel Yaron

João Távora <joaotavora@gmail.com> writes:

> Hello,
>
> I've finally pushed this to master, after working on mostly on the user
> confirmation logic.  I deprecated `eglot-confirm-server-initiated-edits'
> (had crap semantics anyway) and replaced it with a shiny new
> 'eglot-confirm-server-edits', with a simpler name but much more powerful
> (see its docstring).
>
> I left in Philip's new implementation of the diff confirmation but I
> didn't make it the default (although it's my preferred one) because it
> might appeal to everyone used to the "summary" prompt.
>
> Please have a look, give it some testing, and tell me if I missed
> anything.
>
> Thanks to Philip for the original idea.

Thank you very much for finishing up the patch to the end.

> João





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  1 sibling, 2 replies; 33+ messages in thread
From: Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-01 21:12 UTC (permalink / raw)
  To: João Távora; +Cc: dmitry, Philip Kaludercic, 60338, Eli Zaretskii

João Távora <joaotavora@gmail.com> writes:

> I've finally pushed this to master, after working on mostly on the user
> confirmation logic.  I deprecated `eglot-confirm-server-initiated-edits'
> (had crap semantics anyway) and replaced it with a shiny new
> 'eglot-confirm-server-edits', with a simpler name but much more powerful
> (see its docstring).
>
[...]
>
> Please have a look, give it some testing, and tell me if I missed
> anything.

Great stuff!  One issue I came across is with unsaved buffers.
Here's a recipe:

With emacs -Q, set `eglot-confirm-server-edits` to `diff`.  In a fresh
git repository, create a new file `foo.go` and insert the following:

--8<---------------cut here---------------start------------->8---
package baz

type Foobar interface {}
--8<---------------cut here---------------end--------------->8---

Now save the file, and do `M-x go-ts-mode` followed by `M-x eglot` to
connect to an LSP server (`gopls` in my case).  Without saving the
buffer, insert `type Baz interface {}` after the last line:

--8<---------------cut here---------------start------------->8---
package baz

type Foobar interface {}
type Baz interface {}
--8<---------------cut here---------------end--------------->8---

With point on `Baz`, do `M-x eglot-rename RET Spam RET`.  Emacs displays
the following diff in *EGLOT proposed server changes*:

--8<---------------cut here---------------start------------->8---
diff -u /Users/eshelyaron/tmp/bug/foo.go /var/folders/q1/h0vdt_hx00zgbv3bpyx2vdr00000gp/T/buffer-content-sgzkbC
--- /Users/eshelyaron/tmp/bug/foo.go	2023-09-01 20:55:02
+++ /var/folders/q1/h0vdt_hx00zgbv3bpyx2vdr00000gp/T/buffer-content-sgzkbC	2023-09-01 20:55:07
@@ -1,3 +1,4 @@
 package baz
 
 type Foobar interface {}
+Spam
\ No newline at end of file

Diff finished.  Fri Sep  1 20:55:08 2023
--8<---------------cut here---------------end--------------->8---

We clearly get an incorrect patch. It seems like the patch is produced
by comparing the modified version of the buffer to the visited file,
instead of comparing it to the current buffer contents.

Another, smaller issue is that while `eglot-rename` is creating the
patch it prints messages along the lines of:

    [eglot] applying 1 edits to `*temp*-207900'

Which feels less appropriate in these settings.

Lastly, I have an improvement suggestion for the `:type` of
`eglot-confirm-server-edits` to make it nicer to deal with via Custom:

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 8d95019c3ed..afb99e4b9ca 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -413,12 +413,17 @@ eglot-confirm-server-edits
 `eglot-code-actions', `eglot-code-action-quickfix', etc.  ACTION
 is one of the symbols described above.  The value `t' for COMMAND
 is accepted and its ACTION is the default value."
-  :type '(choice (const :tag "Use diff" diff)
+  :type (let ((basic-choices
+               '((const :tag "Use diff" diff)
                  (const :tag "Summarize and prompt" summary)
                  (const :tag "Maybe use diff" maybe-diff)
                  (const :tag "Maybe summarize and prompt" maybe-summary)
-                 (const :tag "Don't confirm" nil)
-                 (alist :tag "Per-command alist")))
+                 (const :tag "Don't confirm" nil))))
+          `(choice ,@basic-choices
+                   (alist :tag "Per-command alist"
+                          :key-type (choice (function :tag "Command")
+                                            (const :tag "Default" t))
+                          :value-type (choice . ,basic-choices)))))
 
 (defcustom eglot-extend-to-xref nil
   "If non-nil, activate Eglot in cross-referenced non-project files."





^ permalink raw reply related	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  1 sibling, 0 replies; 33+ messages in thread
From: João Távora @ 2023-09-01 21:19 UTC (permalink / raw)
  To: Eshel Yaron; +Cc: dmitry, Philip Kaludercic, 60338, Eli Zaretskii

On Fri, Sep 1, 2023 at 10:13 PM Eshel Yaron <me@eshelyaron.com> wrote:
>
> João Távora <joaotavora@gmail.com> writes:
>
> > I've finally pushed this to master, after working on mostly on the user
> > confirmation logic.  I deprecated `eglot-confirm-server-initiated-edits'
> > (had crap semantics anyway) and replaced it with a shiny new
> > 'eglot-confirm-server-edits', with a simpler name but much more powerful
> > (see its docstring).
> >
> [...]
> >
> > Please have a look, give it some testing, and tell me if I missed
> > anything.
>
> Great stuff!  One issue I came across is with unsaved buffers.

I'm fixing that as we speak.  I'll read your recipe later, but
I'm 90% sure it's the same stuff I've been trying to fix for
most of today.

> Lastly, I have an improvement suggestion for the `:type` of
> `eglot-confirm-server-edits` to make it nicer to deal with via Custom:
>
> diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
> index 8d95019c3ed..afb99e4b9ca 100644
> --- a/lisp/progmodes/eglot.el
> +++ b/lisp/progmodes/eglot.el
> @@ -413,12 +413,17 @@ eglot-confirm-server-edits
>  `eglot-code-actions', `eglot-code-action-quickfix', etc.  ACTION
>  is one of the symbols described above.  The value `t' for COMMAND
>  is accepted and its ACTION is the default value."
> -  :type '(choice (const :tag "Use diff" diff)
> +  :type (let ((basic-choices
> +               '((const :tag "Use diff" diff)
>                   (const :tag "Summarize and prompt" summary)
>                   (const :tag "Maybe use diff" maybe-diff)
>                   (const :tag "Maybe summarize and prompt" maybe-summary)
> -                 (const :tag "Don't confirm" nil)
> -                 (alist :tag "Per-command alist")))
> +                 (const :tag "Don't confirm" nil))))
> +          `(choice ,@basic-choices
> +                   (alist :tag "Per-command alist"
> +                          :key-type (choice (function :tag "Command")
> +                                            (const :tag "Default" t))
> +                          :value-type (choice . ,basic-choices)))))
>
>  (defcustom eglot-extend-to-xref nil
>    "If non-nil, activate Eglot in cross-referenced non-project files."

I'll look at this bit, too.   I looks sane, and I'm glad I found a custom.el
expert to call on :-)

João





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  1 sibling, 1 reply; 33+ messages in thread
From: João Távora @ 2023-09-01 22:01 UTC (permalink / raw)
  To: Eshel Yaron; +Cc: dmitry, Philip Kaludercic, 60338, Eli Zaretskii

OK, Eshel

I think I fixed all these issues in the latest
fdf6c164efd0bb467d0d46460161c146e955a48c which I just
pushed to master.  Please have a look.

Thanks,
João

On Fri, Sep 1, 2023 at 10:13 PM Eshel Yaron <me@eshelyaron.com> wrote:
>
> João Távora <joaotavora@gmail.com> writes:
>
> > I've finally pushed this to master, after working on mostly on the user
> > confirmation logic.  I deprecated `eglot-confirm-server-initiated-edits'
> > (had crap semantics anyway) and replaced it with a shiny new
> > 'eglot-confirm-server-edits', with a simpler name but much more powerful
> > (see its docstring).
> >
> [...]
> >
> > Please have a look, give it some testing, and tell me if I missed
> > anything.
>
> Great stuff!  One issue I came across is with unsaved buffers.
> Here's a recipe:
>
> With emacs -Q, set `eglot-confirm-server-edits` to `diff`.  In a fresh
> git repository, create a new file `foo.go` and insert the following:
>
> --8<---------------cut here---------------start------------->8---
> package baz
>
> type Foobar interface {}
> --8<---------------cut here---------------end--------------->8---
>
> Now save the file, and do `M-x go-ts-mode` followed by `M-x eglot` to
> connect to an LSP server (`gopls` in my case).  Without saving the
> buffer, insert `type Baz interface {}` after the last line:
>
> --8<---------------cut here---------------start------------->8---
> package baz
>
> type Foobar interface {}
> type Baz interface {}
> --8<---------------cut here---------------end--------------->8---
>
> With point on `Baz`, do `M-x eglot-rename RET Spam RET`.  Emacs displays
> the following diff in *EGLOT proposed server changes*:
>
> --8<---------------cut here---------------start------------->8---
> diff -u /Users/eshelyaron/tmp/bug/foo.go /var/folders/q1/h0vdt_hx00zgbv3bpyx2vdr00000gp/T/buffer-content-sgzkbC
> --- /Users/eshelyaron/tmp/bug/foo.go    2023-09-01 20:55:02
> +++ /var/folders/q1/h0vdt_hx00zgbv3bpyx2vdr00000gp/T/buffer-content-sgzkbC      2023-09-01 20:55:07
> @@ -1,3 +1,4 @@
>  package baz
>
>  type Foobar interface {}
> +Spam
> \ No newline at end of file
>
> Diff finished.  Fri Sep  1 20:55:08 2023
> --8<---------------cut here---------------end--------------->8---
>
> We clearly get an incorrect patch. It seems like the patch is produced
> by comparing the modified version of the buffer to the visited file,
> instead of comparing it to the current buffer contents.
>
> Another, smaller issue is that while `eglot-rename` is creating the
> patch it prints messages along the lines of:
>
>     [eglot] applying 1 edits to `*temp*-207900'
>
> Which feels less appropriate in these settings.
>
> Lastly, I have an improvement suggestion for the `:type` of
> `eglot-confirm-server-edits` to make it nicer to deal with via Custom:
>
> diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
> index 8d95019c3ed..afb99e4b9ca 100644
> --- a/lisp/progmodes/eglot.el
> +++ b/lisp/progmodes/eglot.el
> @@ -413,12 +413,17 @@ eglot-confirm-server-edits
>  `eglot-code-actions', `eglot-code-action-quickfix', etc.  ACTION
>  is one of the symbols described above.  The value `t' for COMMAND
>  is accepted and its ACTION is the default value."
> -  :type '(choice (const :tag "Use diff" diff)
> +  :type (let ((basic-choices
> +               '((const :tag "Use diff" diff)
>                   (const :tag "Summarize and prompt" summary)
>                   (const :tag "Maybe use diff" maybe-diff)
>                   (const :tag "Maybe summarize and prompt" maybe-summary)
> -                 (const :tag "Don't confirm" nil)
> -                 (alist :tag "Per-command alist")))
> +                 (const :tag "Don't confirm" nil))))
> +          `(choice ,@basic-choices
> +                   (alist :tag "Per-command alist"
> +                          :key-type (choice (function :tag "Command")
> +                                            (const :tag "Default" t))
> +                          :value-type (choice . ,basic-choices)))))
>
>  (defcustom eglot-extend-to-xref nil
>    "If non-nil, activate Eglot in cross-referenced non-project files."



-- 
João Távora





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 1 reply; 33+ messages in thread
From: Eshel Yaron via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-02  6:13 UTC (permalink / raw)
  To: João Távora; +Cc: dmitry, Philip Kaludercic, 60338, Eli Zaretskii

João Távora <joaotavora@gmail.com> writes:

> OK, Eshel
>
> I think I fixed all these issues in the latest
> fdf6c164efd0bb467d0d46460161c146e955a48c which I just
> pushed to master.  Please have a look.

Looks good, and works well too, thank you!






^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 1 reply; 33+ messages in thread
From: João Távora @ 2023-09-02  9:55 UTC (permalink / raw)
  To: Eshel Yaron; +Cc: dmitry, Philip Kaludercic, Eli Zaretskii, 60338-done

Eshel Yaron <me@eshelyaron.com> writes:

> João Távora <joaotavora@gmail.com> writes:
>
>> OK, Eshel
>>
>> I think I fixed all these issues in the latest
>> fdf6c164efd0bb467d0d46460161c146e955a48c which I just
>> pushed to master.  Please have a look.
>
> Looks good, and works well too, thank you!

I'm glad to hear that.  I must say that altough I like the new
functionality myself -- both the new user option and the diff view --
the current implementation of the latter leaves much to be desired.

I've pushed a further commit to simplify it, but it's complicated and
brittle.  A little change to diff.el would simplify some of it, but then
you can't easily publish those changes to Emacs < 29.

Anyway I invite everyone to have a look and try to improve it, perhaps
moving it out of Eglot into the shiny new "refactoring interface" if
those ever become a thing.

In the meantime, I'm going to close this.  Thanks everybody.

João







^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-09-02  9:55                                 ` João Távora
@ 2023-09-07  1:00                                   ` Dmitry Gutov
  2023-09-07  6:28                                     ` Juri Linkov
  0 siblings, 1 reply; 33+ messages in thread
From: Dmitry Gutov @ 2023-09-07  1:00 UTC (permalink / raw)
  To: João Távora, Eshel Yaron
  Cc: Philip Kaludercic, Eli Zaretskii, 60338-done

On 02/09/2023 12:55, João Távora wrote:
> Anyway I invite everyone to have a look and try to improve it, perhaps
> moving it out of Eglot into the shiny new "refactoring interface" if
> those ever become a thing.

Regarding the diff approach vs. "shiny new", I've run a little 
comparison with VS Code: doing a rename of 'glyph_row' across the Emacs 
codebase (a moderately sized project) takes about ~1.44s to produce the 
diff.

VS Code's "Refactor Preview" takes about 1/3rd of that time to show. I'm 
guessing that's because it just paints whatever the language server 
returns instead of doing a bunch of process calls. If that is the case, 
one of the approaches seems to have a fundamental performance advantage.

Not to belittle the new addition, though - it's a good step for Eglot.

Two other thoughts after trying it:

- I would probably want to bind the originally proposed 
'diff-apply-everything' to 'C-c C-c' (e.g. "commit the change to disk"), 
but that's already taken in diff-mode.

- 'git diff' has a less-frequently used option called '--word-diff' 
which could reduce the length of the diff in the case I am testing (but 
I guess diff-mode would have to be updated to support that output). And 
another way in that direction would be to reduce the size of the diff 
context (e.g. to 0).





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  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
  0 siblings, 2 replies; 33+ messages in thread
From: Juri Linkov @ 2023-09-07  6:28 UTC (permalink / raw)
  To: Dmitry Gutov
  Cc: Philip Kaludercic, 60338, Eshel Yaron, João Távora,
	Eli Zaretskii

> Two other thoughts after trying it:
>
> - I would probably want to bind the originally proposed
>   'diff-apply-everything' to 'C-c C-c' (e.g. "commit the change to disk"),
>   but that's already taken in diff-mode.

If there are no more intuitive keys, maybe then use the proposed by Philip
'C-c C-x'?

> - 'git diff' has a less-frequently used option called '--word-diff' which
>   could reduce the length of the diff in the case I am testing (but I guess
>   diff-mode would have to be updated to support that output). And another
>   way in that direction would be to reduce the size of the diff context
>  (e.g. to 0).

Whereas the output of '--word-diff' is closer to VS Code, but it's hard to read
for anyone accustomed to the diff unified format ;-)

BTW, it looks '--word-diff' could help Samuel in bug#61396?





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-09-07  6:28                                     ` Juri Linkov
@ 2023-09-07 12:41                                       ` Dmitry Gutov
  2023-09-07 12:45                                       ` Dmitry Gutov
  1 sibling, 0 replies; 33+ messages in thread
From: Dmitry Gutov @ 2023-09-07 12:41 UTC (permalink / raw)
  To: Juri Linkov
  Cc: Philip Kaludercic, 60338, Eshel Yaron, João Távora,
	Eli Zaretskii

On 07/09/2023 09:28, Juri Linkov wrote:
>> Two other thoughts after trying it:
>>
>> - I would probably want to bind the originally proposed
>>    'diff-apply-everything' to 'C-c C-c' (e.g. "commit the change to disk"),
>>    but that's already taken in diff-mode.
> If there are no more intuitive keys, maybe then use the proposed by Philip
> 'C-c C-x'?

IDK, seems a little dangerous given to its proximity to 'C-x C-c'.

Personally, the existing 'C-c C-c' binding seems unnecessary given that 
this command has 6 other bindings already.

If we chose to replace it, we could just start the new command with a 
prompt (Apply all changes from diff?), so it doesn't surprise anyone.





^ permalink raw reply	[flat|nested] 33+ messages in thread

* bug#60338: [PATCH] Add option to present server changes as diffs
  2023-09-07  6:28                                     ` Juri Linkov
  2023-09-07 12:41                                       ` Dmitry Gutov
@ 2023-09-07 12:45                                       ` Dmitry Gutov
  1 sibling, 0 replies; 33+ messages in thread
From: Dmitry Gutov @ 2023-09-07 12:45 UTC (permalink / raw)
  To: Juri Linkov
  Cc: Philip Kaludercic, 60338, Eshel Yaron, João Távora,
	Eli Zaretskii

On 07/09/2023 09:28, Juri Linkov wrote:
> BTW, it looks '--word-diff' could help Samuel in bug#61396?

Quite possible, but it'd also require changes in diff-mode, and the 
currently discussed alternative (in refine logic, I think) might be more 
localized.

Also, 'git apply' doesn't work on word diffs :(





^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2023-09-07 12:45 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).