Merge pull request #115758 from microsoft/tyriar/115053

Support vscode install directory variable and resolve in env var collection variables
This commit is contained in:
Daniel Imms 2021-02-05 07:48:48 -08:00 committed by GitHub
commit 80a738c2ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 50 additions and 20 deletions

View file

@ -945,6 +945,9 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
getConfigurationValue: (folderUri: URI | undefined, section: string): string | undefined => {
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
},
getAppRoot: (): string | undefined => {
return env ? env['VSCODE_CWD'] : undefined;
},
getExecPath: (): string | undefined => {
return env ? env['VSCODE_EXEC_PATH'] : undefined;
},

View file

@ -201,10 +201,11 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect<ITerminalEnvironment>(`env.${platformKey}`));
const baseEnv = terminalConfig.get<boolean>('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv();
const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver);
const env = terminalEnvironment.createTerminalEnvironment(
shellLaunchConfig,
envFromConfig,
terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver),
variableResolver,
isWorkspaceShellAllowed,
this._extHostInitDataService.version,
terminalConfig.get<'auto' | 'off' | 'on'>('detectLocale', 'auto'),
@ -214,7 +215,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
// Apply extension environment variable collections to the environment
if (!shellLaunchConfig.strictEnv) {
const mergedCollection = new MergedEnvironmentVariableCollection(this._environmentVariableCollections);
mergedCollection.applyToProcessEnvironment(env);
mergedCollection.applyToProcessEnvironment(env, variableResolver);
}
this._proxy.$sendResolvedLaunchConfig(id, shellLaunchConfig);

View file

@ -248,7 +248,8 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
this._configHelper.showRecommendations(shellLaunchConfig);
const baseEnv = this._configHelper.config.inheritEnv ? processEnv : await this._terminalInstanceService.getMainProcessParentEnv();
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._configurationResolverService), isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.detectLocale, baseEnv);
const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._configurationResolverService);
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, envFromConfigValue, variableResolver, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.detectLocale, baseEnv);
if (!shellLaunchConfig.strictEnv) {
this._extEnvironmentVariableCollection = this._environmentVariableService.mergedCollection;
@ -259,7 +260,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce
// info widget. While technically these could differ due to the slight change of a race
// condition, the chance is minimal plus the impact on the user is also not that great
// if it happens - it's not worth adding plumbing to sync back the resolved collection.
this._extEnvironmentVariableCollection.applyToProcessEnvironment(env);
this._extEnvironmentVariableCollection.applyToProcessEnvironment(env, variableResolver);
if (this._extEnvironmentVariableCollection.map.size > 0) {
this._environmentVariableInfo = new EnvironmentVariableInfoChangesActive(this._extEnvironmentVariableCollection);
this._onEnvironmentVariableInfoChange.fire(this._environmentVariableInfo);

View file

@ -48,8 +48,10 @@ export interface IMergedEnvironmentVariableCollection {
/**
* Applies this collection to a process environment.
* @param variableResolver An optional function to use to resolve variables within the
* environment values.
*/
applyToProcessEnvironment(env: IProcessEnvironment): void;
applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: (str: string) => string): void;
/**
* Generates a diff of this connection against another. Returns undefined if the collections are

View file

@ -41,7 +41,7 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
});
}
applyToProcessEnvironment(env: IProcessEnvironment): void {
applyToProcessEnvironment(env: IProcessEnvironment, variableResolver?: (str: string) => string): void {
let lowerToActualVariableNames: { [lowerKey: string]: string | undefined } | undefined;
if (isWindows) {
lowerToActualVariableNames = {};
@ -50,15 +50,16 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa
this.map.forEach((mutators, variable) => {
const actualVariable = isWindows ? lowerToActualVariableNames![variable.toLowerCase()] || variable : variable;
mutators.forEach(mutator => {
const value = variableResolver ? variableResolver(mutator.value) : mutator.value;
switch (mutator.type) {
case EnvironmentVariableMutatorType.Append:
env[actualVariable] = (env[actualVariable] || '') + mutator.value;
env[actualVariable] = (env[actualVariable] || '') + value;
break;
case EnvironmentVariableMutatorType.Prepend:
env[actualVariable] = mutator.value + (env[actualVariable] || '');
env[actualVariable] = value + (env[actualVariable] || '');
break;
case EnvironmentVariableMutatorType.Replace:
env[actualVariable] = mutator.value;
env[actualVariable] = value;
break;
}
});

View file

@ -27,7 +27,10 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
static readonly INPUT_OR_COMMAND_VARIABLES_PATTERN = /\${((input|command):(.*?))}/g;
constructor(
context: { getExecPath: () => string | undefined },
context: {
getAppRoot: () => string | undefined,
getExecPath: () => string | undefined
},
envVariables: IProcessEnvironment,
editorService: IEditorService,
private readonly configurationService: IConfigurationService,
@ -47,6 +50,9 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
getConfigurationValue: (folderUri: uri | undefined, suffix: string): string | undefined => {
return configurationService.getValue<string>(suffix, folderUri ? { resource: folderUri } : {});
},
getAppRoot: (): string | undefined => {
return context.getAppRoot();
},
getExecPath: (): string | undefined => {
return context.getExecPath();
},
@ -364,7 +370,7 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
@IQuickInputService quickInputService: IQuickInputService,
@ILabelService labelService: ILabelService
) {
super({ getExecPath: () => undefined }, Object.create(null), editorService, configurationService, commandService, workspaceContextService, quickInputService, labelService);
super({ getAppRoot: () => undefined, getExecPath: () => undefined }, Object.create(null), editorService, configurationService, commandService, workspaceContextService, quickInputService, labelService);
}
}

View file

@ -20,6 +20,7 @@ export interface IVariableResolveContext {
getFolderUri(folderName: string): uri | undefined;
getWorkspaceFolderCount(): number;
getConfigurationValue(folderUri: uri | undefined, section: string): string | undefined;
getAppRoot(): string | undefined;
getExecPath(): string | undefined;
getFilePath(): string | undefined;
getWorkspaceFolderPathForFile?(): string | undefined;
@ -313,6 +314,13 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe
}
return match;
case 'execInstallFolder':
const ar = this._context.getAppRoot();
if (ar) {
return ar;
}
return match;
case 'pathSeparator':
return paths.sep;

View file

@ -28,6 +28,9 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi
@ILabelService labelService: ILabelService
) {
super({
getAppRoot: (): string | undefined => {
return environmentService.appRoot;
},
getExecPath: (): string | undefined => {
return environmentService.execPath;
}

View file

@ -53,6 +53,11 @@ class TestConfigurationResolverService extends BaseConfigurationResolverService
}
const nullContext = {
getAppRoot: () => undefined,
getExecPath: () => undefined
};
suite('Configuration Resolver Service', () => {
let configurationResolverService: IConfigurationResolverService | null;
let envVariables: { [key: string]: string } = { key1: 'Value for key1', key2: 'Value for key2' };
@ -72,7 +77,7 @@ suite('Configuration Resolver Service', () => {
labelService = new MockLabelService();
containingWorkspace = testWorkspace(uri.parse('file:///VSCode/workspaceLocation'));
workspace = containingWorkspace.folders[0];
configurationResolverService = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(containingWorkspace), quickInputService, labelService);
configurationResolverService = new TestConfigurationResolverService(nullContext, environmentService.userEnv, editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(containingWorkspace), quickInputService, labelService);
});
teardown(() => {
@ -211,7 +216,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz');
});
@ -222,7 +227,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(undefined, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz');
});
@ -239,7 +244,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz');
});
@ -256,7 +261,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
if (platform.isWindows) {
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for key1 xyz');
} else {
@ -277,7 +282,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
if (platform.isWindows) {
assert.strictEqual(service.resolve(workspace, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceFolder} - ${workspaceFolder} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for key1 - Value for key2');
} else {
@ -311,7 +316,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz');
});
@ -321,7 +326,7 @@ suite('Configuration Resolver Service', () => {
editor: {}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.strictEqual(service.resolve(workspace, 'abc ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz');
assert.strictEqual(service.resolve(workspace, 'abc ${env:unknownVariable} xyz'), 'abc xyz');
});
@ -334,7 +339,7 @@ suite('Configuration Resolver Service', () => {
}
});
let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
let service = new TestConfigurationResolverService(nullContext, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService, labelService);
assert.throws(() => service.resolve(workspace, 'abc ${env} xyz'));
assert.throws(() => service.resolve(workspace, 'abc ${env:} xyz'));