diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index d2dc740c09b5..3b1b0768d3cf 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -8,10 +8,9 @@ import type { Disposable } from "vscode"; export type RunnableEnvCfgItem = { mask?: string; - env: Record; + env: { [key: string]: { toString(): string } | null }; platform?: string | string[]; }; -export type RunnableEnvCfg = Record | RunnableEnvCfgItem[]; type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSelector }; @@ -261,18 +260,13 @@ export class Config { return this.get("testExplorer"); } - runnablesExtraEnv(label: string): Record | undefined { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const item = this.get("runnables.extraEnv") ?? this.get("runnableEnv"); - if (!item) return undefined; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const fixRecord = (r: Record) => { - for (const key in r) { - if (typeof r[key] !== "string") { - r[key] = String(r[key]); - } - } - }; + runnablesExtraEnv(label: string): Env { + const serverEnv = this.serverExtraEnv; + let extraEnv = + this.get< + RunnableEnvCfgItem[] | { [key: string]: { toString(): string } | null } | null + >("runnables.extraEnv") ?? {}; + if (!extraEnv) return serverEnv; const platform = process.platform; const checkPlatform = (it: RunnableEnvCfgItem) => { @@ -283,19 +277,25 @@ export class Config { return true; }; - if (item instanceof Array) { + if (extraEnv instanceof Array) { const env = {}; - for (const it of item) { + for (const it of extraEnv) { const masked = !it.mask || new RegExp(it.mask).test(label); if (masked && checkPlatform(it)) { Object.assign(env, it.env); } } - fixRecord(env); - return env; + extraEnv = env; } - fixRecord(item); - return item; + const runnableExtraEnv = substituteVariablesInEnv( + Object.fromEntries( + Object.entries(extraEnv).map(([k, v]) => [ + k, + typeof v === "string" ? v : v?.toString(), + ]), + ), + ); + return { ...runnableExtraEnv, ...serverEnv }; } get restartServerOnConfigChange() { diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index adb75c23c70c..24f8d9087300 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -6,7 +6,14 @@ import type * as ra from "./lsp_ext"; import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { createTaskFromRunnable, prepareEnv } from "./run"; -import { execute, isCargoRunnableArgs, unwrapUndefinable, log, normalizeDriveLetter } from "./util"; +import { + execute, + isCargoRunnableArgs, + unwrapUndefinable, + log, + normalizeDriveLetter, + Env, +} from "./util"; import type { Config } from "./config"; // Here we want to keep track on everything that's currently running @@ -206,10 +213,7 @@ type SourceFileMap = { destination: string; }; -async function discoverSourceFileMap( - env: Record, - cwd: string, -): Promise { +async function discoverSourceFileMap(env: Env, cwd: string): Promise { const sysroot = env["RUSTC_TOOLCHAIN"]; if (sysroot) { // let's try to use the default toolchain @@ -232,7 +236,7 @@ type PropertyFetcher = ( type DebugConfigProvider> = { executableProperty: keyof DebugConfig; - environmentProperty: PropertyFetcher, keyof DebugConfig>; + environmentProperty: PropertyFetcher; runnableArgsProperty: PropertyFetcher; sourceFileMapProperty?: keyof DebugConfig; type: Type; @@ -276,7 +280,7 @@ const knownEngines: { "environment", Object.entries(env).map((entry) => ({ name: entry[0], - value: entry[1], + value: entry[1] ?? "", })), ], runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [ @@ -304,10 +308,7 @@ const knownEngines: { }, }; -async function getDebugExecutable( - runnableArgs: ra.CargoRunnableArgs, - env: Record, -): Promise { +async function getDebugExecutable(runnableArgs: ra.CargoRunnableArgs, env: Env): Promise { const cargo = new Cargo(runnableArgs.workspaceRoot || ".", env); const executable = await cargo.executableFromArgs(runnableArgs); @@ -328,7 +329,7 @@ function getDebugConfig( runnable: ra.Runnable, runnableArgs: ra.CargoRunnableArgs, executable: string, - env: Record, + env: Env, sourceFileMap?: Record, ): vscode.DebugConfiguration { const { @@ -380,14 +381,14 @@ type CodeLldbDebugConfig = { args: string[]; sourceMap: Record | undefined; sourceLanguages: ["rust"]; - env: Record; + env: Env; } & BaseDebugConfig<"lldb">; type NativeDebugConfig = { target: string; // See https://github.com/WebFreak001/code-debug/issues/359 arguments: string; - env: Record; + env: Env; valuesFormatting: "prettyPrinters"; } & BaseDebugConfig<"gdb">; diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index 95166c427b2b..87c1d529f7ee 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -7,7 +7,7 @@ import type { CtxInit } from "./ctx"; import { makeDebugConfig } from "./debug"; import type { Config } from "./config"; import type { LanguageClient } from "vscode-languageclient/node"; -import { log, unwrapUndefinable, type RustEditor } from "./util"; +import { Env, log, unwrapUndefinable, type RustEditor } from "./util"; const quickPickButtons = [ { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." }, @@ -122,11 +122,8 @@ export class RunnableQuickPick implements vscode.QuickPickItem { } } -export function prepareBaseEnv( - inheritEnv: boolean, - base?: Record, -): Record { - const env: Record = { RUST_BACKTRACE: "short" }; +export function prepareBaseEnv(inheritEnv: boolean, base?: Env): Env { + const env: Env = { RUST_BACKTRACE: "short" }; if (inheritEnv) { Object.assign(env, process.env); } @@ -136,11 +133,7 @@ export function prepareBaseEnv( return env; } -export function prepareEnv( - inheritEnv: boolean, - runnableEnv?: Record, - runnableEnvCfg?: Record, -): Record { +export function prepareEnv(inheritEnv: boolean, runnableEnv?: Env, runnableEnvCfg?: Env): Env { const env = prepareBaseEnv(inheritEnv, runnableEnv); if (runnableEnvCfg) { diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts index 730ec6d1e90c..eb0748a704bf 100644 --- a/src/tools/rust-analyzer/editors/code/src/tasks.ts +++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode"; import type { Config } from "./config"; import * as toolchain from "./toolchain"; +import { Env } from "./util"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. @@ -117,8 +118,8 @@ export async function buildRustTask( export async function targetToExecution( definition: TaskDefinition, options?: { - env?: { [key: string]: string }; cwd?: string; + env?: Env; }, cargo?: string, ): Promise { @@ -131,7 +132,12 @@ export async function targetToExecution( command = definition.command; args = definition.args || []; } - return new vscode.ProcessExecution(command, args, options); + return new vscode.ProcessExecution(command, args, { + cwd: options?.cwd, + env: Object.fromEntries( + Object.entries(options?.env ?? {}).map(([key, value]) => [key, value ?? ""]), + ), + }); } export function activateTaskProvider(config: Config): vscode.Disposable { diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index a859ce6ff007..06f75a8f8d65 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -3,7 +3,7 @@ import * as os from "os"; import * as path from "path"; import * as readline from "readline"; import * as vscode from "vscode"; -import { log, memoizeAsync, unwrapUndefinable } from "./util"; +import { Env, log, memoizeAsync, unwrapUndefinable } from "./util"; import type { CargoRunnableArgs } from "./lsp_ext"; interface CompilationArtifact { @@ -37,7 +37,7 @@ interface CompilerMessage { export class Cargo { constructor( readonly rootFolder: string, - readonly env: Record, + readonly env: Env, ) {} // Made public for testing purposes @@ -156,7 +156,7 @@ export class Cargo { /** Mirrors `toolchain::cargo()` implementation */ // FIXME: The server should provide this -export function cargoPath(env?: Record): Promise { +export function cargoPath(env?: Env): Promise { if (env?.["RUSTC_TOOLCHAIN"]) { return Promise.resolve("cargo"); }