Properly reload changed configs for server start

This commit is contained in:
Lukas Wirth 2022-10-17 14:53:46 +02:00
parent 6f435977df
commit 8aaafddee8
4 changed files with 51 additions and 57 deletions

View file

@ -310,6 +310,10 @@ export function ssr(ctx: Ctx): Cmd {
export function serverVersion(ctx: Ctx): Cmd { export function serverVersion(ctx: Ctx): Cmd {
return async () => { return async () => {
if (!ctx.serverPath) {
void vscode.window.showWarningMessage(`rust-analyzer server is not running`);
return;
}
const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" }); const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
const versionString = stdout.slice(`rust-analyzer `.length).trim(); const versionString = stdout.slice(`rust-analyzer `.length).trim();

View file

@ -14,8 +14,6 @@ export class Config {
readonly rootSection = "rust-analyzer"; readonly rootSection = "rust-analyzer";
private readonly requiresWorkspaceReloadOpts = [ private readonly requiresWorkspaceReloadOpts = [
"serverPath",
"server",
// FIXME: This shouldn't be here, changing this setting should reload // FIXME: This shouldn't be here, changing this setting should reload
// `continueCommentsOnNewline` behavior without restart // `continueCommentsOnNewline` behavior without restart
"typing", "typing",
@ -23,6 +21,8 @@ export class Config {
private readonly requiresReloadOpts = [ private readonly requiresReloadOpts = [
"cargo", "cargo",
"procMacro", "procMacro",
"serverPath",
"server",
"files", "files",
"lens", // works as lens.* "lens", // works as lens.*
] ]

View file

@ -6,6 +6,8 @@ import { Config, substituteVariablesInEnv, substituteVSCodeVariables } from "./c
import { createClient } from "./client"; import { createClient } from "./client";
import { isRustEditor, log, RustEditor } from "./util"; import { isRustEditor, log, RustEditor } from "./util";
import { ServerStatusParams } from "./lsp_ext"; import { ServerStatusParams } from "./lsp_ext";
import { PersistentState } from "./persistent_state";
import { bootstrap } from "./bootstrap";
export type Workspace = export type Workspace =
| { | {
@ -17,28 +19,18 @@ export type Workspace =
}; };
export class Ctx { export class Ctx {
private client: lc.LanguageClient | undefined;
readonly config: Config;
serverPath: string;
readonly statusBar: vscode.StatusBarItem; readonly statusBar: vscode.StatusBarItem;
readonly config: Config;
private client: lc.LanguageClient | undefined;
traceOutputChannel: vscode.OutputChannel | undefined; traceOutputChannel: vscode.OutputChannel | undefined;
outputChannel: vscode.OutputChannel | undefined; outputChannel: vscode.OutputChannel | undefined;
serverOptions:
| {
run: lc.Executable;
debug: lc.Executable;
}
| undefined;
workspace: Workspace; workspace: Workspace;
state: PersistentState;
serverPath: string | undefined;
constructor( constructor(readonly extCtx: vscode.ExtensionContext, workspace: Workspace) {
readonly extCtx: vscode.ExtensionContext,
config: Config,
serverPath: string,
workspace: Workspace
) {
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
extCtx.subscriptions.push(this.statusBar); extCtx.subscriptions.push(this.statusBar);
extCtx.subscriptions.push({ extCtx.subscriptions.push({
@ -50,9 +42,10 @@ export class Ctx {
this.statusBar.tooltip = "ready"; this.statusBar.tooltip = "ready";
this.statusBar.command = "rust-analyzer.analyzerStatus"; this.statusBar.command = "rust-analyzer.analyzerStatus";
this.statusBar.show(); this.statusBar.show();
this.serverPath = serverPath;
this.config = config;
this.workspace = workspace; this.workspace = workspace;
this.state = new PersistentState(extCtx.globalState);
this.config = new Config(extCtx);
} }
clientFetcher() { clientFetcher() {
@ -64,6 +57,7 @@ export class Ctx {
} }
async getClient() { async getClient() {
// if server path changes -> dispose
if (!this.traceOutputChannel) { if (!this.traceOutputChannel) {
this.traceOutputChannel = vscode.window.createOutputChannel( this.traceOutputChannel = vscode.window.createOutputChannel(
"Rust Analyzer Language Server Trace" "Rust Analyzer Language Server Trace"
@ -72,8 +66,20 @@ export class Ctx {
if (!this.outputChannel) { if (!this.outputChannel) {
this.outputChannel = vscode.window.createOutputChannel("Rust Analyzer Language Server"); this.outputChannel = vscode.window.createOutputChannel("Rust Analyzer Language Server");
} }
if (!this.serverOptions) {
log.info("Creating server options client"); if (!this.client) {
log.info("Creating language client");
this.serverPath = await bootstrap(this.extCtx, this.config, this.state).catch((err) => {
let message = "bootstrap error. ";
message +=
'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
message += 'To enable verbose logs use { "rust-analyzer.trace.extension": true }';
log.error("Bootstrap error", err);
throw new Error(message);
});
const newEnv = substituteVariablesInEnv( const newEnv = substituteVariablesInEnv(
Object.assign({}, process.env, this.config.serverExtraEnv) Object.assign({}, process.env, this.config.serverExtraEnv)
); );
@ -81,16 +87,11 @@ export class Ctx {
command: this.serverPath, command: this.serverPath,
options: { env: newEnv }, options: { env: newEnv },
}; };
this.serverOptions = { const serverOptions = {
run, run,
debug: run, debug: run,
}; };
} else {
this.serverOptions.run.command = this.serverPath;
this.serverOptions.debug.command = this.serverPath;
}
if (!this.client) {
log.info("Creating language client");
let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer"); let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
if (this.workspace.kind === "Detached Files") { if (this.workspace.kind === "Detached Files") {
@ -106,7 +107,7 @@ export class Ctx {
this.traceOutputChannel, this.traceOutputChannel,
this.outputChannel, this.outputChannel,
initializationOptions, initializationOptions,
this.serverOptions serverOptions
); );
this.client.onNotification(ra.serverStatus, (params) => this.setServerStatus(params)); this.client.onNotification(ra.serverStatus, (params) => this.setServerStatus(params));
} }
@ -128,6 +129,7 @@ export class Ctx {
async disposeClient() { async disposeClient() {
log.info("Deactivating language client"); log.info("Deactivating language client");
await this.client?.dispose(); await this.client?.dispose();
this.serverPath = undefined;
this.client = undefined; this.client = undefined;
} }

View file

@ -3,12 +3,9 @@ import * as lc from "vscode-languageclient/node";
import * as commands from "./commands"; import * as commands from "./commands";
import { Ctx, Workspace } from "./ctx"; import { Ctx, Workspace } from "./ctx";
import { log, isRustDocument } from "./util"; import { isRustDocument } from "./util";
import { PersistentState } from "./persistent_state";
import { activateTaskProvider } from "./tasks"; import { activateTaskProvider } from "./tasks";
import { setContextValue } from "./util"; import { setContextValue } from "./util";
import { bootstrap } from "./bootstrap";
import { Config } from "./config";
const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
@ -56,24 +53,13 @@ export async function activate(
} }
: { kind: "Workspace Folder" }; : { kind: "Workspace Folder" };
const state = new PersistentState(context.globalState); const ctx = new Ctx(context, workspace);
const config = new Config(context);
const serverPath = await bootstrap(context, config, state).catch((err) => {
let message = "bootstrap error. ";
message += 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
message += 'To enable verbose logs use { "rust-analyzer.trace.extension": true }';
log.error("Bootstrap error", err);
throw new Error(message);
});
const ctx = new Ctx(context, config, serverPath, workspace);
// VS Code doesn't show a notification when an extension fails to activate // VS Code doesn't show a notification when an extension fails to activate
// so we do it ourselves. // so we do it ourselves.
return await activateServer(ctx).catch((err) => { return await activateServer(ctx).catch((err) => {
void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`); void vscode.window.showErrorMessage(
`Cannot activate rust-analyzer extension: ${err.message}`
);
throw err; throw err;
}); });
} }
@ -83,7 +69,6 @@ async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> {
ctx.pushExtCleanup(activateTaskProvider(ctx.config)); ctx.pushExtCleanup(activateTaskProvider(ctx.config));
} }
await ctx.activate();
await initCommonContext(ctx); await initCommonContext(ctx);
if (ctx.config.typingContinueCommentsOnNewline) { if (ctx.config.typingContinueCommentsOnNewline) {
@ -91,17 +76,19 @@ async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> {
} }
vscode.workspace.onDidChangeConfiguration( vscode.workspace.onDidChangeConfiguration(
(_) => async (_) => {
ctx await ctx
.getClient() .clientFetcher()
.then((it) => .client?.sendNotification("workspace/didChangeConfiguration", { settings: "" });
it.sendNotification("workspace/didChangeConfiguration", { settings: "" }) },
)
.catch(log.error),
null, null,
ctx.subscriptions ctx.subscriptions
); );
await ctx.activate().catch((err) => {
void vscode.window.showErrorMessage(`Cannot activate rust-analyzer server: ${err.message}`);
});
return ctx.clientFetcher(); return ctx.clientFetcher();
} }
@ -130,6 +117,7 @@ async function initCommonContext(ctx: Ctx) {
// Commands which invokes manually via command palette, shortcut, etc. // Commands which invokes manually via command palette, shortcut, etc.
ctx.registerCommand("reload", (_) => async () => { ctx.registerCommand("reload", (_) => async () => {
void vscode.window.showInformationMessage("Reloading rust-analyzer..."); void vscode.window.showInformationMessage("Reloading rust-analyzer...");
// FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed
await ctx.disposeClient(); await ctx.disposeClient();
await ctx.activate(); await ctx.activate();
}); });