Skip to content
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

Completion request and content change arrival order #968

Closed
marcusroberts opened this issue May 26, 2022 · 10 comments
Closed

Completion request and content change arrival order #968

marcusroberts opened this issue May 26, 2022 · 10 comments
Assignees
Labels
bug Issue identified by VS Code Team member as probable bug
Milestone

Comments

@marcusroberts
Copy link

Watching the trace for communications between the extension and the server, we see textDocument/completion get sent before textDocument/didChange, so we are looking at the source code as it was before the change was made.

When it's a trigger character we can add it on to the text before the cursor (although the cursor position is reported in the changed source that follows in the didChange) but when it's just invoked by being an identifier that identifier isn't in the source we hold.

What's the best way to handle this? Our own server is single threaded so we know it will process them in this order, but I guess other servers are async and could be processing both at the same time? Is there a guaranteed order that all clients will send these two in?

@rcjsuen
Copy link
Contributor

rcjsuen commented May 26, 2022

Watching the trace for communications between the extension and the server, we see textDocument/completion get sent before textDocument/didChange, so we are looking at the source code as it was before the change was made.

This sounds like a bug that should be investigated. There are many language servers that work with Visual Studio Code so it's hard for me to believe this is a bug that is affecting everyone (myself included as I am the author of a language server myself).

How can this bug be reproduced?

@marcusroberts
Copy link
Author

I can't believe it's a bug either because of its impact...

This is for the Grain LSP I'm an author of. We moved up from a pretty old version of the client library to the latest as part of a major upgrade we are undertaking.

In the Trace for our LSP we see this when we type a character at the start of a line for example:

`[Trace - 7:46:01 AM] Sending request 'textDocument/completion - (99)'.
Params: {
"textDocument": {
"uri": "file:///Users/marcus/Projects/tests/test.gr"
},
"position": {
"line": 15,
"character": 1
},
"context": {
"triggerKind": 1
}
}

[Trace - 7:46:01 AM] Sending notification 'textDocument/didChange'.
Params: {
"textDocument": {
"uri": "file:///Users/marcus/Projects/tests/test.gr",
"version": 104
},
"contentChanges": [
{
"text": "import Regex from "regex"\nimport Result from "result"\nimport List from "list"\nprint("Hello,Grain")\n\n10\n\nlet re = Result.unwrap(Regex.make("o"))\n\nprint(Regex.replace(re,"foo","bar"))\n\n\nlet fuu = 1\nlet foo = 2\n\nR\n"
}
]
}`

and similarly for a completion character:

`[Trace - 10:44:02 AM] Sending request 'textDocument/completion - (109)'.
Params: {
"textDocument": {
"uri": "file:///Users/marcus/Projects/tests/test.gr"
},
"position": {
"line": 16,
"character": 5
},
"context": {
"triggerKind": 2,
"triggerCharacter": "."
}
}

[Trace - 10:44:02 AM] Sending notification 'textDocument/didChange'.
Params: {
"textDocument": {
"uri": "file:///Users/marcus/Projects/tests/test.gr",
"version": 112
},
"contentChanges": [
{
"text": "import Regex from "regex"\nimport Result from "result"\nimport List from "list"\nprint("Hello,Grain")\n\n10\n\nlet re = Result.unwrap(Regex.make("o"))\n\nprint(Regex.replace(re,"foo","bar"))\n\n\nlet fuu = 1\nlet foo = 2\n\n\nList.\n\n"
}
]
}
`

@DanTup
Copy link
Contributor

DanTup commented May 30, 2022

FWIW, I don't see this behaviour with Dart, I see the didChange sent first:

[12:01:24 PM] [Analyzer] [Info] ==> Content-Length: 379
[12:01:24 PM] [Analyzer] [Info] ==> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/danny/Dev/Google/dart-sdk/sdk/pkg/analysis_server/test/src/services/correction/fix/fix_in_file_test.dart","version":3},"contentChanges":[{"range":{"start":{"line":126,"character":4},"end":{"line":126,"character":4}},"rangeLength":0,"text":"()"}]},"clientRequestTime":1653908484135}
[12:01:24 PM] [Analyzer] [Info] ==> Content-Length: 338
[12:01:24 PM] [Analyzer] [Info] ==> {"jsonrpc":"2.0","id":28,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///Users/danny/Dev/Google/dart-sdk/sdk/pkg/analysis_server/test/src/services/correction/fix/fix_in_file_test.dart"},"position":{"line":126,"character":5},"context":{"triggerKind":2,"triggerCharacter":"("}},"clientRequestTime":1653908484135}

However, my server is using incremental changes (textDocument/didChange contains only a delta). I don't know if that's affecting anything.

@DanTup
Copy link
Contributor

DanTup commented May 30, 2022

Interestingly, in the code for full (non-incremental) sync, there is some field called changeDelayer. I don't know what it is, but perhaps it's related:

Perhaps it's an optimisation to reduce traffic for full sync (so every keystroke isn't sending the entire document), but the completion request isn't forcing delivery?

@marcusroberts
Copy link
Author

@DanTup is that on the latest version? We are doing full change, I'll look at incremental too.

@DanTup
Copy link
Contributor

DanTup commented May 30, 2022

@marcusroberts yep, that was on v8 of the client. Although based on the code I linked above, I suspect the issue could be specific to full sync.

@dbaeumer
Copy link
Member

Regarding the change delayer. The flushing of buffered full document syncs is happening here:

this.forceDocumentSync();

and at the corresponding place in sendNotification

@dbaeumer
Copy link
Member

I was actually able to reproduce this in full text document sync mode. Need to investigate why this is happening.

@dbaeumer
Copy link
Member

The problem is that internally forceDocumentSync() is now async (send notification is async) and I need to await the force document sync.

The reason this is not showing up in incremental sync is that there nothing is buffered.

@marcusroberts
Copy link
Author

I'm glad you could reproduce and then find the cause, and so quickly too, thank you!

@dbaeumer dbaeumer added the bug Issue identified by VS Code Team member as probable bug label May 30, 2022
@dbaeumer dbaeumer self-assigned this May 30, 2022
@dbaeumer dbaeumer added this to the Next milestone May 30, 2022
dbaeumer added a commit that referenced this issue May 30, 2022
…merald

Fixes #968: Completion request and content change arrival order
@dbaeumer dbaeumer modified the milestones: Next, 8.0.2 Jul 13, 2022
turbolent added a commit to onflow/vscode-cadence that referenced this issue Jan 7, 2023
Update to the latest release of vscode-languageclient, which includes
microsoft/vscode-languageserver-node@529ee89
which fixes
microsoft/vscode-languageserver-node#968:
completion requests were sometimes sent before didChange requests,
leading to broken/flaky completions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue identified by VS Code Team member as probable bug
Projects
None yet
Development

No branches or pull requests

4 participants