all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Eli Zaretskii <eliz@gnu.org>
To: "Casey Banner" <kcbanner@gmail.com>,
	"João Távora" <joaotavora@gmail.com>
Cc: 73857@debbugs.gnu.org
Subject: bug#73857: [PATCH] * lisp/progmodes/eglot.el: add support for insertReplaceEdit
Date: Fri, 18 Oct 2024 08:56:32 +0300	[thread overview]
Message-ID: <86plnynecv.fsf@gnu.org> (raw)
In-Reply-To: <CAKHgf3D4F_OBQaDqZ_cDFCzEUXNqQFPUXbTDxDVQgJ0e=viNTQ@mail.gmail.com> (message from Casey Banner on Thu, 17 Oct 2024 20:54:38 -0400)

> From: Casey Banner <kcbanner@gmail.com>
> Date: Thu, 17 Oct 2024 20:54:38 -0400
> 
> Since 3.16, LSP supports the capability `insertReplaceSupport`. This
> allows textEdit to be an `InsertReplaceEdit` see:
> (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#insertReplaceEdit)
> 
> This patch adds support for this capability, and uses the `replace`
> field of the `InsertReplaceEdit`. Original functionality (ie.
> `TextEdit`) is preserved.
> 
> The benefits of this were originally discussed here:
> https://github.com/joaotavora/eglot/discussions/1456, but this is a summary:
> 
> Consider this file:
> 
> ```
> const Foo = struct {
>     correct_name: u32,
> };
> 
> fn example(foo: Foo) u32 {
>     return foo.correct_name;
> }
> ```
> 
> 1. Place the cursor on 6:22 (the _ in correct_name)
> 2. Backspace once to delete the t
> 3. Receive the following LSP message: `<-- textDocument/completion[20] {"jsonrpc":"2.0","id":20,"result":
> {"isIncomplete":false,"items":[{"label":"correct_name","kind":5,"detail":"u32","documentation":
> {"kind":"plaintext","value":""},"sortText":"2_correct_name","textEdit":{"range":{"start":
> {"line":5,"character":15},"end":{"line":5,"character":21}},"newText":"correct_name"}}]}}`
> 4. Accept the completion
> 5. The buffer now contains `    return foo.correct_name_name;` on line 6
> 
> I expected it to replace the entire token, resulting in `    return foo.correct_name;`.
> 
> Indeed with this patch applied (and an LSP that supports the
> capability), the behaviour I expected is now what happens.
> 
> This is the first real elisp that I've written besides configuration, so
> I'm not sure if this is the correct way, but it seems to work for me.
> 
> Patch is attached.

Thanks.

João, any comments?

> From bbf79f95636d699ccf9ba7028e6c3dce23af2378 Mon Sep 17 00:00:00 2001
> From: kcbanner <kcbanner@gmail.com>
> Date: Thu, 17 Oct 2024 00:43:32 -0400
> Subject: [PATCH] * lisp/progmodes/eglot.el: add support for insertReplaceEdit
> 
> ---
>  lisp/progmodes/eglot.el | 25 +++++++++++++++++++------
>  1 file changed, 19 insertions(+), 6 deletions(-)
> 
> diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
> index 0a14146a245..8285506928f 100644
> --- a/lisp/progmodes/eglot.el
> +++ b/lisp/progmodes/eglot.el
> @@ -647,6 +647,7 @@ eglot--uri-path-allowed-chars
>                        (:detail :deprecated :children))
>        (TextDocumentEdit (:textDocument :edits) ())
>        (TextEdit (:range :newText))
> +      (InsertReplaceEdit (:newText :insert :replace))
>        (VersionedTextDocumentIdentifier (:uri :version) ())
>        (WorkDoneProgress (:kind) (:title :message :percentage :cancellable))
>        (WorkspaceEdit () (:changes :documentChanges))
> @@ -959,7 +960,8 @@ eglot-client-capabilities
>                                                         ["documentation"
>                                                          "details"
>                                                          "additionalTextEdits"])
> -                                      :tagSupport (:valueSet [1]))
> +                                      :tagSupport (:valueSet [1])
> +                                      :insertReplaceSupport t)
>                                      :contextSupport t)
>               :hover              (list :dynamicRegistration :json-false
>                                         :contentFormat (eglot--accepted-formats))
> @@ -3368,12 +3370,19 @@ eglot-completion-at-point
>                          ;; state, _not_ the current "foo.bar".
>                          (delete-region orig-pos (point))
>                          (insert (substring bounds-string (- orig-pos (car bounds))))
> -                        (eglot--dbind ((TextEdit) range newText) textEdit
> -                          (pcase-let ((`(,beg . ,end)
> +                        (eglot--dcase textEdit
> +                          (((TextEdit) range newText)
> +                           (pcase-let ((`(,beg . ,end)
>                                         (eglot-range-region range)))
>                              (delete-region beg end)
>                              (goto-char beg)
> -                            (funcall (or snippet-fn #'insert) newText))))
> +                            (funcall (or snippet-fn #'insert) newText)))
> +                          (((InsertReplaceEdit) newText replace)
> +                           (pcase-let ((`(,beg . ,end)
> +                                        (eglot-range-region replace)))
> +                             (delete-region beg end)
> +                             (goto-char beg)
> +                             (funcall (or snippet-fn #'insert) newText)))))
>                         (snippet-fn
>                          ;; A snippet should be inserted, but using plain
>                          ;; `insertText'.  This requires us to delete the
> @@ -3602,8 +3611,12 @@ eglot--apply-text-edits
>                            (replace-buffer-contents temp)))
>                        (when reporter
>                          (eglot--reporter-update reporter (cl-incf done))))))))
> -            (mapcar (eglot--lambda ((TextEdit) range newText)
> -                      (cons newText (eglot-range-region range 'markers)))
> +            (mapcar (lambda (text-edit-or-insert-replace-edit)
> +                      (eglot--dcase text-edit-or-insert-replace-edit
> +                        (((TextEdit) range newText)
> +                         (cons newText (eglot-range-region range 'markers)))
> +                        (((InsertReplaceEdit) newText replace)
> +                         (cons newText (eglot-range-region replace 'markers)))))
>                      (reverse edits)))
>        (undo-amalgamate-change-group change-group)
>        (when reporter
> -- 
> 2.46.0.windows.1
> 





  reply	other threads:[~2024-10-18  5:56 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-18  0:54 bug#73857: [PATCH] * lisp/progmodes/eglot.el: add support for insertReplaceEdit Casey Banner
2024-10-18  5:56 ` Eli Zaretskii [this message]
2024-11-02 11:56   ` Eli Zaretskii
2024-11-02 13:22     ` João Távora
2024-11-04  0:24       ` Dmitry Gutov

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=86plnynecv.fsf@gnu.org \
    --to=eliz@gnu.org \
    --cc=73857@debbugs.gnu.org \
    --cc=joaotavora@gmail.com \
    --cc=kcbanner@gmail.com \
    /path/to/YOUR_REPLY

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

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

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

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