mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 04:44:57 +00:00
Refactor server lifecycle
This commit is contained in:
parent
0849f7001c
commit
087af54069
12 changed files with 216 additions and 199 deletions
|
@ -1,19 +1,38 @@
|
|||
import * as vscode from 'vscode';
|
||||
import * as lc from 'vscode-languageclient';
|
||||
import { Server } from './server';
|
||||
import { Config } from './config';
|
||||
import { createClient } from './client'
|
||||
|
||||
export class Ctx {
|
||||
readonly config: Config;
|
||||
// Because we have "reload server" action, various listeners **will** face a
|
||||
// situation where the client is not ready yet, and should be prepared to
|
||||
// deal with it.
|
||||
//
|
||||
// Ideally, this should be replaced with async getter though.
|
||||
client: lc.LanguageClient | null = null
|
||||
private extCtx: vscode.ExtensionContext;
|
||||
private onDidRestartHooks: Array<(client: lc.LanguageClient) => void> = [];
|
||||
|
||||
constructor(extCtx: vscode.ExtensionContext) {
|
||||
this.config = new Config(extCtx)
|
||||
this.extCtx = extCtx;
|
||||
}
|
||||
|
||||
get client(): lc.LanguageClient {
|
||||
return Server.client;
|
||||
async restartServer() {
|
||||
let old = this.client;
|
||||
if (old) {
|
||||
await old.stop()
|
||||
}
|
||||
this.client = null;
|
||||
const client = createClient(this.config);
|
||||
this.pushCleanup(client.start());
|
||||
await client.onReady();
|
||||
|
||||
this.client = client
|
||||
for (const hook of this.onDidRestartHooks) {
|
||||
hook(client)
|
||||
}
|
||||
}
|
||||
|
||||
get activeRustEditor(): vscode.TextEditor | undefined {
|
||||
|
@ -60,35 +79,34 @@ export class Ctx {
|
|||
this.extCtx.subscriptions.push(d);
|
||||
}
|
||||
|
||||
async sendRequestWithRetry<R>(
|
||||
method: string,
|
||||
param: any,
|
||||
token?: vscode.CancellationToken,
|
||||
): Promise<R> {
|
||||
await this.client.onReady();
|
||||
for (const delay of [2, 4, 6, 8, 10, null]) {
|
||||
try {
|
||||
return await (token ? this.client.sendRequest(method, param, token) : this.client.sendRequest(method, param));
|
||||
} catch (e) {
|
||||
if (
|
||||
e.code === lc.ErrorCodes.ContentModified &&
|
||||
delay !== null
|
||||
) {
|
||||
await sleep(10 * (1 << delay));
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
throw 'unreachable';
|
||||
}
|
||||
|
||||
onNotification(method: string, handler: lc.GenericNotificationHandler) {
|
||||
this.client.onReady()
|
||||
.then(() => this.client.onNotification(method, handler))
|
||||
onDidRestart(hook: (client: lc.LanguageClient) => void) {
|
||||
this.onDidRestartHooks.push(hook)
|
||||
}
|
||||
}
|
||||
|
||||
export type Cmd = (...args: any[]) => any;
|
||||
|
||||
export async function sendRequestWithRetry<R>(
|
||||
client: lc.LanguageClient,
|
||||
method: string,
|
||||
param: any,
|
||||
token?: vscode.CancellationToken,
|
||||
): Promise<R> {
|
||||
for (const delay of [2, 4, 6, 8, 10, null]) {
|
||||
try {
|
||||
return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param));
|
||||
} catch (e) {
|
||||
if (
|
||||
e.code === lc.ErrorCodes.ContentModified &&
|
||||
delay !== null
|
||||
) {
|
||||
await sleep(10 * (1 << delay));
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
throw 'unreachable';
|
||||
}
|
||||
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue