After-completion `textEdit` should be triggered by `CompleteChanged` instead of `CompleteDone`
Per [spec](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/), `textEdit` is
> An edit which is applied to a document when selecting this completion.
In vim's context, this is `CompleteChanged` rather than `CompleteDone`.
At this moment, the `textEdit` is applied on `CompleteDone`:
https://github.com/prabirshrestha/vim-lsp/blob/cb3406a591cd1ceb50e6926a9aac63c11becc100/autoload/lsp/ui/vim/completion.vim#L8
I'll try to illustrate the problem caused by this below.
Some language servers would send `textEdit` to make the completion text replace the word the cursor's at. This is pretty bad and annoying design IMO but seems to be the standard in the IDE world, JetBrains IDEs do this at least. `rust-analyzer` is a language server that does this. So, taking this Rust snippet as an example:
```rust
fn main() {
let mut a = 9;
let b = a;
}
```
For some reason, I intend to change this into
```rust
fn main() {
let mut a = 9;
let b = std::mem::take(&mut a);
}
```
In insert mode, I place my cursor at the `a` of `let b = a;`, and enter `s`. Completion popup tells me that I can complete into `std`. I use e.g. tab to select `std`. Here, the expected behavior is (once again, I don't think this should be the expected behavior, but looking at how e.g. JetBrains IDEA work they very much expect this), a `textEdit` happens, and the completion `std` replaces `a`, so we get
```rust
fn main() {
let mut a = 9;
let b = std;
}
```
Cursor's between `std` and `;`
However, since `textEdit` is handled on `CompleteDone`, but not `CompleteChanged`, at this moment, `vim-lsp` will lead us to
```rust
fn main() {
let mut a = 9;
let b = stda;
}
```
Cursor's between `std` and `a`.
And, we continue to type, our input of `:`, a trigger character, which causes completion refresh, closes the completion popup and triggers `CompleteDone`. The `textEdit` fires, and removes the `a` now, leaving us to
```rust
fn main() {
let mut a = 9;
let b = std;
}
```
So, our current behavior is a bit more annoying than the expected behavior, which is annoying enough: it wastes an extra keypress and is more surprising. A completion replacing the word under cursor is weird but is understandable and predictable, but that an input of `:`, which isn't even a completion and shouldn't lead to `textEdit`, deletes not only itself but also the word after cursor, really get a user caught off-guard.
Simply changing the autocmd used to `CompleteChanged` will solve this problem in vanilla vim-lsp. However, if snippet integration is used, the `textEdit` handling logic doesn't seem to be able to handle the much more complex edits, and won't update the snippet applied properly on change of completion options.
7 条评论