aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/ctx.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/ctx.ts')
-rw-r--r--editors/code/src/ctx.ts112
1 files changed, 112 insertions, 0 deletions
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
new file mode 100644
index 000000000..a2a4e42a9
--- /dev/null
+++ b/editors/code/src/ctx.ts
@@ -0,0 +1,112 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3import { Config } from './config';
4import { createClient } from './client';
5
6export class Ctx {
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;
14 private extCtx: vscode.ExtensionContext;
15 private onDidRestartHooks: Array<(client: lc.LanguageClient) => void> = [];
16
17 constructor(extCtx: vscode.ExtensionContext) {
18 this.config = new Config(extCtx);
19 this.extCtx = extCtx;
20 }
21
22 async restartServer() {
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 }
36 }
37
38 get activeRustEditor(): vscode.TextEditor | undefined {
39 const editor = vscode.window.activeTextEditor;
40 return editor && editor.document.languageId === 'rust'
41 ? editor
42 : undefined;
43 }
44
45 registerCommand(name: string, factory: (ctx: Ctx) => Cmd) {
46 const fullName = `rust-analyzer.${name}`;
47 const cmd = factory(this);
48 const d = vscode.commands.registerCommand(fullName, cmd);
49 this.pushCleanup(d);
50 }
51
52 overrideCommand(name: string, factory: (ctx: Ctx) => Cmd) {
53 const defaultCmd = `default:${name}`;
54 const override = factory(this);
55 const original = (...args: any[]) =>
56 vscode.commands.executeCommand(defaultCmd, ...args);
57 try {
58 const d = vscode.commands.registerCommand(
59 name,
60 async (...args: any[]) => {
61 if (!(await override(...args))) {
62 return await original(...args);
63 }
64 },
65 );
66 this.pushCleanup(d);
67 } catch (_) {
68 vscode.window.showWarningMessage(
69 'Enhanced typing feature is disabled because of incompatibility with VIM extension, consider turning off rust-analyzer.enableEnhancedTyping: https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/README.md#settings',
70 );
71 }
72 }
73
74 get subscriptions(): { dispose(): any }[] {
75 return this.extCtx.subscriptions;
76 }
77
78 pushCleanup(d: { dispose(): any }) {
79 this.extCtx.subscriptions.push(d);
80 }
81
82 onDidRestart(hook: (client: lc.LanguageClient) => void) {
83 this.onDidRestartHooks.push(hook);
84 }
85}
86
87export type Cmd = (...args: any[]) => any;
88
89export 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
112const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));