From 83ba7099ffaaa36b68e320a8b184afe5ee483a46 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 13 Jun 2019 15:13:31 -0700 Subject: [PATCH 1/2] Default shell API Part of #75091 --- src/vs/vscode.proposed.d.ts | 6 +++ src/vs/workbench/api/node/extHost.api.impl.ts | 3 ++ .../api/node/extHostTerminalService.ts | 15 ++++++- .../terminal/common/terminalEnvironment.ts | 43 ++++++++++++++----- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 317a06d6a5b98..1f8752a3f71ff 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 dc6f2cd2e1652..0edd04ad5b85c 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 2aad0a4c38f7d..ec4d4771989dc 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 0e49495815a3f..4c850cc9831c5 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -161,34 +161,55 @@ export function escapeNonWindowsPath(path: string): string { return newPath; } -export function mergeDefaultShellPathAndArgs( - shell: IShellLaunchConfig, +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 -): void { +): string { 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; + 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 (shell.executable && shell.executable.toLowerCase().indexOf(sysnativePath) === 0) { - shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length)); + 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 (shell.executable && platformOverride === platform.Platform.Windows) { - shell.executable = shell.executable.replace(/\//g, '\\'); + 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 }, + isWorkspaceShellAllowed: boolean, + defaultShell: string, + platformOverride: platform.Platform = platform.platform +): void { + shell.executable = getDefaultShell(fetchSetting, isWorkspaceShellAllowed, defaultShell, platformOverride); + shell.args = getDefaultShellArgs(fetchSetting, isWorkspaceShellAllowed, defaultShell, platformOverride); } export function createTerminalEnvironment( From 976320ddd2046535d4b364834e20eaa9adaf0d31 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 13 Jun 2019 15:41:00 -0700 Subject: [PATCH 2/2] Pass workspace shell permissions to ext host --- src/vs/workbench/api/browser/mainThreadTerminalService.ts | 5 +++++ src/vs/workbench/api/common/extHost.protocol.ts | 1 + src/vs/workbench/api/node/extHostTerminalService.ts | 4 ++++ .../contrib/terminal/browser/terminalConfigHelper.ts | 5 +++++ src/vs/workbench/contrib/terminal/common/terminal.ts | 8 ++++++-- 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index bec1d0f39d5a9..3dcabc3718b75 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -41,6 +41,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request))); this._toDispose.push(terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null))); this._toDispose.push(terminalService.onInstanceTitleChanged(instance => this._onTitleChanged(instance.id, instance.title))); + this._toDispose.push(terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed))); // Set initial ext host state this.terminalService.terminalInstances.forEach(t => { @@ -182,6 +183,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._proxy.$acceptTerminalTitleChange(terminalId, name); } + private _onWorkspacePermissionsChanged(isAllowed: boolean): void { + this._proxy.$acceptWorkspacePermissionsChanged(isAllowed); + } + private _onTerminalRendererInput(terminalId: number, data: string): void { this._proxy.$acceptTerminalRendererInput(terminalId, data); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ae81362612060..3e033e28d8c54 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1107,6 +1107,7 @@ export interface ExtHostTerminalServiceShape { $acceptProcessRequestInitialCwd(id: number): void; $acceptProcessRequestCwd(id: number): void; $acceptProcessRequestLatency(id: number): number; + $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; } export interface ExtHostSCMShape { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index ec4d4771989dc..f5d85a2f68e28 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -625,6 +625,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { }); return index; } + + public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void { + this._isWorkspaceShellAllowed = isAllowed; + } } class ApiRequest { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 3d336e6e28666..965f9de0e44db 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -14,6 +14,7 @@ import { Terminal as XTermTerminal } from 'xterm'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; import { mergeDefaultShellPathAndArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { Emitter, Event } from 'vs/base/common/event'; const MINIMUM_FONT_SIZE = 6; const MAXIMUM_FONT_SIZE = 25; @@ -29,6 +30,9 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { private _lastFontMeasurement: ITerminalFont; public config: ITerminalConfiguration; + private readonly _onWorkspacePermissionsChanged = new Emitter(); + public get onWorkspacePermissionsChanged(): Event { return this._onWorkspacePermissionsChanged.event; } + public constructor( private readonly _linuxDistro: LinuxDistro, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -160,6 +164,7 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { } public setWorkspaceShellAllowed(isAllowed: boolean): void { + this._onWorkspacePermissionsChanged.fire(isAllowed); this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index f1091ed5d1c7e..e700aef3e1b34 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -109,6 +109,9 @@ export interface ITerminalConfiguration { export interface ITerminalConfigHelper { config: ITerminalConfiguration; + + onWorkspacePermissionsChanged: Event; + configFontIsMonospace(): boolean; getFont(): ITerminalFont; /** @@ -210,6 +213,9 @@ export interface ITerminalService { activeTabIndex: number; configHelper: ITerminalConfigHelper; + terminalInstances: ITerminalInstance[]; + terminalTabs: ITerminalTab[]; + onActiveTabChanged: Event; onTabDisposed: Event; onInstanceCreated: Event; @@ -220,8 +226,6 @@ export interface ITerminalService { onInstancesChanged: Event; onInstanceTitleChanged: Event; onActiveInstanceChanged: Event; - terminalInstances: ITerminalInstance[]; - terminalTabs: ITerminalTab[]; /** * Creates a terminal.