Completion should consider parts already in buffer
Consider this c++ snippet, with clangd server:
int main() {
int uniqueVariableName = 0;
uniqueVariable = 42;
}
If I move the cursor to the "V" in "uniqueVariable", and execute completion-at-point, lsp-mode won't consider that "Variable" is already in the buffer, so the buffer will have "uniqueVariableNameVariable = 42;" after completion. Similarly, if I activate completion-preview-mode (this is a new feature of emacs master branch), preview will have "unique(VariableName)Variable" (I put the previewed part in parentheses). So the preview consistent with the completion, but it doesn't do what a user might expect.
For this case, eglot works in a nicer way: it will correctly complete "uniqueVariable" to "uniqueVariableName" (when cursor is at "V"), and preview also shows "uniqueVariable(Name)". It can even correctly complete "uVariable" when the cursor is at "V".
Note: to make the preview visible, you need to apply some edit to the text. Like press backspace to delete the "e", and then reinsert it.
(I don't have any experience with eglot, I just checked this particular case. Maybe because eglot handles this case better, it has other shortcomings that lsp-mode doesn't have.)
The way the completion item is inserted is not a subject of interpretation of the client but it is specified by the server. The client's job is just to apply it.
[Trace - 09:55:20 ] Received response 'textDocument/completion - (307)' in 45ms.
Result: {
"isIncomplete": null,
"items": [
{
"detail": "int",
"filterText": "uniqueVariableName",
"insertText": "uniqueVariableName",
"insertTextFormat": 1,
"kind": 6,
"label": "uniqueVariableName",
"score": 3.8985373973846436,
"sortText": "3f867e5duniqueVariableName",
"textEdit": {
"newText": "uniqueVariableName",
"range": {
"end": {
"character": 8,
"line": 3
},
"start": {
"character": 2,
"line": 3
}
}
}
}
]
}
As you can see from the range sent from the server to the client we are supposed to insert, not override.
OTH in order to support the case the spec supports using InsertReplaceEdit which then the client can depending of user prefs decide what to use: Here it is how it looks like in JDTLS:
[Trace - 09:58:18 ] Received response 'completionItem/resolve - (481)' in 21ms.
Result: {
"label": "uniqueVariableName : int",
"kind": 6,
"sortText": "999999161",
"insertText": "uniqueVariableName",
"insertTextFormat": 2,
"textEdit": {
"newText": "uniqueVariableName",
"insert": {
"start": {
"line": 50,
"character": 8
},
"end": {
"line": 50,
"character": 14
}
},
"replace": {
"start": {
"line": 50,
"character": 8
},
"end": {
"line": 50,
"character": 18
}
}
},
"command": {
"title": "",
"command": "java.completion.onDidSelect",
"arguments": [
"18",
"0"
]
}
}
AFAICS clangd does not support this new protocol feature.
What should happen, if the language server doesn't provide textEdit? Is lsp-mode written in way that if only insertText exists, then lsp-mode has the behavior I described in the first comment? I'm asking because I tried to remove textEdit handling in lsp-mode (in lsp-completion--guess-prefix and lsp-completion--exit-fn), but the behavior didn't change.
Regarding this new protocol feature: if replace was just strictly an addition to the protocol, we could say that lsp-mode could have a mode, where if the language server didn't provide replace, then lsp-mode tries to figure out replace by itself. Something like: by default it's the same range as insert, but if the conditions allows it, lsp-mode could extend the region so we can get the behavior I described.
What should happen, if the language server doesn't provide textEdit?
AFAIC is should be the same.
then lsp-mode tries to figure out replace
Tehnically this can be implemented as an optional feature.
I managed to achieve what I want, I modified lsp-completion-at-point to return the end of the symbol instead of (point) (I mean the second element of the list), and I removed text-edit handling from lsp-completion--exit-fn. The only drawback I noticed so far is that words won't complete if the cursor is in the middle of a word and the completion needs a different ending. So, for example, if the buffer has "uniqueVariableFunc", and the cursor is at "F", then completion won't happen to "uniqueVariableName". But eglot somehow still manages to complete in this case, so I think this approach can be developed further.
Of course, for other language servers, this approach may cause serious drawbacks, I don't know. But still, I think it would be a good thing if lsp-mode had something like this. Capf in other modes work like this, lsp-mode is an exception.
Anyways, feel free to close this issue, I just wanted to raise this point.