diff options
author | Veetaha <[email protected]> | 2020-02-22 18:58:00 +0000 |
---|---|---|
committer | Veetaha <[email protected]> | 2020-02-23 13:49:09 +0000 |
commit | 4cee7cddc84aa3769d5d3e87e5745f4c981bca28 (patch) | |
tree | f8b98268925cf01e2cc74a9e12fac81a460c1eff | |
parent | 838ad6bcfb2a82c030e18d019b8a06752f0fc828 (diff) |
vscode: gracefully handle cancellation errors
-rw-r--r-- | editors/code/src/ctx.ts | 21 | ||||
-rw-r--r-- | editors/code/src/highlighting.ts | 3 | ||||
-rw-r--r-- | editors/code/src/inlay_hints.ts | 40 | ||||
-rw-r--r-- | editors/code/src/util.ts | 40 |
4 files changed, 60 insertions, 44 deletions
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index dfc8aa7b9..43540e0d8 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -52,24 +52,3 @@ export interface Disposable { | |||
52 | dispose(): void; | 52 | dispose(): void; |
53 | } | 53 | } |
54 | export type Cmd = (...args: any[]) => unknown; | 54 | export type Cmd = (...args: any[]) => unknown; |
55 | |||
56 | export async function sendRequestWithRetry<R>( | ||
57 | client: lc.LanguageClient, | ||
58 | method: string, | ||
59 | param: unknown, | ||
60 | token?: vscode.CancellationToken, | ||
61 | ): Promise<R> { | ||
62 | for (const delay of [2, 4, 6, 8, 10, null]) { | ||
63 | try { | ||
64 | return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param)); | ||
65 | } catch (err) { | ||
66 | if (delay === null || err.code !== lc.ErrorCodes.ContentModified) { | ||
67 | throw err; | ||
68 | } | ||
69 | await sleep(10 * (1 << delay)); | ||
70 | } | ||
71 | } | ||
72 | throw 'unreachable'; | ||
73 | } | ||
74 | |||
75 | const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); | ||
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts index b34e49c17..77b4a1a68 100644 --- a/editors/code/src/highlighting.ts +++ b/editors/code/src/highlighting.ts | |||
@@ -3,7 +3,8 @@ import * as lc from 'vscode-languageclient'; | |||
3 | 3 | ||
4 | import { ColorTheme, TextMateRuleSettings } from './color_theme'; | 4 | import { ColorTheme, TextMateRuleSettings } from './color_theme'; |
5 | 5 | ||
6 | import { Ctx, sendRequestWithRetry } from './ctx'; | 6 | import { Ctx } from './ctx'; |
7 | import { sendRequestWithRetry } from './util'; | ||
7 | 8 | ||
8 | export function activateHighlighting(ctx: Ctx) { | 9 | export function activateHighlighting(ctx: Ctx) { |
9 | const highlighter = new Highlighter(ctx); | 10 | const highlighter = new Highlighter(ctx); |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 7e6c310a9..5f9229efb 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | 3 | ||
4 | import { Ctx, sendRequestWithRetry } from './ctx'; | 4 | import { Ctx } from './ctx'; |
5 | import { log } from './util'; | 5 | import { log, sendRequestWithRetry } from './util'; |
6 | 6 | ||
7 | export function activateInlayHints(ctx: Ctx) { | 7 | export function activateInlayHints(ctx: Ctx) { |
8 | const hintsUpdater = new HintsUpdater(ctx); | 8 | const hintsUpdater = new HintsUpdater(ctx); |
@@ -152,28 +152,24 @@ class HintsUpdater { | |||
152 | } | 152 | } |
153 | 153 | ||
154 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { | 154 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { |
155 | const client = this.ctx.client; | 155 | this.pending.get(documentUri)?.cancel(); |
156 | if (!client) return null; | ||
157 | 156 | ||
158 | const request: InlayHintsParams = { | ||
159 | textDocument: { uri: documentUri }, | ||
160 | }; | ||
161 | const tokenSource = new vscode.CancellationTokenSource(); | 157 | const tokenSource = new vscode.CancellationTokenSource(); |
162 | const prevHintsRequest = this.pending.get(documentUri); | ||
163 | prevHintsRequest?.cancel(); | ||
164 | |||
165 | this.pending.set(documentUri, tokenSource); | 158 | this.pending.set(documentUri, tokenSource); |
166 | try { | 159 | |
167 | return await sendRequestWithRetry<InlayHint[] | null>( | 160 | const request: InlayHintsParams = { textDocument: { uri: documentUri } }; |
168 | client, | 161 | |
169 | 'rust-analyzer/inlayHints', | 162 | return sendRequestWithRetry<InlayHint[]>( |
170 | request, | 163 | this.ctx.client, |
171 | tokenSource.token, | 164 | 'rust-analyzer/inlayHints', |
172 | ); | 165 | request, |
173 | } finally { | 166 | tokenSource.token |
174 | if (!tokenSource.token.isCancellationRequested) { | 167 | ) |
175 | this.pending.delete(documentUri); | 168 | .catch(_ => null) |
176 | } | 169 | .finally(() => { |
177 | } | 170 | if (!tokenSource.token.isCancellationRequested) { |
171 | this.pending.delete(documentUri); | ||
172 | } | ||
173 | }); | ||
178 | } | 174 | } |
179 | } | 175 | } |
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 7a6657753..2f18f85a3 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts | |||
@@ -1,3 +1,6 @@ | |||
1 | import * as lc from "vscode-languageclient"; | ||
2 | import * as vscode from "vscode"; | ||
3 | |||
1 | let enabled: boolean = false; | 4 | let enabled: boolean = false; |
2 | 5 | ||
3 | export const log = { | 6 | export const log = { |
@@ -16,3 +19,40 @@ export const log = { | |||
16 | enabled = yes; | 19 | enabled = yes; |
17 | } | 20 | } |
18 | }; | 21 | }; |
22 | |||
23 | export async function sendRequestWithRetry<R>( | ||
24 | client: lc.LanguageClient, | ||
25 | method: string, | ||
26 | param: unknown, | ||
27 | token?: vscode.CancellationToken, | ||
28 | ): Promise<R> { | ||
29 | for (const delay of [2, 4, 6, 8, 10, null]) { | ||
30 | try { | ||
31 | return await (token | ||
32 | ? client.sendRequest(method, param, token) | ||
33 | : client.sendRequest(method, param) | ||
34 | ); | ||
35 | } catch (error) { | ||
36 | if (delay === null) { | ||
37 | log.error("LSP request timed out", { method, param, error }); | ||
38 | throw error; | ||
39 | } | ||
40 | |||
41 | if (error.code === lc.ErrorCodes.RequestCancelled) { | ||
42 | throw error; | ||
43 | } | ||
44 | |||
45 | if (error.code !== lc.ErrorCodes.ContentModified) { | ||
46 | log.error("LSP request failed", { method, param, error }); | ||
47 | throw error; | ||
48 | } | ||
49 | |||
50 | await sleep(10 * (1 << delay)); | ||
51 | } | ||
52 | } | ||
53 | throw 'unreachable'; | ||
54 | } | ||
55 | |||
56 | function sleep(ms: number) { | ||
57 | return new Promise(resolve => setTimeout(resolve, ms)); | ||
58 | } | ||