Skip to content

After-completion textEdit should be triggered by CompleteChanged instead of CompleteDone #1625

@suguruwataru

Description

@suguruwataru

Per spec, 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:

autocmd CompleteDone * call s:on_complete_done()

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:

fn main() {
    let mut a = 9;
    let b = a;
}

For some reason, I intend to change this into

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

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

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

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions