Skip to content

Commit 46cd1ce

Browse files
committed
feat: Allow unsetting env vars in server.extraEnv config
1 parent 66e3b58 commit 46cd1ce

File tree

5 files changed

+41
-21
lines changed

5 files changed

+41
-21
lines changed

editors/code/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,8 @@
543543
"additionalProperties": {
544544
"type": [
545545
"string",
546-
"number"
546+
"number",
547+
"null"
547548
]
548549
},
549550
"default": null,

editors/code/src/bootstrap.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,16 @@ async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean>
187187
export async function isValidExecutable(path: string, extraEnv: Env): Promise<boolean> {
188188
log.debug("Checking availability of a binary at", path);
189189

190+
const newEnv = { ...process.env };
191+
for (const [k, v] of Object.entries(extraEnv)) {
192+
if (v) {
193+
newEnv[k] = v;
194+
} else if (k in newEnv) {
195+
delete newEnv[k];
196+
}
197+
}
190198
const res = await spawnAsync(path, ["--version"], {
191-
env: { ...process.env, ...extraEnv },
199+
env: newEnv,
192200
});
193201

194202
if (res.error) {

editors/code/src/config.ts

+21-17
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,14 @@ export class Config {
213213

214214
get serverExtraEnv(): Env {
215215
const extraEnv =
216-
this.get<{ [key: string]: string | number } | null>("server.extraEnv") ?? {};
216+
this.get<{ [key: string]: { toString(): string } | null } | null>(
217+
"server.extraEnv",
218+
) ?? {};
217219
return substituteVariablesInEnv(
218220
Object.fromEntries(
219221
Object.entries(extraEnv).map(([k, v]) => [
220222
k,
221-
typeof v !== "string" ? v.toString() : v,
223+
typeof v === "string" ? v : v?.toString(),
222224
]),
223225
),
224226
);
@@ -398,22 +400,24 @@ export function prepareVSCodeConfig<T>(resp: T): T {
398400

399401
// FIXME: Merge this with `substituteVSCodeVariables` above
400402
export function substituteVariablesInEnv(env: Env): Env {
403+
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
401404
const missingDeps = new Set<string>();
402405
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
403406
// to follow the same convention for our dependency tracking
404407
const definedEnvKeys = new Set(Object.keys(env).map((key) => `env:${key}`));
405408
const envWithDeps = Object.fromEntries(
406409
Object.entries(env).map(([key, value]) => {
407410
const deps = new Set<string>();
408-
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
409-
let match = undefined;
410-
while ((match = depRe.exec(value))) {
411-
const depName = unwrapUndefinable(match.groups?.["depName"]);
412-
deps.add(depName);
413-
// `depName` at this point can have a form of `expression` or
414-
// `prefix:expression`
415-
if (!definedEnvKeys.has(depName)) {
416-
missingDeps.add(depName);
411+
if (value) {
412+
let match = undefined;
413+
while ((match = depRe.exec(value))) {
414+
const depName = unwrapUndefinable(match.groups?.["depName"]);
415+
deps.add(depName);
416+
// `depName` at this point can have a form of `expression` or
417+
// `prefix:expression`
418+
if (!definedEnvKeys.has(depName)) {
419+
missingDeps.add(depName);
420+
}
417421
}
418422
}
419423
return [`env:${key}`, { deps: [...deps], value }];
@@ -454,12 +458,12 @@ export function substituteVariablesInEnv(env: Env): Env {
454458
do {
455459
leftToResolveSize = toResolve.size;
456460
for (const key of toResolve) {
457-
const item = unwrapUndefinable(envWithDeps[key]);
458-
if (item.deps.every((dep) => resolved.has(dep))) {
459-
item.value = item.value.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => {
460-
const item = unwrapUndefinable(envWithDeps[depName]);
461-
return item.value;
462-
});
461+
const item = envWithDeps[key];
462+
if (item && item.deps.every((dep) => resolved.has(dep))) {
463+
item.value =
464+
item.value?.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => {
465+
return envWithDeps[depName]?.value ?? "";
466+
});
463467
resolved.add(key);
464468
toResolve.delete(key);
465469
}

editors/code/src/ctx.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,14 @@ export class Ctx implements RustAnalyzerExtensionApi {
213213
this.refreshServerStatus();
214214
},
215215
);
216-
const newEnv = Object.assign({}, process.env, this.config.serverExtraEnv);
216+
const newEnv = { ...process.env };
217+
for (const [k, v] of Object.entries(this.config.serverExtraEnv)) {
218+
if (v) {
219+
newEnv[k] = v;
220+
} else if (k in newEnv) {
221+
delete newEnv[k];
222+
}
223+
}
217224
const run: lc.Executable = {
218225
command: this._serverPath,
219226
options: { env: newEnv },

editors/code/src/util.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function assert(condition: boolean, explanation: string): asserts conditi
1414
}
1515

1616
export type Env = {
17-
[name: string]: string;
17+
[name: string]: string | undefined;
1818
};
1919

2020
class Log {

0 commit comments

Comments
 (0)