diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 676847dd71a..fc9f72d62c5 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -69,7 +69,7 @@ class CustomVariableResolver extends AbstractVariableResolverService { getLineNumber: (): string | undefined => { return resolvedVariables['lineNumber']; } - }, undefined, Promise.resolve(env)); + }, undefined, Promise.resolve(os.homedir()), Promise.resolve(env)); } } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index ac4d2f1542f..31921d3cbef 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -941,7 +941,12 @@ export class ExtHostDebugConsole { export class ExtHostVariableResolverService extends AbstractVariableResolverService { - constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, editorTabs: IExtHostEditorTabs, workspaceService?: IExtHostWorkspace) { + constructor(folders: vscode.WorkspaceFolder[], + editorService: ExtHostDocumentsAndEditors | undefined, + configurationService: ExtHostConfigProvider, + editorTabs: IExtHostEditorTabs, + workspaceService?: IExtHostWorkspace, + userHome?: string) { function getActiveUri(): URI | undefined { if (editorService) { const activeEditor = editorService.activeEditor(); @@ -1019,7 +1024,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ } return undefined; } - }, undefined, Promise.resolve(process.env)); + }, undefined, userHome ? Promise.resolve(userHome) : undefined, Promise.resolve(process.env)); } } diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index a0bb7828978..b235b34a8fc 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -5,6 +5,7 @@ import * as nls from 'vs/nls'; import type * as vscode from 'vscode'; +import { homedir } from 'os'; import * as platform from 'vs/base/common/platform'; import { DebugAdapterExecutable, ThemeIcon } from 'vs/workbench/api/common/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter'; @@ -155,7 +156,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService { - return new ExtHostVariableResolverService(folders, editorService, configurationService, this._editorTabs, this._workspaceService); + return new ExtHostVariableResolverService(folders, editorService, configurationService, this._editorTabs, this._workspaceService, homedir()); } } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 094e54aa851..c47e3203461 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -131,7 +131,7 @@ export class ExtHostTask extends ExtHostTaskBase { private async getVariableResolver(workspaceFolders: vscode.WorkspaceFolder[]): Promise { if (this._variableResolver === undefined) { const configProvider = await this._configurationService.getConfigProvider(); - this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, this.editorTabs, this.workspaceService); + this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, this.editorTabs, this.workspaceService, homedir()); } return this._variableResolver; } diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 9917a6ffdda..473e7f2eca8 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -113,7 +113,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR } return undefined; } - }, labelService, envVariablesPromise); + }, labelService, pathService.userHome().then(home => home.path), envVariablesPromise); } public override async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary, target?: ConfigurationTarget): Promise { diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 5f5a35e798f..3106a335007 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -28,6 +28,8 @@ export interface IVariableResolveContext { getLineNumber(): string | undefined; } +type Environment = { env?: IProcessEnvironment; userHome?: string }; + export class AbstractVariableResolverService implements IConfigurationResolverService { static readonly VARIABLE_LHS = '${'; @@ -38,11 +40,13 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe private _context: IVariableResolveContext; private _labelService?: ILabelService; private _envVariablesPromise?: Promise; + private _userHomePromise?: Promise; protected _contributedVariables: Map Promise> = new Map(); - constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _envVariablesPromise?: Promise) { + constructor(_context: IVariableResolveContext, _labelService?: ILabelService, _userHomePromise?: Promise, _envVariablesPromise?: Promise) { this._context = _context; this._labelService = _labelService; + this._userHomePromise = _userHomePromise; if (_envVariablesPromise) { this._envVariablesPromise = _envVariablesPromise.then(envVariables => { return this.prepareEnv(envVariables); @@ -70,7 +74,11 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe public async resolveAsync(root: IWorkspaceFolder | undefined, value: string[]): Promise; public async resolveAsync(root: IWorkspaceFolder | undefined, value: IStringDictionary): Promise>; public async resolveAsync(root: IWorkspaceFolder | undefined, value: any): Promise { - return this.recursiveResolve(await this._envVariablesPromise, root ? root.uri : undefined, value); + const environment: Environment = { + env: await this._envVariablesPromise, + userHome: await this._userHomePromise + }; + return this.recursiveResolve(environment, root ? root.uri : undefined, value); } private async resolveAnyBase(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): Promise { @@ -92,7 +100,11 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe delete result.linux; // substitute all variables recursively in string values - return this.recursiveResolve(await this._envVariablesPromise, workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables); + const environmentPromises: Environment = { + env: await this._envVariablesPromise, + userHome: await this._userHomePromise + }; + return this.recursiveResolve(environmentPromises, workspaceFolder ? workspaceFolder.uri : undefined, result, commandValueMapping, resolvedVariables); } public async resolveAnyAsync(workspaceFolder: IWorkspaceFolder | undefined, config: any, commandValueMapping?: IStringDictionary): Promise { @@ -121,7 +133,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } } - private recursiveResolve(environment: IProcessEnvironment | undefined, folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): any { + private recursiveResolve(environment: Environment, folderUri: uri | undefined, value: any, commandValueMapping?: IStringDictionary, resolvedVariables?: Map): any { if (types.isString(value)) { return this.resolveString(environment, folderUri, value, commandValueMapping, resolvedVariables); } else if (types.isArray(value)) { @@ -137,7 +149,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return value; } - private resolveString(environment: IProcessEnvironment | undefined, folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary | undefined, resolvedVariables?: Map): string { + private resolveString(environment: Environment, folderUri: uri | undefined, value: string, commandValueMapping: IStringDictionary | undefined, resolvedVariables?: Map): string { // loop through all variables occurrences in 'value' const replaced = value.replace(AbstractVariableResolverService.VARIABLE_REGEXP, (match: string, variable: string) => { @@ -166,7 +178,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe return this._labelService ? this._labelService.getUriLabel(displayUri, { noPrefix: true }) : displayUri.fsPath; } - private evaluateSingleVariable(environment: IProcessEnvironment | undefined, match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary | undefined): string { + private evaluateSingleVariable(environment: Environment, match: string, variable: string, folderUri: uri | undefined, commandValueMapping: IStringDictionary | undefined): string { // try to separate variable arguments from variable name let argument: string | undefined; @@ -225,9 +237,9 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe case 'env': if (argument) { - if (environment) { + if (environment.env) { // Depending on the source of the environment, on Windows, the values may all be lowercase. - const env = environment[isWindows ? argument.toLowerCase() : argument]; + const env = environment.env[isWindows ? argument.toLowerCase() : argument]; if (types.isString(env)) { return env; } @@ -270,6 +282,13 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe case 'workspaceFolderBasename': return paths.basename(this.fsPath(getFolderUri())); + case 'userHome': { + if (environment.userHome) { + return environment.userHome; + } + throw new Error(localize('canNotResolveUserHome', "Variable {0} can not be resolved. UserHome path is not defined", match)); + } + case 'lineNumber': { const lineNumber = this._context.getLineNumber(); if (lineNumber) {