Refactor server lifecycle

This commit is contained in:
Aleksey Kladov 2019-12-31 18:14:00 +01:00
parent 0849f7001c
commit 087af54069
12 changed files with 216 additions and 199 deletions

View file

@ -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));