diff options
Diffstat (limited to 'editors/code/src/ctx.ts')
-rw-r--r-- | editors/code/src/ctx.ts | 76 |
1 files changed, 47 insertions, 29 deletions
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 393d6a602..13988056a 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -1,19 +1,38 @@ | |||
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 | import { Server } from './server'; | ||
4 | import { Config } from './config'; | 3 | import { Config } from './config'; |
4 | import { createClient } from './client' | ||
5 | 5 | ||
6 | export class Ctx { | 6 | export class Ctx { |
7 | readonly config: Config; | 7 | readonly config: Config; |
8 | // Because we have "reload server" action, various listeners **will** face a | ||
9 | // situation where the client is not ready yet, and should be prepared to | ||
10 | // deal with it. | ||
11 | // | ||
12 | // Ideally, this should be replaced with async getter though. | ||
13 | client: lc.LanguageClient | null = null | ||
8 | private extCtx: vscode.ExtensionContext; | 14 | private extCtx: vscode.ExtensionContext; |
15 | private onDidRestartHooks: Array<(client: lc.LanguageClient) => void> = []; | ||
9 | 16 | ||
10 | constructor(extCtx: vscode.ExtensionContext) { | 17 | constructor(extCtx: vscode.ExtensionContext) { |
11 | this.config = new Config(extCtx) | 18 | this.config = new Config(extCtx) |
12 | this.extCtx = extCtx; | 19 | this.extCtx = extCtx; |
13 | } | 20 | } |
14 | 21 | ||
15 | get client(): lc.LanguageClient { | 22 | async restartServer() { |
16 | return Server.client; | 23 | let old = this.client; |
24 | if (old) { | ||
25 | await old.stop() | ||
26 | } | ||
27 | this.client = null; | ||
28 | const client = createClient(this.config); | ||
29 | this.pushCleanup(client.start()); | ||
30 | await client.onReady(); | ||
31 | |||
32 | this.client = client | ||
33 | for (const hook of this.onDidRestartHooks) { | ||
34 | hook(client) | ||
35 | } | ||
17 | } | 36 | } |
18 | 37 | ||
19 | get activeRustEditor(): vscode.TextEditor | undefined { | 38 | get activeRustEditor(): vscode.TextEditor | undefined { |
@@ -60,35 +79,34 @@ export class Ctx { | |||
60 | this.extCtx.subscriptions.push(d); | 79 | this.extCtx.subscriptions.push(d); |
61 | } | 80 | } |
62 | 81 | ||
63 | async sendRequestWithRetry<R>( | 82 | onDidRestart(hook: (client: lc.LanguageClient) => void) { |
64 | method: string, | 83 | this.onDidRestartHooks.push(hook) |
65 | param: any, | ||
66 | token?: vscode.CancellationToken, | ||
67 | ): Promise<R> { | ||
68 | await this.client.onReady(); | ||
69 | for (const delay of [2, 4, 6, 8, 10, null]) { | ||
70 | try { | ||
71 | return await (token ? this.client.sendRequest(method, param, token) : this.client.sendRequest(method, param)); | ||
72 | } catch (e) { | ||
73 | if ( | ||
74 | e.code === lc.ErrorCodes.ContentModified && | ||
75 | delay !== null | ||
76 | ) { | ||
77 | await sleep(10 * (1 << delay)); | ||
78 | continue; | ||
79 | } | ||
80 | throw e; | ||
81 | } | ||
82 | } | ||
83 | throw 'unreachable'; | ||
84 | } | ||
85 | |||
86 | onNotification(method: string, handler: lc.GenericNotificationHandler) { | ||
87 | this.client.onReady() | ||
88 | .then(() => this.client.onNotification(method, handler)) | ||
89 | } | 84 | } |
90 | } | 85 | } |
91 | 86 | ||
92 | export type Cmd = (...args: any[]) => any; | 87 | export type Cmd = (...args: any[]) => any; |
93 | 88 | ||
89 | export async function sendRequestWithRetry<R>( | ||
90 | client: lc.LanguageClient, | ||
91 | method: string, | ||
92 | param: any, | ||
93 | token?: vscode.CancellationToken, | ||
94 | ): Promise<R> { | ||
95 | for (const delay of [2, 4, 6, 8, 10, null]) { | ||
96 | try { | ||
97 | return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param)); | ||
98 | } catch (e) { | ||
99 | if ( | ||
100 | e.code === lc.ErrorCodes.ContentModified && | ||
101 | delay !== null | ||
102 | ) { | ||
103 | await sleep(10 * (1 << delay)); | ||
104 | continue; | ||
105 | } | ||
106 | throw e; | ||
107 | } | ||
108 | } | ||
109 | throw 'unreachable'; | ||
110 | } | ||
111 | |||
94 | const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); | 112 | const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); |