5693: Fix no inlay hints / unresolved tokens until manual edit to refresh r=jonas-schievink a=Veetaha

Fixes https://github.com/rust-analyzer/rust-analyzer/issues/5349 

Now we return ContentModified during the workspace loading. This signifies the language
client to retry the operation (i.e. the client will
continue polling the server while it returns ContentModified).
I believe that there might be cases of overly big projects where the backoff
logic we have setup in `sendRequestWithRetry` (which we use for inlay hints)
might bail too early (currently the largest retry standby time is 10 seconds).
However, I've tried on one of my project with 500+ dependencies and it is still enough.

Here are the examples before/after the change (the gifs are quite lengthy because they show testing rather large cargo workspace).

<details>
<summary>Before</summary>

Here you can see that the client receives empty array of inlay hints and does nothing more.
Same applies to semantic tokens. The client receives unresolved tokens and does nothing more.
The user needs to do a manual edit to refresh the editor.

![prev-demo](https://user-images.githubusercontent.com/36276403/89717721-e4471280-d9c1-11ea-89ce-7dc3e83d9768.gif)

</details>

<details>
<summary>After</summary>

Here the server returns ContentModified, so the client periodically retries the requests and eventually receives the wellformed response.

![new-demo](https://user-images.githubusercontent.com/36276403/89717725-eb6e2080-d9c1-11ea-84c9-796bb2b22cec.gif)

</details>

Co-authored-by: Veetaha <veetaha2@gmail.com>
This commit is contained in:
bors[bot] 2020-08-09 22:39:10 +00:00 committed by GitHub
commit b1cb4ac13d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 13 additions and 3 deletions

View file

@ -337,6 +337,16 @@ impl GlobalState {
fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
self.register_request(&req, request_received);
if self.status == Status::Loading && req.method != "shutdown" {
self.respond(lsp_server::Response::new_err(
req.id,
// FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
lsp_server::ErrorCode::ContentModified as i32,
"Rust Analyzer is still loading...".to_owned(),
));
return Ok(());
}
RequestDispatcher { req: Some(req), global_state: self }
.on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))?
.on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?

View file

@ -64,7 +64,8 @@ export async function sendRequestWithRetry<TParam, TRet>(
param: TParam,
token?: vscode.CancellationToken,
): Promise<TRet> {
for (const delay of [2, 4, 6, 8, 10, null]) {
// The sequence is `10 * (2 ** (2 * n))` where n is 1, 2, 3...
for (const delay of [40, 160, 640, 2560, 10240, null]) {
try {
return await (token
? client.sendRequest(reqType, param, token)
@ -84,8 +85,7 @@ export async function sendRequestWithRetry<TParam, TRet>(
log.warn("LSP request failed", { method: reqType.method, param, error });
throw error;
}
await sleep(10 * (1 << delay));
await sleep(delay);
}
}
throw 'unreachable';