-
Notifications
You must be signed in to change notification settings - Fork 401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fill additionalTextEdits during completionItem/resolve #1487
Conversation
Previously in ImportRewrite object was updated in multiple locations (for different type of proposals), and my original thought was to decouple it, and remove every update of the ImportRewrite object during completion stage. I partially figure it out, but also made a mess in the code.
Pros: minimal code changes, less risk of regression I think pros >>> cons |
this is not officially specified yet, so we need to ensure clients explicitly support that new behavior by adding an initialization option (like everything we support that deviates from LSP) Clients not supporting this feature should receive the regular completion items |
Yes, theoretically we shall wait for the release of LSP v3.16, in order not to block any other clients. I create this PR to let us know how far we can improve, and let you guys review it in advance. The original behaviour of jdtls was to calculate all textEdits in resolve stage. We moved it to completion stage only because of previous vscode's behaviour change. Other clients seemed not to have the regression issue at that time (not sure about their status now). As vscode has already fixed that, and the behavior has been proposed in LSP v3.16 (though the paper work has not been done yet). If we do want to merge this PR in advance to fix the perf issue for users now, I think we should at least make sure it doesn't block any other client. |
org.eclipse.lsp4j.TextEdit edit = toRequiredTypeEdit(requiredProposal, trigger, proposal.canUseDiamond(context)); | ||
if (proposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION || proposal.getKind() == CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION | ||
|| proposal.getKind() == CompletionProposal.ANONYMOUS_CLASS_DECLARATION) { | ||
completionBuffer.append(edit.getNewText()); | ||
range = edit.getRange(); | ||
} else { | ||
additionalTextEdits.add(edit); | ||
} | ||
break; | ||
default: | ||
/* | ||
* In 3.3 we only support the above required proposals, see | ||
* CompletionProposal#getRequiredProposals() | ||
*/ | ||
Assert.isTrue(false); | ||
completionBuffer.append(edit.getNewText()); | ||
range = edit.getRange(); | ||
} else { | ||
additionalTextEdits.add(edit); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if we should do something like:
if (proposal.getKind() == CompletionProposal.CONSTRUCTOR_INVOCATION || proposal.getKind() == CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION
|| proposal.getKind() == CompletionProposal.ANONYMOUS_CLASS_DECLARATION) {
org.eclipse.lsp4j.TextEdit edit = toRequiredTypeEdit(requiredProposal, trigger, proposal.canUseDiamond(context));
completionBuffer.append(edit.getNewText());
range = edit.getRange();
} else if (addImports) {
org.eclipse.lsp4j.TextEdit edit = toRequiredTypeEdit(requiredProposal, trigger, proposal.canUseDiamond(context));
additionalTextEdits.add(edit);
}
are all additional edits imports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are all additional edits imports?
No. In most cases they are imports. But there is a special case CompletionHandlerTest.java#testCompletion_method_withLSPV3(), besides the imports, there is another additonal edit to replace "HashMap" with "HashMap". It look confusing (redudant), but after playing for a while, I found a case that might better explain it.
public class A {
public static void main(String[] args) {
HashMap map = new HashMap<>();
map.putA
}
}
// additional edits for `putAll`
"additionalTextEdits": [
{
"range": {
"start": {
"line": 2,
"character": 8
},
"end": {
"line": 2,
"character": 15
}
},
"newText": "HashMap<K,V>" //<-- this is the `toRequiredTypeEdit` one
},
{
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 0,
"character": 0
}
},
"newText": "import java.util.HashMap;\n\n"
}
No matter whether they are imports or not, the additional edits will be correctly calculated in the resolve stage. The change you proposed is surely harmless, and can also reduce the times of calling toRequiredTypeEdit
.
new Type completion is really super slow (with no completion limit).
I've added some extra logs after https://github.com/eclipse/eclipse.jdt.ls/blob/c2bdb489d305ece226da4e55f6e349d65ce34383/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandler.java#L150 and after https://github.com/eclipse/eclipse.jdt.ls/blob/c2bdb489d305ece226da4e55f6e349d65ce34383/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CompletionHandler.java#L154
(tested on the spring-boot codebase) |
I can reproduce it. It's not related to |
New type completion is slow, because computing items for "anonymous class creation" is slow. E.g. given
I'm thinking of whether we can put the body in addtionalTextEdits instead of textEdits. E.g. |
Sounds good to me. |
I tried my proposal in vscode, given above
I tried the workaround below, and it did work correctly and fast. However, it is against the LSP spec that textEdit must not change.
Now I'm thinking, guarded by the feature flag it won't break any other clients, so maybe it's worth a try? |
Signed-off-by: Yan Zhang <[email protected]>
I don't think we should go that route. This is exactly what we were doing before. It violates the spec, we have no guarantee that vscode will keep working that way in the future (cc @jrieken), and it won't work for other clients once lazy loading additional textedits is part of the LSP. |
I've created microsoft/language-server-protocol#1032 in LSP repo. |
A compromising approach is, we don't fill unimplemented methods automatically, leaving the work to code actions/source actions (we already have one named "Add unimplemented methods") . Typescript is doing this way as mentioned in microsoft/language-server-protocol#1032 (comment) |
@Eskibear ok let's see how it works |
Signed-off-by: Yan Zhang <[email protected]>
I simply remove the new body in 8a901a9, and it looks like below (with no completion limit). |
} | ||
// Construct empty body for performance concern | ||
// See https://github.com/microsoft/language-server-protocol/issues/1032#issuecomment-648748013 | ||
String newBody = "{\n\t${0}\n}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only use ${0} if snippets are supported by the client
// Construct empty body for performance concern | ||
// See https://github.com/microsoft/language-server-protocol/issues/1032#issuecomment-648748013 | ||
String newBody = "{\n\t${0}\n}"; | ||
|
||
StringBuffer buf = new StringBuffer("new A()"); //$NON-NLS-1$ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StringBuilder
Signed-off-by: Yan Zhang <[email protected]>
Thanks @Eskibear, completion is great again! |
It seems that this has been superset with new capability |
The goal is to improve performance of completion. Previously all text edits (including addtional ones which cost much time for imports) are caclulated in
CompletionProposalReplacementProvider#updateReplacement
. Now we decouple them,TextEdits
is calculated in "completion" stage, andAdditionalTextEdits
in "resolve" stageCurrenly it's not ready for review, just to show you how far it would improve.Performance Preview
In the initial commit, I defer the calculation of imports (only for
TYPE_REF
candidates) to resolving stage. In spring-petclinic project, when I type "S" to complete, it takes:(macOS, 2.9 GHz Dual-Core Intel Core i5, 8G RAM)
References
VS Code has already supported the so-called Relaxed resolveCompletionItem in v1.46.
Proposed spec in LSP microsoft/language-server-protocol#1003