diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 56d11496bb0..37044a2f362 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -286,6 +286,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { private _terminalProcesses: { [id: number]: ITerminalChildProcess } = {}; private _terminalRenderers: ExtHostTerminalRenderer[] = []; private _getTerminalPromises: { [id: number]: Promise } = {}; + private _variableResolver: ExtHostVariableResolverService | undefined; + private _lastActiveWorkspace: IWorkspaceFolder | undefined; // TODO: Pull this from main side private _isWorkspaceShellAllowed: boolean = false; @@ -307,9 +309,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { private _extHostConfiguration: ExtHostConfiguration, private _extHostWorkspace: ExtHostWorkspace, private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors, - private _logService: ILogService, + private _logService: ILogService ) { this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService); + this.updateLastActiveWorkspace(); + this.updateVariableResolver(); + this.registerListeners(); } public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal { @@ -366,18 +371,21 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { this._isWorkspaceShellAllowed, getSystemShell(platform.platform), process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'), - process.env.windir + process.env.windir, + this._lastActiveWorkspace, + this._variableResolver ); } - private _getDefaultShellArgs(configProvider: ExtHostConfigProvider): string[] | string | undefined { + private _getDefaultShellArgs(configProvider: ExtHostConfigProvider): string[] | string { const fetchSetting = (key: string) => { const setting = configProvider .getConfiguration(key.substr(0, key.lastIndexOf('.'))) .inspect(key.substr(key.lastIndexOf('.') + 1)); return this._apiInspectConfigToPlain(setting); }; - return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed); + + return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, this._lastActiveWorkspace, this._variableResolver); } public async resolveTerminalRenderer(id: number): Promise { @@ -526,6 +534,24 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { return env; } + private registerListeners(): void { + this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(() => this.updateLastActiveWorkspace()); + this._extHostWorkspace.onDidChangeWorkspace(() => this.updateVariableResolver()); + } + + private updateLastActiveWorkspace(): void { + const activeEditor = this._extHostDocumentsAndEditors.activeEditor(); + if (activeEditor) { + this._lastActiveWorkspace = this._extHostWorkspace.getWorkspaceFolder(activeEditor.document.uri) as IWorkspaceFolder; + } + } + + private async updateVariableResolver(): Promise { + const configProvider = await this._extHostConfiguration.getConfigProvider(); + const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2(); + this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider); + } + public async $createProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise { const shellLaunchConfig: IShellLaunchConfig = { name: shellLaunchConfigDto.name, @@ -541,6 +567,21 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { if (!shellLaunchConfig.executable) { shellLaunchConfig.executable = this.getDefaultShell(configProvider); shellLaunchConfig.args = this._getDefaultShellArgs(configProvider); + } else { + if (this._variableResolver) { + shellLaunchConfig.executable = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.executable); + if (shellLaunchConfig.args) { + if (Array.isArray(shellLaunchConfig.args)) { + const resolvedArgs: string[] = []; + for (const arg of shellLaunchConfig.args) { + resolvedArgs.push(this._variableResolver.resolve(this._lastActiveWorkspace, arg)); + } + shellLaunchConfig.args = resolvedArgs; + } else { + shellLaunchConfig.args = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.args); + } + } + } } // Get the initial cwd @@ -559,14 +600,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { } } as IWorkspaceFolder : null; const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect(`env.${platformKey}`)); - const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2(); - const variableResolver = workspaceFolders ? new ExtHostVariableResolverService(workspaceFolders, this._extHostDocumentsAndEditors, configProvider) : undefined; const baseEnv = terminalConfig.get('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv(); const env = terminalEnvironment.createTerminalEnvironment( shellLaunchConfig, lastActiveWorkspace, envFromConfig, - variableResolver, + this._variableResolver, isWorkspaceShellAllowed, pkg.version, terminalConfig.get('setLocaleVariables', false), diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 5bbb0612709..bfa9741748e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -184,17 +184,29 @@ export class TerminalProcessManager implements ITerminalProcessManager { rows: number, isScreenReaderModeEnabled: boolean ): Promise { + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); + const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); + const lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; if (!shellLaunchConfig.executable) { const defaultConfig = await this._terminalInstanceService.getDefaultShellAndArgs(); shellLaunchConfig.executable = defaultConfig.shell; shellLaunchConfig.args = defaultConfig.args; + } else { + shellLaunchConfig.executable = this._configurationResolverService.resolve(lastActiveWorkspace === null ? undefined : lastActiveWorkspace, shellLaunchConfig.executable); + if (shellLaunchConfig.args) { + if (Array.isArray(shellLaunchConfig.args)) { + const resolvedArgs: string[] = []; + for (const arg of shellLaunchConfig.args) { + resolvedArgs.push(this._configurationResolverService.resolve(lastActiveWorkspace === null ? undefined : lastActiveWorkspace, arg)); + } + shellLaunchConfig.args = resolvedArgs; + } else { + shellLaunchConfig.args = this._configurationResolverService.resolve(lastActiveWorkspace === null ? undefined : lastActiveWorkspace, shellLaunchConfig.args); + } + } } - const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd); - - const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); - const lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; const envFromConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.env.${platformKey}`); const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); const baseEnv = this._configHelper.config.inheritEnv ? process.env as platform.IProcessEnvironment : await this._terminalInstanceService.getMainProcessParentEnv(); diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index db22a46964d..58b897d4aa6 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -169,7 +169,9 @@ export function getDefaultShell( defaultShell: string, isWoW64: boolean, windir: string | undefined, - platformOverride: platform.Platform = platform.platform + lastActiveWorkspace: IWorkspaceFolder | undefined, + configurationResolverService: IConfigurationResolverService | undefined, + platformOverride: platform.Platform = platform.platform, ): string { const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; const shellConfigValue = fetchSetting(`terminal.integrated.shell.${platformKey}`); @@ -190,17 +192,33 @@ export function getDefaultShell( executable = executable.replace(/\//g, '\\'); } + if (configurationResolverService) { + executable = configurationResolverService.resolve(lastActiveWorkspace, executable); + } + return executable; } export function getDefaultShellArgs( fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined }, isWorkspaceShellAllowed: boolean, - platformOverride: platform.Platform = platform.platform -): string[] { + lastActiveWorkspace: IWorkspaceFolder | undefined, + configurationResolverService: IConfigurationResolverService | undefined, + platformOverride: platform.Platform = platform.platform, +): string | string[] { const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; const shellArgsConfigValue = fetchSetting(`terminal.integrated.shellArgs.${platformKey}`); - const args = (isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default; + let args = ((isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default); + if (typeof args === 'string' && platformOverride === platform.Platform.Windows) { + return configurationResolverService ? configurationResolverService.resolve(lastActiveWorkspace, args) : args; + } + if (configurationResolverService) { + const resolvedArgs: string[] = []; + for (const arg of args) { + resolvedArgs.push(configurationResolverService.resolve(lastActiveWorkspace, arg)); + } + args = resolvedArgs; + } return args; } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index 94b5a713671..e9b13c0d68f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -17,6 +17,9 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { getDefaultShell, getDefaultShellArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; let Terminal: typeof XTermTerminal; let WebLinksAddon: typeof XTermWebLinksAddon; @@ -28,7 +31,10 @@ export class TerminalInstanceService implements ITerminalInstanceService { constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IStorageService private readonly _storageService: IStorageService + @IStorageService private readonly _storageService: IStorageService, + @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @IHistoryService private readonly _historyService: IHistoryService ) { } @@ -65,19 +71,26 @@ export class TerminalInstanceService implements ITerminalInstanceService { return this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, false); } - public getDefaultShellAndArgs(platformOverride: Platform = platform): Promise<{ shell: string, args: string[] | undefined }> { + public getDefaultShellAndArgs(platformOverride: Platform = platform): Promise<{ shell: string, args: string | string[] }> { const isWorkspaceShellAllowed = this._isWorkspaceShellAllowed(); + const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); + let lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : undefined; + lastActiveWorkspace = lastActiveWorkspace === null ? undefined : lastActiveWorkspace; const shell = getDefaultShell( (key) => this._configurationService.inspect(key), isWorkspaceShellAllowed, getSystemShell(platformOverride), process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'), process.env.windir, + lastActiveWorkspace, + this._configurationResolverService, platformOverride ); const args = getDefaultShellArgs( (key) => this._configurationService.inspect(key), isWorkspaceShellAllowed, + lastActiveWorkspace, + this._configurationResolverService, platformOverride ); return Promise.resolve({ shell, args });