From: João Távora Date: Mon, 20 Jan 2025 18:58:05 +0000 (+0000) Subject: Eglot: add support for insertReplaceEdit (bug#73857) X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=d034a60b89884fd8c0b21094304218756892d927;p=emacs.git Eglot: add support for insertReplaceEdit (bug#73857) * lisp/progmodes/eglot.el (eglot-server-programs): Mention zig-ts-mode. (eglot--lsp-interface-alist): Describe 'InsertReplaceEdit'. (eglot-client-capabilities): Advertise 'insertReplaceSupport'. (eglot-completion-at-point): Consider 'InsertReplaceEdit'. (eglot--apply-text-edits): Consider 'InsertReplaceEdit'. * test/lisp/progmodes/eglot-tests.el (eglot-test-zig-insert-replace-completion): New test. Special thanks to kcbanner@gmail.com (cherry picked from commit 1143cf09a339d57051a4341103c9e342d8876649) --- diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index d2b175d3b9c..7f6a686a371 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -312,7 +312,7 @@ automatically)." ((lua-mode lua-ts-mode) . ,(eglot-alternatives '("lua-language-server" "lua-lsp"))) (yang-mode . ("yang-language-server")) - (zig-mode . ("zls")) + ((zig-mode zig-ts-mode) . ("zls")) ((css-mode css-ts-mode) . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio") ("css-languageserver" "--stdio")))) @@ -656,6 +656,7 @@ This can be useful when using docker to run a language server.") (: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)) @@ -968,7 +969,8 @@ object." ["documentation" "details" "additionalTextEdits"]) - :tagSupport (:valueSet [1])) + :tagSupport (:valueSet [1]) + :insertReplaceSupport t) :contextSupport t) :hover (list :dynamicRegistration :json-false :contentFormat (eglot--accepted-formats)) @@ -3406,8 +3408,15 @@ for which LSP on-type-formatting should be requested." ;; insertion to potentially cancel an essential ;; resolution request (github#1474). 'dont-cancel-on-input) - (let ((snippet-fn (and (eql insertTextFormat 2) - (eglot--snippet-expansion-fn)))) + (let* ((snippet-fn (and (eql insertTextFormat 2) + (eglot--snippet-expansion-fn))) + (apply-edit + (lambda (range text) + (pcase-let ((`(,beg . ,end) + (eglot-range-region range))) + (delete-region beg end) + (goto-char beg) + (funcall (or snippet-fn #'insert) text))))) (cond (textEdit ;; Revert buffer back to state when the edit ;; was obtained from server. If a `proxy' @@ -3416,12 +3425,11 @@ for which LSP on-type-formatting should be requested." ;; 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-range-region range))) - (delete-region beg end) - (goto-char beg) - (funcall (or snippet-fn #'insert) newText)))) + (eglot--dcase textEdit + (((TextEdit) range newText) + (funcall apply-edit range newText)) + (((InsertReplaceEdit) newText replace) + (funcall apply-edit replace newText)))) (snippet-fn ;; A snippet should be inserted, but using plain ;; `insertText'. This requires us to delete the @@ -3650,8 +3658,12 @@ If SILENT, don't echo progress in mode-line." (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 (edit) + (eglot--dcase 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 diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index 2dc45df7d9e..d54654b84ac 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -725,6 +725,21 @@ directory hierarchy." "fn test() -> i32 { let v: usize = 1; v.count_ones.1234567890;") (buffer-string)))))) +(ert-deftest eglot-test-zig-insert-replace-completion () + "Test zls's use of 'InsertReplaceEdit'." + (skip-unless (functionp 'zig-ts-mode)) + (eglot--with-fixture + `(("project" . + (("main.zig" . + ,(concat "const Foo = struct {correct_name: u32,\n};\n" + "fn example(foo: Foo) u32 {return foo.correc_name; }"))))) + (with-current-buffer + (eglot--find-file-noselect "project/main.zig") + (should (eglot--tests-connect)) + (search-forward "foo.correc") + (completion-at-point) + (should (looking-back "correct_name"))))) + (ert-deftest eglot-test-basic-xref () "Test basic xref functionality in a clangd LSP." (skip-unless (executable-find "clangd"))