diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 871087e2be6..6c6acac7a80 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -335,7 +335,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape private _onRequestDefaultShellAndArgs(request: IDefaultShellAndArgsRequest): void { if (this._isPrimaryExtHost()) { - this._proxy.$requestDefaultShellAndArgs().then(e => request(e.shell, e.args)); + this._proxy.$requestDefaultShellAndArgs(request.useAutomationShell).then(e => request.callback(e.shell, e.args)); } } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 4272e94a833..e8fec875029 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -255,7 +255,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostClipboard; }, get shell() { - return extHostTerminalService.getDefaultShell(configProvider); + return extHostTerminalService.getDefaultShell(false, configProvider); }, openExternal(uri: URI) { return extHostWindow.openUri(uri, { allowTunneling: !!initData.remote.isRemote }); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ea7865ddb0d..dc50dfafa7c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1166,7 +1166,7 @@ export interface ExtHostTerminalServiceShape { $acceptProcessRequestLatency(id: number): number; $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; $requestAvailableShells(): Promise; - $requestDefaultShellAndArgs(): Promise; + $requestDefaultShellAndArgs(useAutomationShell: boolean): Promise; } export interface ExtHostSCMShape { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 7c6907caa26..fcd283b560c 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -26,7 +26,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal; createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal; attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void; - getDefaultShell(configProvider: ExtHostConfigProvider): string; + getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; } export const IExtHostTerminalService = createDecorator('IExtHostTerminalService'); diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index f18bd5bfe91..87adb02d748 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -349,7 +349,7 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe }).then(async needNewTerminal => { const configProvider = await this._configurationService.getConfigProvider(); - const shell = this._terminalService.getDefaultShell(configProvider); + const shell = this._terminalService.getDefaultShell(true, configProvider); if (needNewTerminal || !this._integratedTerminalInstance) { const options: vscode.TerminalOptions = { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 02de2e82ed6..99a7cc8309c 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -274,7 +274,7 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT this._setupExtHostProcessListeners(id, p); } - public getDefaultShell(configProvider: ExtHostConfigProvider): string { + public getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string { const fetchSetting = (key: string) => { const setting = configProvider .getConfiguration(key.substr(0, key.lastIndexOf('.'))) @@ -289,11 +289,12 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT process.env.windir, this._lastActiveWorkspace, this._variableResolver, - this._logService + this._logService, + useAutomationShell ); } - private _getDefaultShellArgs(configProvider: ExtHostConfigProvider): string[] | string { + private _getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string { const fetchSetting = (key: string) => { const setting = configProvider .getConfiguration(key.substr(0, key.lastIndexOf('.'))) @@ -301,7 +302,7 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT return this._apiInspectConfigToPlain(setting); }; - return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, this._lastActiveWorkspace, this._variableResolver, this._logService); + return terminalEnvironment.getDefaultShellArgs(fetchSetting, this._isWorkspaceShellAllowed, useAutomationShell, this._lastActiveWorkspace, this._variableResolver, this._logService); } public async $acceptActiveTerminalChanged(id: number | null): Promise { @@ -461,8 +462,8 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); const configProvider = await this._extHostConfiguration.getConfigProvider(); if (!shellLaunchConfig.executable) { - shellLaunchConfig.executable = this.getDefaultShell(configProvider); - shellLaunchConfig.args = this._getDefaultShellArgs(configProvider); + shellLaunchConfig.executable = this.getDefaultShell(false, configProvider); + shellLaunchConfig.args = this._getDefaultShellArgs(false, configProvider); } else { if (this._variableResolver) { shellLaunchConfig.executable = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.executable); @@ -603,11 +604,11 @@ export class ExtHostTerminalService implements IExtHostTerminalService, ExtHostT return detectAvailableShells(); } - public async $requestDefaultShellAndArgs(): Promise { + public async $requestDefaultShellAndArgs(useAutomationShell: boolean): Promise { const configProvider = await this._extHostConfiguration.getConfigProvider(); return Promise.resolve({ - shell: this.getDefaultShell(configProvider), - args: this._getDefaultShellArgs(configProvider) + shell: this.getDefaultShell(useAutomationShell, configProvider), + args: this._getDefaultShellArgs(useAutomationShell, configProvider) }); } diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 2559188da9e..9d52970cf2c 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -766,7 +766,7 @@ export class TerminalTaskSystem implements ITaskSystem { let terminalName = this.createTerminalName(task, workspaceFolder); let originalCommand = task.command.name; if (isShellCommand) { - const defaultConfig = await this.terminalInstanceService.getDefaultShellAndArgs(platform); + const defaultConfig = await this.terminalInstanceService.getDefaultShellAndArgs(true, platform); shellLaunchConfig = { name: terminalName, executable: defaultConfig.shell, args: defaultConfig.args, waitOnExit }; let shellSpecified: boolean = false; let shellOptions: ShellConfiguration | undefined = task.command.options && task.command.options.shell; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index f1481ee18f6..dbd0aa41286 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -71,6 +71,21 @@ configurationRegistry.registerConfiguration({ title: nls.localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), type: 'object', properties: { + 'terminal.integrated.automationShell.linux': { + markdownDescription: nls.localize('terminal.integrated.automationShell.linux', "A path that when set will override {0} and ignore {1} and {2} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.linux`', '`shellArgs`', '`env`'), + type: ['string', 'null'], + default: null + }, + 'terminal.integrated.automationShell.osx': { + markdownDescription: nls.localize('terminal.integrated.automationShell.osx', "A path that when set will override {0} and ignore {1} and {2} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.osx`', '`shellArgs`', '`env`'), + type: ['string', 'null'], + default: null + }, + 'terminal.integrated.automationShell.windows': { + markdownDescription: nls.localize('terminal.integrated.automationShell.windows', "A path that when set will override {0} and ignore {1} and {2} values for automation-related terminal usage like tasks and debug.", '`terminal.integrated.shell.windows`', '`shellArgs`', '`env`'), + type: ['string', 'null'], + default: null + }, 'terminal.integrated.shellArgs.linux': { markdownDescription: nls.localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), type: 'array', diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 418b2900f17..42e98f25547 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -30,7 +30,7 @@ export interface ITerminalInstanceService { createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper; createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess; - getDefaultShellAndArgs(platformOverride?: Platform): Promise<{ shell: string, args: string[] | string | undefined }>; + getDefaultShellAndArgs(useAutomationShell: boolean, platformOverride?: Platform): Promise<{ shell: string, args: string[] | string | undefined }>; getMainProcessParentEnv(): Promise; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index 08980a94000..3ed29f8f3bc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -52,11 +52,14 @@ export class TerminalInstanceService implements ITerminalInstanceService { throw new Error('Not implemented'); } - public getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }> { - return new Promise(r => this._onRequestDefaultShellAndArgs.fire((shell, args) => r({ shell, args }))); + public getDefaultShellAndArgs(useAutomationShell: boolean, ): Promise<{ shell: string, args: string[] | string | undefined }> { + return new Promise(r => this._onRequestDefaultShellAndArgs.fire({ + useAutomationShell, + callback: (shell, args) => r({ shell, args }) + })); } public async getMainProcessParentEnv(): Promise { return {}; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 826122750b8..b3ff7db5e88 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -195,7 +195,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce 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(); + const defaultConfig = await this._terminalInstanceService.getDefaultShellAndArgs(false); shellLaunchConfig.executable = defaultConfig.shell; shellLaunchConfig.args = defaultConfig.args; } else { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index eb991bcabfa..9c988c3a6e6 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -76,6 +76,11 @@ export interface ITerminalConfiguration { osx: string | null; windows: string | null; }; + automationShell: { + linux: string | null; + osx: string | null; + windows: string | null; + }; shellArgs: { linux: string[]; osx: string[]; @@ -754,7 +759,8 @@ export interface IAvailableShellsRequest { } export interface IDefaultShellAndArgsRequest { - (shell: string, args: string[] | string | undefined): void; + useAutomationShell: boolean; + callback: (shell: string, args: string[] | string | undefined) => void; } export enum LinuxDistro { diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 4a75398475f..255e7f232ab 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -197,11 +197,10 @@ export function getDefaultShell( lastActiveWorkspace: IWorkspaceFolder | undefined, configurationResolverService: IConfigurationResolverService | undefined, logService: ILogService, + useAutomationShell: boolean, 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}`); - let executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || (shellConfigValue.default || defaultShell); + let executable = getAutomationShell(fetchSetting, isWorkspaceShellAllowed, useAutomationShell, platformOverride) || defaultShell; // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's // safe to assume that this was used by accident as Sysnative does not @@ -222,7 +221,7 @@ export function getDefaultShell( try { executable = configurationResolverService.resolve(lastActiveWorkspace, executable); } catch (e) { - logService.error(`Could not resolve terminal.integrated.shell.${platformKey}`, e); + logService.error(`Could not resolve shell`, e); executable = executable; } } @@ -233,11 +232,18 @@ export function getDefaultShell( export function getDefaultShellArgs( fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined }, isWorkspaceShellAllowed: boolean, + useAutomationShell: boolean, lastActiveWorkspace: IWorkspaceFolder | undefined, configurationResolverService: IConfigurationResolverService | undefined, logService: ILogService, platformOverride: platform.Platform = platform.platform, ): string | string[] { + if (useAutomationShell) { + if (!!getAutomationShell(fetchSetting, isWorkspaceShellAllowed, useAutomationShell, platformOverride)) { + return []; + } + } + const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; const shellArgsConfigValue = fetchSetting(`terminal.integrated.shellArgs.${platformKey}`); let args = ((isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default); @@ -259,6 +265,19 @@ export function getDefaultShellArgs( return args; } +function getAutomationShell( + fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined }, + isWorkspaceShellAllowed: boolean, + useAutomationShell: boolean, + platformOverride: platform.Platform = platform.platform, +): string | null { + const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; + const shellTypeKey = useAutomationShell ? 'automationShell' : 'shell'; + const shellConfigValue = fetchSetting(`terminal.integrated.${shellTypeKey}.${platformKey}`); + const executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || (shellConfigValue.default); + return executable; +} + export function createTerminalEnvironment( shellLaunchConfig: IShellLaunchConfig, lastActiveWorkspace: IWorkspaceFolder | null, diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index 2b7fa1c4fa3..a7c3813e987 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -73,7 +73,7 @@ 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 | string[] }> { + public getDefaultShellAndArgs(useAutomationShell: boolean, 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; @@ -87,11 +87,13 @@ export class TerminalInstanceService implements ITerminalInstanceService { lastActiveWorkspace, this._configurationResolverService, this._logService, + useAutomationShell, platformOverride ); const args = getDefaultShellArgs( (key) => this._configurationService.inspect(key), isWorkspaceShellAllowed, + useAutomationShell, lastActiveWorkspace, this._configurationResolverService, this._logService,