Skip to content

Commit 05b374a

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

File tree

5 files changed

+38
-20
lines changed

5 files changed

+38
-20
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

+18-16
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,13 @@ 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>("server.extraEnv") ??
217+
{};
217218
return substituteVariablesInEnv(
218219
Object.fromEntries(
219220
Object.entries(extraEnv).map(([k, v]) => [
220221
k,
221-
typeof v !== "string" ? v.toString() : v,
222+
typeof v === "string" ? v : v?.toString(),
222223
]),
223224
),
224225
);
@@ -398,22 +399,24 @@ export function prepareVSCodeConfig<T>(resp: T): T {
398399

399400
// FIXME: Merge this with `substituteVSCodeVariables` above
400401
export function substituteVariablesInEnv(env: Env): Env {
402+
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
401403
const missingDeps = new Set<string>();
402404
// vscode uses `env:ENV_NAME` for env vars resolution, and it's easier
403405
// to follow the same convention for our dependency tracking
404406
const definedEnvKeys = new Set(Object.keys(env).map((key) => `env:${key}`));
405407
const envWithDeps = Object.fromEntries(
406408
Object.entries(env).map(([key, value]) => {
407409
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);
410+
if (value) {
411+
let match = undefined;
412+
while ((match = depRe.exec(value))) {
413+
const depName = unwrapUndefinable(match.groups?.["depName"]);
414+
deps.add(depName);
415+
// `depName` at this point can have a form of `expression` or
416+
// `prefix:expression`
417+
if (!definedEnvKeys.has(depName)) {
418+
missingDeps.add(depName);
419+
}
417420
}
418421
}
419422
return [`env:${key}`, { deps: [...deps], value }];
@@ -454,11 +457,10 @@ export function substituteVariablesInEnv(env: Env): Env {
454457
do {
455458
leftToResolveSize = toResolve.size;
456459
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;
460+
const item = envWithDeps[key];
461+
if (item && item.deps.every((dep) => resolved.has(dep))) {
462+
item.value = item.value?.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => {
463+
return envWithDeps[depName]?.value ?? "";
462464
});
463465
resolved.add(key);
464466
toResolve.delete(key);

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)