From 0bb42e1c67c93b1e4c49f55b5a471e590d27541a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 17 Jun 2019 10:00:04 -0700 Subject: [PATCH 1/6] Update remote node-pty --- remote/package.json | 8 ++++---- remote/yarn.lock | 17 ++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/remote/package.json b/remote/package.json index da17913e867..acf5412a080 100644 --- a/remote/package.json +++ b/remote/package.json @@ -12,7 +12,7 @@ "keytar": "4.2.1", "minimist": "1.2.0", "native-watchdog": "1.0.0", - "node-pty": "0.8.1", + "node-pty": "0.9.0-beta17", "onigasm-umd": "^2.2.2", "semver": "^5.5.0", "spdlog": "^0.9.0", @@ -21,11 +21,11 @@ "vscode-proxy-agent": "0.4.0", "vscode-ripgrep": "^1.2.5", "vscode-textmate": "^4.1.1", - "yauzl": "^2.9.2", - "yazl": "^2.4.3", "xterm": "3.15.0-beta34", "xterm-addon-search": "0.1.0-beta6", - "xterm-addon-web-links": "0.1.0-beta10" + "xterm-addon-web-links": "0.1.0-beta10", + "yauzl": "^2.9.2", + "yazl": "^2.4.3" }, "optionalDependencies": { "vscode-windows-ca-certs": "0.1.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index 11db8ed16fb..e5f73a8d3e6 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -599,11 +599,6 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -nan@2.12.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" - integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== - nan@2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" @@ -614,7 +609,7 @@ nan@^2.10.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" integrity sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw== -nan@^2.12.1, nan@^2.14.0: +nan@^2.12.1, nan@^2.13.2, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -636,12 +631,12 @@ node-addon-api@1.6.2: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.6.2.tgz#d8aad9781a5cfc4132cc2fecdbdd982534265217" integrity sha512-479Bjw9nTE5DdBSZZWprFryHGjUaQC31y1wHo19We/k0BZlrmhqQitWoUL0cD8+scljCbIUL+E58oRDEakdGGA== -node-pty@0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f" - integrity sha512-j+/g0Q5dR+vkELclpJpz32HcS3O/3EdPSGPvDXJZVJQLCvgG0toEbfmymxAEyQyZEpaoKHAcoL+PvKM+4N9nlw== +node-pty@0.9.0-beta17: + version "0.9.0-beta17" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0-beta17.tgz#9b490df86a8124dea595e9fbedeaaf4b2eedbbcb" + integrity sha512-E94XwIs3JxLKAboquHY9Kytbbj/T/tJtRpQoAUdfPE7UXRta/NV+xdmRNhZkeU9jCji+plm656GbYFievgNPkQ== dependencies: - nan "2.12.1" + nan "^2.13.2" noop-logger@^0.1.1: version "0.1.1" From a321476b277d6ce727e8a200b42ef7b4932be5de Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 17 Jun 2019 10:47:50 -0700 Subject: [PATCH 2/6] Disable shell selector on remote --- .../contrib/terminal/browser/terminal.contribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 2ab913b9cfc..dfefd249327 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -370,7 +370,8 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAct primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); -if (platform.isWindows) { +// TODO: Web should support this as well, the shell query needs to go to the ext host though +if (platform.isWindows && !platform.isWeb) { actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); } actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); From 7dfbccb8e213f93618206ae0799ee181b8464155 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 17 Jun 2019 10:48:03 -0700 Subject: [PATCH 3/6] Disable conpty on remote --- src/vs/workbench/api/node/extHostTerminalService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index f0dd94833f7..eadd6cbdf7d 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -534,7 +534,9 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Fork the process and listen for messages this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); - const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty') as boolean, this._logService); + // TODO: Support conpty on remote, it doesn't seem to work for some reason? + const enableConpty = false; //terminalConfig.get('windowsEnableConpty') as boolean; + const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, enableConpty, this._logService); p.onProcessReady((e: { pid: number, cwd: string }) => this._proxy.$sendProcessReady(id, e.pid, e.cwd)); p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title)); p.onProcessData(data => this._proxy.$sendProcessData(id, data)); From c287c3cfc77a92429ffaf497935ea1a18e0720a3 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 17 Jun 2019 11:53:58 -0700 Subject: [PATCH 4/6] Get default shell selector working --- remote/package.json | 3 +- remote/yarn.lock | 7 ++ .../api/browser/mainThreadTerminalService.ts | 59 +++++----- .../workbench/api/common/extHost.protocol.ts | 6 + .../api/node/extHostTerminalService.ts | 13 ++- .../terminal/browser/terminal.contribution.ts | 8 +- .../terminal/browser/terminalActions.ts | 11 +- .../terminal/browser/terminalNativeService.ts | 11 +- .../terminal/browser/terminalService.ts | 8 +- .../contrib/terminal/common/terminal.ts | 10 +- .../terminal/common/terminalService.ts | 33 +++++- .../electron-browser/terminalNativeService.ts | 103 +----------------- .../contrib/terminal/node/terminal.ts | 71 +++++++++++- 13 files changed, 192 insertions(+), 151 deletions(-) diff --git a/remote/package.json b/remote/package.json index acf5412a080..7a48c4cb238 100644 --- a/remote/package.json +++ b/remote/package.json @@ -28,6 +28,7 @@ "yazl": "^2.4.3" }, "optionalDependencies": { - "vscode-windows-ca-certs": "0.1.0" + "vscode-windows-ca-certs": "0.1.0", + "vscode-windows-registry": "1.0.1" } } diff --git a/remote/yarn.lock b/remote/yarn.lock index e5f73a8d3e6..6bf34b0726a 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -1112,6 +1112,13 @@ vscode-windows-ca-certs@0.1.0: dependencies: node-addon-api "1.6.2" +vscode-windows-registry@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vscode-windows-registry/-/vscode-windows-registry-1.0.1.tgz#bc9f765563eb6dc1c9ad9a41f9eaacc84dfadc7c" + integrity sha512-q0aKXi9Py1OBdmXIJJFeJBzpPJMMUxMJNBU9FysWIXEwJyMQGEVevKzM2J3Qz/cHSc5LVqibmoUWzZ7g+97qRg== + dependencies: + nan "^2.12.1" + which-pm-runs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index a68310bb3ee..2d9fba053ce 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { UriComponents, URI } from 'vs/base/common/uri'; @@ -22,11 +22,11 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape constructor( extHostContext: IExtHostContext, - @ITerminalService private readonly terminalService: ITerminalService + @ITerminalService private readonly _terminalService: ITerminalService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService); this._remoteAuthority = extHostContext.remoteAuthority; - this._toDispose.push(terminalService.onInstanceCreated((instance) => { + this._toDispose.push(_terminalService.onInstanceCreated((instance) => { // Delay this message so the TerminalInstance constructor has a chance to finish and // return the ID normally to the extension host. The ID that is passed here will be used // to register non-extension API terminals in the extension host. @@ -35,25 +35,26 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._onInstanceDimensionsChanged(instance); }, EXT_HOST_CREATION_DELAY); })); - this._toDispose.push(terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance))); - this._toDispose.push(terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance))); - this._toDispose.push(terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance))); - 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))); + this._toDispose.push(_terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance))); + this._toDispose.push(_terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance))); + this._toDispose.push(_terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance))); + 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))); + this._toDispose.push(_terminalService.onRequestWindowsShells(r => this._onRequestDetectWindowsShell(r))); // Set initial ext host state - this.terminalService.terminalInstances.forEach(t => { + this._terminalService.terminalInstances.forEach(t => { this._onTerminalOpened(t); t.processReady.then(() => this._onTerminalProcessIdReady(t)); }); - const activeInstance = this.terminalService.getActiveInstance(); + const activeInstance = this._terminalService.getActiveInstance(); if (activeInstance) { this._proxy.$acceptActiveTerminalChanged(activeInstance.id); } - this.terminalService.extHostReady(extHostContext.remoteAuthority); + this._terminalService.extHostReady(extHostContext.remoteAuthority); } public dispose(): void { @@ -75,7 +76,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape strictEnv, runInBackground }; - const terminal = this.terminalService.createTerminal(shellLaunchConfig); + const terminal = this._terminalService.createTerminal(shellLaunchConfig); return Promise.resolve({ id: terminal.id, name: terminal.title @@ -83,55 +84,55 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } public $createTerminalRenderer(name: string): Promise { - const instance = this.terminalService.createTerminalRenderer(name); + const instance = this._terminalService.createTerminalRenderer(name); return Promise.resolve(instance.id); } public $show(terminalId: number, preserveFocus: boolean): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (terminalInstance) { - this.terminalService.setActiveInstance(terminalInstance); - this.terminalService.showPanel(!preserveFocus); + this._terminalService.setActiveInstance(terminalInstance); + this._terminalService.showPanel(!preserveFocus); } } public $hide(terminalId: number): void { - const instance = this.terminalService.getActiveInstance(); + const instance = this._terminalService.getActiveInstance(); if (instance && instance.id === terminalId) { - this.terminalService.hidePanel(); + this._terminalService.hidePanel(); } } public $dispose(terminalId: number): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (terminalInstance) { terminalInstance.dispose(); } } public $terminalRendererWrite(terminalId: number, text: string): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) { terminalInstance.write(text); } } public $terminalRendererSetName(terminalId: number, name: string): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) { terminalInstance.setTitle(name, false); } } public $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) { terminalInstance.setDimensions(dimensions); } } public $terminalRendererRegisterOnInputListener(terminalId: number): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (!terminalInstance) { return; } @@ -147,14 +148,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } public $sendText(terminalId: number, text: string, addNewLine: boolean): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (terminalInstance) { terminalInstance.sendText(text, addNewLine); } } public $registerOnDataListener(terminalId: number): void { - const terminalInstance = this.terminalService.getInstanceFromId(terminalId); + const terminalInstance = this._terminalService.getInstanceFromId(terminalId); if (!terminalInstance) { return; } @@ -275,4 +276,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } this._terminalProcesses[terminalId].emitLatency(sum / COUNT); } + + private _onRequestDetectWindowsShell(resolve: (shells: IShellDefinition[]) => void): void { + this._proxy.$requestWindowsShells().then(shells => resolve(shells)); + } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index bb93e419568..d21659ce49c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1106,6 +1106,11 @@ export interface ShellLaunchConfigDto { env?: { [key: string]: string | null }; } +export interface IShellDefinitionDto { + label: string; + path: string; +} + export interface ExtHostTerminalServiceShape { $acceptTerminalClosed(id: number): void; $acceptTerminalOpened(id: number, name: string): void; @@ -1123,6 +1128,7 @@ export interface ExtHostTerminalServiceShape { $acceptProcessRequestCwd(id: number): void; $acceptProcessRequestLatency(id: number): number; $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; + $requestWindowsShells(): Promise; } export interface ExtHostSCMShape { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index eadd6cbdf7d..d2693538cc6 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -10,7 +10,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; 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 { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto, IShellDefinitionDto } from 'vs/workbench/api/common/extHost.protocol'; 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'; @@ -20,7 +20,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getDefaultShell, detectWindowsShells } from 'vs/workbench/contrib/terminal/node/terminal'; const RENDERER_NO_PROCESS_ID = -1; @@ -575,6 +575,15 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { return id; } + public $requestWindowsShells(): Promise { + console.log('$requestWindowsShells'); + if (!platform.isWindows) { + throw new Error('Can only detect Windows shells on Windows'); + } + console.log('$requestWindowsShells2'); + return detectWindowsShells(); + } + private _onProcessExit(id: number, exitCode: number): void { // Remove listeners this._terminalProcesses[id].dispose(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index dfefd249327..db127081d5d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -370,10 +370,10 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAct primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); -// TODO: Web should support this as well, the shell query needs to go to the ext host though -if (platform.isWindows && !platform.isWeb) { - actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); -} +// TODO: This should be the remote OS +// if (platform.isWindows) { +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); +// } actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 19d55b65081..199d6b2183b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper, ITerminalNativeService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -624,13 +624,13 @@ export class SelectDefaultShellWindowsTerminalAction extends Action { constructor( id: string, label: string, - @ITerminalNativeService private readonly _terminalNativeService: ITerminalNativeService + @ITerminalService private readonly _terminalService: ITerminalService ) { super(id, label); } public run(event?: any): Promise { - return this._terminalNativeService.selectDefaultWindowsShell(); + return this._terminalService.selectDefaultWindowsShell(); } } @@ -712,8 +712,7 @@ export class SwitchTerminalAction extends Action { constructor( id: string, label: string, - @ITerminalService private readonly terminalService: ITerminalService, - @ITerminalNativeService private readonly terminalNativeService: ITerminalNativeService + @ITerminalService private readonly terminalService: ITerminalService ) { super(id, label, 'terminal-action switch-terminal'); } @@ -728,7 +727,7 @@ export class SwitchTerminalAction extends Action { } if (item === SelectDefaultShellWindowsTerminalAction.LABEL) { this.terminalService.refreshActiveTab(); - return this.terminalNativeService.selectDefaultWindowsShell(); + return this.terminalService.selectDefaultWindowsShell(); } const selectedTabIndex = parseInt(item.split(':')[0], 10) - 1; this.terminalService.setActiveTabByIndex(selectedTabIndex); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts index 0e2849731fc..4d15ce7da31 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; -import { ITerminalNativeService, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalNativeService, LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; import { Emitter, Event } from 'vs/base/common/event'; export class TerminalNativeService implements ITerminalNativeService { @@ -23,10 +23,6 @@ export class TerminalNativeService implements ITerminalNativeService { throw new Error('Not implemented'); } - public selectDefaultWindowsShell(): Promise { - throw new Error('Not implemented'); - } - public getWslPath(): Promise { throw new Error('Not implemented'); } @@ -34,4 +30,9 @@ export class TerminalNativeService implements ITerminalNativeService { public getWindowsBuildNumber(): number { throw new Error('Not implemented'); } + + // TODO: Remove local impl + public detectWindowsShells(): Promise { + throw new Error('Not implemented'); + } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 00c2820793d..1e2017721bc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -21,6 +21,8 @@ import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class TerminalService extends CommonTerminalService implements ITerminalService { private _configHelper: IBrowserTerminalConfigHelper; @@ -39,9 +41,11 @@ export class TerminalService extends CommonTerminalService implements ITerminalS @IExtensionService extensionService: IExtensionService, @IFileService fileService: IFileService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @ITerminalNativeService readonly terminalNativeService: ITerminalNativeService + @ITerminalNativeService readonly terminalNativeService: ITerminalNativeService, + @IQuickInputService readonly quickInputService: IQuickInputService, + @IConfigurationService readonly configurationService: IConfigurationService ) { - super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService, remoteAgentService, terminalNativeService); + super(contextKeyService, panelService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService, remoteAgentService, terminalNativeService, quickInputService, configurationService); this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, this.terminalNativeService.linuxDistro); } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index c66a35797f0..7f7ba8436e1 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -222,6 +222,7 @@ export interface ITerminalService { onInstancesChanged: Event; onInstanceTitleChanged: Event; onActiveInstanceChanged: Event; + onRequestWindowsShells: Event<(shells: IShellDefinition[]) => void>; /** * Creates a terminal. @@ -267,6 +268,8 @@ export interface ITerminalService { findNext(): void; findPrevious(): void; + selectDefaultWindowsShell(): Promise; + setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; setWorkspaceShellAllowed(isAllowed: boolean): void; @@ -299,7 +302,12 @@ export interface ITerminalNativeService { getWindowsBuildNumber(): number; whenFileDeleted(path: URI): Promise; getWslPath(path: string): Promise; - selectDefaultWindowsShell(): Promise; + detectWindowsShells(): Promise; +} + +export interface IShellDefinition { + label: string; + path: string; } export const enum Direction { diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index c7da0cb4dbc..be79d920ce8 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalNativeService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalNativeService, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; @@ -22,6 +22,8 @@ import { basename } from 'vs/base/common/path'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { timeout } from 'vs/base/common/async'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; +import { IPickOptions, IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; export abstract class TerminalService implements ITerminalService { public _serviceBrand: any; @@ -63,6 +65,8 @@ export abstract class TerminalService implements ITerminalService { public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } protected readonly _onTabDisposed = new Emitter(); public get onTabDisposed(): Event { return this._onTabDisposed.event; } + protected readonly _onRequestWindowsShells = new Emitter<(shells: IShellDefinition[]) => void>(); + public get onRequestWindowsShells(): Event<(shells: IShellDefinition[]) => void> { return this._onRequestWindowsShells.event; } public abstract get configHelper(): ITerminalConfigHelper; @@ -76,7 +80,9 @@ export abstract class TerminalService implements ITerminalService { @IExtensionService private readonly _extensionService: IExtensionService, @IFileService protected readonly _fileService: IFileService, @IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService, - @ITerminalNativeService private readonly _terminalNativeService: ITerminalNativeService + @ITerminalNativeService private readonly _terminalNativeService: ITerminalNativeService, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @IConfigurationService private readonly _configurationService: IConfigurationService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -522,4 +528,27 @@ export abstract class TerminalService implements ITerminalService { c(escapeNonWindowsPath(originalPath)); }); } + + public selectDefaultWindowsShell(): Promise { + return this._detectWindowsShells().then(shells => { + const options: IPickOptions = { + placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") + }; + const quickPickItems = shells.map(s => { + return { label: s.label, description: s.path }; + }); + return this._quickInputService.pick(quickPickItems, options).then(async value => { + if (!value) { + return undefined; + } + const shell = value.description; + await this._configurationService.updateValue('terminal.integrated.shell.windows', shell, ConfigurationTarget.USER).then(() => shell); + return Promise.resolve(); + }); + }); + } + + private _detectWindowsShells(): Promise { + return new Promise(r => this._onRequestWindowsShells.fire(r)); + } } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts index 68b7462447a..4eba4ead961 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts @@ -3,19 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import { ipcRenderer as ipc } from 'electron'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; -import { ITerminalNativeService, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalNativeService, LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; -import { getWindowsBuildNumber, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; -import { IQuickPickItem, IPickOptions, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { getWindowsBuildNumber, linuxDistro, detectWindowsShells } from 'vs/workbench/contrib/terminal/node/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { execFile } from 'child_process'; -import { coalesce } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class TerminalNativeService implements ITerminalNativeService { @@ -30,8 +26,6 @@ export class TerminalNativeService implements ITerminalNativeService { constructor( @IFileService private readonly _fileService: IFileService, - @IQuickInputService private readonly _quickInputService: IQuickInputService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IInstantiationService readonly instantiationService: IInstantiationService, ) { ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => this._onOpenFileRequest.fire(request)); @@ -58,97 +52,8 @@ export class TerminalNativeService implements ITerminalNativeService { }); } - public selectDefaultWindowsShell(): Promise { - return this._detectWindowsShells().then(shells => { - const options: IPickOptions = { - placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") - }; - return this._quickInputService.pick(shells, options).then(value => { - if (!value) { - return undefined; - } - const shell = value.description; - return this._configurationService.updateValue('terminal.integrated.shell.windows', shell, ConfigurationTarget.USER).then(() => shell); - }); - }); - } - - /** - * Get the executable file path of shell from registry. - * @param shellName The shell name to get the executable file path - * @returns `[]` or `[ 'path' ]` - */ - private async _getShellPathFromRegistry(shellName: string): Promise { - const Registry = await import('vscode-windows-registry'); - - try { - const shellPath = Registry.GetStringRegKey('HKEY_LOCAL_MACHINE', `SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${shellName}.exe`, ''); - - if (shellPath === undefined) { - return []; - } - - return [shellPath]; - } catch (error) { - return []; - } - } - - private async _detectWindowsShells(): Promise { - // Determine the correct System32 path. We want to point to Sysnative - // when the 32-bit version of VS Code is running on a 64-bit machine. - // The reason for this is because PowerShell's important PSReadline - // module doesn't work if this is not the case. See #27915. - const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); - const system32Path = `${process.env['windir']}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}`; - - let useWSLexe = false; - - if (getWindowsBuildNumber() >= 16299) { - useWSLexe = true; - } - - const expectedLocations = { - 'Command Prompt': [`${system32Path}\\cmd.exe`], - PowerShell: [`${system32Path}\\WindowsPowerShell\\v1.0\\powershell.exe`], - 'PowerShell Core': await this._getShellPathFromRegistry('pwsh'), - 'WSL Bash': [`${system32Path}\\${useWSLexe ? 'wsl.exe' : 'bash.exe'}`], - 'Git Bash': [ - `${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`, - `${process.env['ProgramW6432']}\\Git\\usr\\bin\\bash.exe`, - `${process.env['ProgramFiles']}\\Git\\bin\\bash.exe`, - `${process.env['ProgramFiles']}\\Git\\usr\\bin\\bash.exe`, - `${process.env['LocalAppData']}\\Programs\\Git\\bin\\bash.exe`, - ] - }; - const promises: PromiseLike<[string, string]>[] = []; - Object.keys(expectedLocations).forEach(key => promises.push(this._validateShellPaths(key, expectedLocations[key]))); - return Promise.all(promises) - .then(coalesce) - .then(results => { - return results.map(result => { - return { - label: result[0], - description: result[1] - }; - }); - }); - } - - private _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { - if (potentialPaths.length === 0) { - return Promise.resolve(null); - } - const current = potentialPaths.shift(); - if (current! === '') { - return this._validateShellPaths(label, potentialPaths); - } - return this._fileService.exists(URI.file(current!)).then(exists => { - if (!exists) { - return this._validateShellPaths(label, potentialPaths); - } - return [label, current] as [string, string]; - }); + public detectWindowsShells(): Promise { + return detectWindowsShells(); } /** diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 493fad0b833..b1610199f3d 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -6,8 +6,10 @@ import * as os from 'os'; import * as platform from 'vs/base/common/platform'; import * as processes from 'vs/base/node/processes'; -import { readFile, fileExists } from 'vs/base/node/pfs'; -import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { readFile, fileExists, stat } from 'vs/base/node/pfs'; +import { LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; +import { coalesce } from 'vs/base/common/arrays'; +import { normalize } from 'vs/base/common/path'; export function getDefaultShell(p: platform.Platform): string { if (p === platform.Platform.Windows) { @@ -82,3 +84,68 @@ export function getWindowsBuildNumber(): number { } return buildNumber; } + +export async function detectWindowsShells(): Promise { + console.log('a'); + // Determine the correct System32 path. We want to point to Sysnative + // when the 32-bit version of VS Code is running on a 64-bit machine. + // The reason for this is because PowerShell's important PSReadline + // module doesn't work if this is not the case. See #27915. + const is32ProcessOn64Windows = process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); + const system32Path = `${process.env['windir']}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}`; + + let useWSLexe = false; + + if (getWindowsBuildNumber() >= 16299) { + useWSLexe = true; + } + + const expectedLocations = { + 'Command Prompt': [`${system32Path}\\cmd.exe`], + PowerShell: [`${system32Path}\\WindowsPowerShell\\v1.0\\powershell.exe`], + 'PowerShell Core': [await getShellPathFromRegistry('pwsh')], + 'WSL Bash': [`${system32Path}\\${useWSLexe ? 'wsl.exe' : 'bash.exe'}`], + 'Git Bash': [ + `${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`, + `${process.env['ProgramW6432']}\\Git\\usr\\bin\\bash.exe`, + `${process.env['ProgramFiles']}\\Git\\bin\\bash.exe`, + `${process.env['ProgramFiles']}\\Git\\usr\\bin\\bash.exe`, + `${process.env['LocalAppData']}\\Programs\\Git\\bin\\bash.exe`, + ] + }; + const promises: PromiseLike[] = []; + Object.keys(expectedLocations).forEach(key => promises.push(validateShellPaths(key, expectedLocations[key]))); + + console.log('$detectWindowsShells'); + return Promise.all(promises).then(coalesce); +} + + +function validateShellPaths(label: string, potentialPaths: string[]): Promise { + if (potentialPaths.length === 0) { + return Promise.resolve(undefined); + } + const current = potentialPaths.shift()!; + if (current! === '') { + return validateShellPaths(label, potentialPaths); + } + return stat(normalize(current)).then(stat => { + if (!stat.isFile && !stat.isSymbolicLink) { + return validateShellPaths(label, potentialPaths); + } + return { + label, + path: current + }; + }); +} + +async function getShellPathFromRegistry(shellName: string): Promise { + const Registry = await import('vscode-windows-registry'); + try { + const shellPath = Registry.GetStringRegKey('HKEY_LOCAL_MACHINE', `SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${shellName}.exe`, ''); + return shellPath ? shellPath : ''; + } catch (error) { + return ''; + } +} \ No newline at end of file From d0820c2e0674c457e5eb318681e8452f645e1cfe Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 17 Jun 2019 11:54:34 -0700 Subject: [PATCH 5/6] Disable old shell detection mechanism --- src/vs/workbench/api/node/extHostTerminalService.ts | 2 -- .../contrib/terminal/browser/terminalNativeService.ts | 7 +------ src/vs/workbench/contrib/terminal/common/terminal.ts | 1 - .../terminal/electron-browser/terminalNativeService.ts | 8 ++------ src/vs/workbench/contrib/terminal/node/terminal.ts | 2 -- 5 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index d2693538cc6..6274c89b139 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -576,11 +576,9 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { } public $requestWindowsShells(): Promise { - console.log('$requestWindowsShells'); if (!platform.isWindows) { throw new Error('Can only detect Windows shells on Windows'); } - console.log('$requestWindowsShells2'); return detectWindowsShells(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts index 4d15ce7da31..2ed3246317c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalNativeService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; -import { ITerminalNativeService, LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalNativeService, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import { Emitter, Event } from 'vs/base/common/event'; export class TerminalNativeService implements ITerminalNativeService { @@ -30,9 +30,4 @@ export class TerminalNativeService implements ITerminalNativeService { public getWindowsBuildNumber(): number { throw new Error('Not implemented'); } - - // TODO: Remove local impl - public detectWindowsShells(): Promise { - throw new Error('Not implemented'); - } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 7f7ba8436e1..205ce54cebc 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -302,7 +302,6 @@ export interface ITerminalNativeService { getWindowsBuildNumber(): number; whenFileDeleted(path: URI): Promise; getWslPath(path: string): Promise; - detectWindowsShells(): Promise; } export interface IShellDefinition { diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts index 4eba4ead961..423284f97a5 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalNativeService.ts @@ -5,10 +5,10 @@ import { ipcRenderer as ipc } from 'electron'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; -import { ITerminalNativeService, LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalNativeService, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; -import { getWindowsBuildNumber, linuxDistro, detectWindowsShells } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getWindowsBuildNumber, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { execFile } from 'child_process'; import { Emitter, Event } from 'vs/base/common/event'; @@ -52,10 +52,6 @@ export class TerminalNativeService implements ITerminalNativeService { }); } - public detectWindowsShells(): Promise { - return detectWindowsShells(); - } - /** * Converts a path to a path on WSL using the wslpath utility. * @param path The original path. diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index b1610199f3d..979a359e8b5 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -86,7 +86,6 @@ export function getWindowsBuildNumber(): number { } export async function detectWindowsShells(): Promise { - console.log('a'); // Determine the correct System32 path. We want to point to Sysnative // when the 32-bit version of VS Code is running on a 64-bit machine. // The reason for this is because PowerShell's important PSReadline @@ -116,7 +115,6 @@ export async function detectWindowsShells(): Promise { const promises: PromiseLike[] = []; Object.keys(expectedLocations).forEach(key => promises.push(validateShellPaths(key, expectedLocations[key]))); - console.log('$detectWindowsShells'); return Promise.all(promises).then(coalesce); } From 331da82ad57e11b87f625bf52c83c2109bdeea4a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 18 Jun 2019 11:28:30 -0700 Subject: [PATCH 6/6] Enable shell selector outside Windows Fixes #75718 --- .../api/browser/mainThreadTerminalService.ts | 6 +++--- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/node/extHostTerminalService.ts | 9 +++------ .../terminal/browser/terminal.contribution.ts | 3 --- .../terminal/browser/terminalActions.ts | 11 +++-------- .../contrib/terminal/common/terminal.ts | 2 +- .../contrib/terminal/common/terminalService.ts | 6 +++--- .../contrib/terminal/node/terminal.ts | 18 ++++++++++++++++-- 8 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 2d9fba053ce..c37b7c685f0 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -42,7 +42,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape 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))); - this._toDispose.push(_terminalService.onRequestWindowsShells(r => this._onRequestDetectWindowsShell(r))); + this._toDispose.push(_terminalService.onRequestAvailableShells(r => this._onRequestAvailableShells(r))); // Set initial ext host state this._terminalService.terminalInstances.forEach(t => { @@ -277,7 +277,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._terminalProcesses[terminalId].emitLatency(sum / COUNT); } - private _onRequestDetectWindowsShell(resolve: (shells: IShellDefinition[]) => void): void { - this._proxy.$requestWindowsShells().then(shells => resolve(shells)); + private _onRequestAvailableShells(resolve: (shells: IShellDefinition[]) => void): void { + this._proxy.$requestAvailableShells().then(shells => resolve(shells)); } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d21659ce49c..bcd55cea583 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1128,7 +1128,7 @@ export interface ExtHostTerminalServiceShape { $acceptProcessRequestCwd(id: number): void; $acceptProcessRequestLatency(id: number): number; $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; - $requestWindowsShells(): Promise; + $requestAvailableShells(): Promise; } export interface ExtHostSCMShape { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 6274c89b139..878127edcc5 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -20,7 +20,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { getDefaultShell, detectWindowsShells } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getDefaultShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal'; const RENDERER_NO_PROCESS_ID = -1; @@ -575,11 +575,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { return id; } - public $requestWindowsShells(): Promise { - if (!platform.isWindows) { - throw new Error('Can only detect Windows shells on Windows'); - } - return detectWindowsShells(); + public $requestAvailableShells(): Promise { + return detectAvailableShells(); } private _onProcessExit(id: number, exitCode: number): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index db127081d5d..8a62400e086 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -370,10 +370,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAct primary: 0, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); -// TODO: This should be the remote OS -// if (platform.isWindows) { actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); -// } actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(AllowWorkspaceShellTerminalCommand, AllowWorkspaceShellTerminalCommand.ID, AllowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Allow Workspace Shell Configuration', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DisallowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand.ID, DisallowWorkspaceShellTerminalCommand.LABEL), 'Terminal: Disallow Workspace Shell Configuration', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RenameTerminalAction, RenameTerminalAction.ID, RenameTerminalAction.LABEL), 'Terminal: Rename', category); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 199d6b2183b..0e01b387a47 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -35,7 +35,6 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export const TERMINAL_PICKER_PREFIX = 'term '; @@ -743,8 +742,7 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { action: IAction, @ITerminalService private readonly terminalService: ITerminalService, @IThemeService themeService: IThemeService, - @IContextViewService contextViewService: IContextViewService, - @IWorkbenchEnvironmentService private workbenchEnvironmentService: IWorkbenchEnvironmentService + @IContextViewService contextViewService: IContextViewService ) { super(null, action, terminalService.getTabLabels().map(label => { text: label }), terminalService.activeTabIndex, contextViewService, { ariaLabel: nls.localize('terminals', 'Open Terminals.') }); @@ -756,11 +754,8 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { private _updateItems(): void { const items = this.terminalService.getTabLabels().map(label => { text: label }); - let enableSelectDefaultShell = this.workbenchEnvironmentService.configuration.remoteAuthority ? false : isWindows; - if (enableSelectDefaultShell) { - items.push({ text: SwitchTerminalActionViewItem.SEPARATOR }); - items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL }); - } + items.push({ text: SwitchTerminalActionViewItem.SEPARATOR }); + items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL }); this.setOptions(items, this.terminalService.activeTabIndex); } } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 614d4de28d9..be1202ac526 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -222,7 +222,7 @@ export interface ITerminalService { onInstancesChanged: Event; onInstanceTitleChanged: Event; onActiveInstanceChanged: Event; - onRequestWindowsShells: Event<(shells: IShellDefinition[]) => void>; + onRequestAvailableShells: Event<(shells: IShellDefinition[]) => void>; /** * Creates a terminal. diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 6af9cfc7bb3..2dd2c47180c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -65,8 +65,8 @@ export abstract class TerminalService implements ITerminalService { public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } protected readonly _onTabDisposed = new Emitter(); public get onTabDisposed(): Event { return this._onTabDisposed.event; } - protected readonly _onRequestWindowsShells = new Emitter<(shells: IShellDefinition[]) => void>(); - public get onRequestWindowsShells(): Event<(shells: IShellDefinition[]) => void> { return this._onRequestWindowsShells.event; } + protected readonly _onRequestAvailableShells = new Emitter<(shells: IShellDefinition[]) => void>(); + public get onRequestAvailableShells(): Event<(shells: IShellDefinition[]) => void> { return this._onRequestAvailableShells.event; } public abstract get configHelper(): ITerminalConfigHelper; @@ -553,6 +553,6 @@ export abstract class TerminalService implements ITerminalService { } private _detectWindowsShells(): Promise { - return new Promise(r => this._onRequestWindowsShells.fire(r)); + return new Promise(r => this._onRequestAvailableShells.fire(r)); } } diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 979a359e8b5..195879eb52f 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -9,7 +9,7 @@ import * as processes from 'vs/base/node/processes'; import { readFile, fileExists, stat } from 'vs/base/node/pfs'; import { LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; import { coalesce } from 'vs/base/common/arrays'; -import { normalize } from 'vs/base/common/path'; +import { normalize, basename } from 'vs/base/common/path'; export function getDefaultShell(p: platform.Platform): string { if (p === platform.Platform.Windows) { @@ -85,7 +85,11 @@ export function getWindowsBuildNumber(): number { return buildNumber; } -export async function detectWindowsShells(): Promise { +export function detectAvailableShells(): Promise { + return platform.isWindows ? detectAvailableWindowsShells() : detectAvailableUnixShells(); +} + +async function detectAvailableWindowsShells(): Promise { // Determine the correct System32 path. We want to point to Sysnative // when the 32-bit version of VS Code is running on a 64-bit machine. // The reason for this is because PowerShell's important PSReadline @@ -118,6 +122,16 @@ export async function detectWindowsShells(): Promise { return Promise.all(promises).then(coalesce); } +async function detectAvailableUnixShells(): Promise { + const contents = await readFile('/etc/shells', 'utf8'); + const shells = contents.split('\n').filter(e => e.trim().indexOf('#') !== 0 && e.trim().length > 0); + return shells.map(e => { + return { + label: basename(e), + path: e + }; + }); +} function validateShellPaths(label: string, potentialPaths: string[]): Promise { if (potentialPaths.length === 0) {