From 8d9bff0c74518d514d59a1638e4717f14caa1d71 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Fri, 10 Mar 2023 19:35:05 -0500 Subject: [PATCH] Add a workspace config-based approach to reloading discovered projects. --- editors/code/src/client.ts | 8 +++++--- editors/code/src/commands.ts | 12 ++++++++--- editors/code/src/config.ts | 17 +++++++++++---- editors/code/src/ctx.ts | 40 ++++++++++++++++++++++++------------ 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 62980ca0464..9103ef2f8fd 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -3,10 +3,10 @@ import * as lc from "vscode-languageclient/node"; import * as vscode from "vscode"; import * as ra from "../src/lsp_ext"; import * as Is from "vscode-languageclient/lib/common/utils/is"; -import { assert } from "./util"; +import { assert, log } from "./util"; import * as diagnostics from "./diagnostics"; import { WorkspaceEdit } from "vscode"; -import { Config, substituteVSCodeVariables } from "./config"; +import { Config, prepareVSCodeConfig } from "./config"; import { randomUUID } from "crypto"; export interface Env { @@ -95,7 +95,9 @@ export async function createClient( const resp = await next(params, token); if (resp && Array.isArray(resp)) { return resp.map((val) => { - return substituteVSCodeVariables(val); + return prepareVSCodeConfig(val, (key, cfg) => { + cfg[key] = config.discoveredWorkspaces; + }); }); } else { return resp; diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index a8ec75a78aa..8a953577e99 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -758,14 +758,20 @@ export function addProject(ctx: CtxInit): Cmd { const workspaces: JsonProject[] = await Promise.all( vscode.workspace.workspaceFolders!.map(async (folder): Promise => { - return discoverWorkspace(vscode.workspace.textDocuments, discoverProjectCommand, { + const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument); + return discoverWorkspace(rustDocuments, discoverProjectCommand, { cwd: folder.uri.fsPath, }); }) ); - await ctx.client.sendRequest(ra.addProject, { - project: workspaces, + ctx.addToDiscoveredWorkspaces(workspaces); + + // this is a workaround to avoid needing writing the `rust-project.json` into + // a workspace-level VS Code-specific settings folder. We'd like to keep the + // `rust-project.json` entirely in-memory. + await ctx.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { + settings: "", }); }; } diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 1dae603714d..75403725479 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -34,6 +34,7 @@ export class Config { constructor(ctx: vscode.ExtensionContext) { this.globalStorageUri = ctx.globalStorageUri; + this.discoveredWorkspaces = []; vscode.workspace.onDidChangeConfiguration( this.onDidChangeConfiguration, this, @@ -55,6 +56,8 @@ export class Config { log.info("Using configuration", Object.fromEntries(cfg)); } + public discoveredWorkspaces: JsonProject[]; + private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) { this.refreshLogging(); @@ -191,7 +194,7 @@ export class Config { * So this getter handles this quirk by not requiring the caller to use postfix `!` */ private get(path: string): T | undefined { - return substituteVSCodeVariables(this.cfg.get(path)); + return prepareVSCodeConfig(this.cfg.get(path)); } get serverPath() { @@ -284,18 +287,24 @@ export class Config { } } -export function substituteVSCodeVariables(resp: T): T { +export function prepareVSCodeConfig( + resp: T, + cb?: (key: Extract, res: { [key: string]: any }) => void +): T { if (Is.string(resp)) { return substituteVSCodeVariableInString(resp) as T; } else if (resp && Is.array(resp)) { return resp.map((val) => { - return substituteVSCodeVariables(val); + return prepareVSCodeConfig(val); }) as T; } else if (resp && typeof resp === "object") { const res: { [key: string]: any } = {}; for (const key in resp) { const val = resp[key]; - res[key] = substituteVSCodeVariables(val); + res[key] = prepareVSCodeConfig(val); + if (cb) { + cb(key, res); + } } return res as T; } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 407d976bab9..ca30d239c95 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; -import { Config, substituteVSCodeVariables } from "./config"; +import { Config, prepareVSCodeConfig } from "./config"; import { createClient } from "./client"; import { executeDiscoverProject, @@ -54,7 +54,7 @@ export async function discoverWorkspace( command: string[], options: ExecOptions ): Promise { - const paths = files.map((f) => f.uri.fsPath).join(" "); + const paths = files.map((f) => `"${f.uri.fsPath}"`).join(" "); const joinedCommand = command.join(" "); const data = await executeDiscoverProject(`${joinedCommand} ${paths}`, options); return JSON.parse(data) as JsonProject; @@ -71,7 +71,7 @@ export type CtxInit = Ctx & { export class Ctx { readonly statusBar: vscode.StatusBarItem; - readonly config: Config; + config: Config; readonly workspace: Workspace; private _client: lc.LanguageClient | undefined; @@ -82,7 +82,6 @@ export class Ctx { private state: PersistentState; private commandFactories: Record; private commandDisposables: Disposable[]; - private discoveredWorkspaces: JsonProject[] | undefined; get client() { return this._client; @@ -193,20 +192,24 @@ export class Ctx { if (discoverProjectCommand) { const workspaces: JsonProject[] = await Promise.all( vscode.workspace.workspaceFolders!.map(async (folder): Promise => { - return discoverWorkspace( - vscode.workspace.textDocuments, - discoverProjectCommand, - { cwd: folder.uri.fsPath } - ); + const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument); + return discoverWorkspace(rustDocuments, discoverProjectCommand, { + cwd: folder.uri.fsPath, + }); }) ); - this.discoveredWorkspaces = workspaces; + this.addToDiscoveredWorkspaces(workspaces); } - const initializationOptions = substituteVSCodeVariables(rawInitializationOptions); - // this appears to be load-bearing, for better or worse. - await initializationOptions.update("linkedProjects", this.discoveredWorkspaces); + const initializationOptions = prepareVSCodeConfig( + rawInitializationOptions, + (key, obj) => { + if (key === "linkedProjects") { + obj["linkedProjects"] = this.config.discoveredWorkspaces; + } + } + ); this._client = await createClient( this.traceOutputChannel, @@ -288,6 +291,17 @@ export class Ctx { return this._serverPath; } + addToDiscoveredWorkspaces(workspaces: JsonProject[]) { + for (const workspace of workspaces) { + const index = this.config.discoveredWorkspaces.indexOf(workspace); + if (~index) { + this.config.discoveredWorkspaces[index] = workspace; + } else { + this.config.discoveredWorkspaces.push(workspace); + } + } + } + private updateCommands(forceDisable?: "disable") { this.commandDisposables.forEach((disposable) => disposable.dispose()); this.commandDisposables = [];