diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 317a06d6a5b..1f8752a3f71 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1110,6 +1110,12 @@ declare module 'vscode' { } namespace window { + /** + * The detected default shell for the extension host, this is overridden by the + * `terminal.integrated.shell` setting for the extension host's platform. + */ + export const shell: string; + /** * An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change. */ diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index dc6f2cd2e16..0edd04ad5b8 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -391,6 +391,9 @@ export function createApiFactory( get terminals() { return extHostTerminalService.terminals; }, + get shell() { + return extHostTerminalService.getDefaultShell(configProvider); + }, showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable { let documentPromise: Promise; if (URI.isUri(documentOrUri)) { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 2aad0a4c38f..ec4d4771989 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -11,7 +11,7 @@ import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { Event, Emitter } from 'vs/base/common/event'; import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/common/extHost.protocol'; -import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; +import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration'; import { ILogService } from 'vs/platform/log/common/log'; import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; @@ -278,6 +278,9 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { private _terminalRenderers: ExtHostTerminalRenderer[] = []; private _getTerminalPromises: { [id: number]: Promise } = {}; + // TODO: Pull this from main side + private _isWorkspaceShellAllowed: boolean = false; + public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; } public get terminals(): ExtHostTerminal[] { return this._terminals; } @@ -325,6 +328,16 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { return renderer; } + public getDefaultShell(configProvider: ExtHostConfigProvider): 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.getDefaultShell(fetchSetting, this._isWorkspaceShellAllowed, getDefaultShell(platform.platform)); + } + public async resolveTerminalRenderer(id: number): Promise { // Check to see if the extension host already knows about this terminal. for (const terminalRenderer of this._terminalRenderers) { diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 0e49495815a..4c850cc9831 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -161,6 +161,46 @@ export function escapeNonWindowsPath(path: string): string { return newPath; } +export function getDefaultShell( + fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined }, + isWorkspaceShellAllowed: boolean, + defaultShell: string, + 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); + + // 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 + // exist and will break the terminal in non-WoW64 environments. + if ((platformOverride === platform.Platform.Windows) && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && process.env.windir) { + const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase(); + if (executable && executable.toLowerCase().indexOf(sysnativePath) === 0) { + executable = path.join(process.env.windir, 'System32', executable.substr(sysnativePath.length)); + } + } + + // Convert / to \ on Windows for convenience + if (executable && platformOverride === platform.Platform.Windows) { + executable = executable.replace(/\//g, '\\'); + } + + return executable; +} + +function getDefaultShellArgs( + fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined }, + isWorkspaceShellAllowed: boolean, + defaultShell: string, + platformOverride: platform.Platform = platform.platform +): 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; + return args; +} + export function mergeDefaultShellPathAndArgs( shell: IShellLaunchConfig, fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined }, @@ -168,27 +208,8 @@ export function mergeDefaultShellPathAndArgs( defaultShell: string, platformOverride: platform.Platform = platform.platform ): void { - const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; - const shellConfigValue = fetchSetting(`terminal.integrated.shell.${platformKey}`); - const shellArgsConfigValue = fetchSetting(`terminal.integrated.shellArgs.${platformKey}`); - - shell.executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || (shellConfigValue.default || defaultShell); - shell.args = (isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default; - - // 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 - // exist and will break the terminal in non-WoW64 environments. - if ((platformOverride === platform.Platform.Windows) && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && process.env.windir) { - const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase(); - if (shell.executable && shell.executable.toLowerCase().indexOf(sysnativePath) === 0) { - shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length)); - } - } - - // Convert / to \ on Windows for convenience - if (shell.executable && platformOverride === platform.Platform.Windows) { - shell.executable = shell.executable.replace(/\//g, '\\'); - } + shell.executable = getDefaultShell(fetchSetting, isWorkspaceShellAllowed, defaultShell, platformOverride); + shell.args = getDefaultShellArgs(fetchSetting, isWorkspaceShellAllowed, defaultShell, platformOverride); } export function createTerminalEnvironment(