From fe169ce4d28fde2a8f71c2d8600c4780b6457e46 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 16:46:45 -0800 Subject: [PATCH 01/20] Move terminalLinkHandler, electron-browser->browser Part of #69115 --- src/tsconfig.strictNullChecks.json | 2 +- .../terminalLinkHandler.ts | 58 +++++++++++++++++-- .../electron-browser/terminalInstance.ts | 2 +- .../terminalLinkHandler.test.ts | 16 ++--- 4 files changed, 62 insertions(+), 16 deletions(-) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/terminalLinkHandler.ts (87%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 7802f41674b94..c6deb24a509b7 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -279,6 +279,7 @@ "./vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts", "./vs/workbench/contrib/terminal/browser/terminalFindWidget.ts", + "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", "./vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts", "./vs/workbench/contrib/terminal/browser/terminalTab.ts", "./vs/workbench/contrib/terminal/browser/terminalWidgetManager.ts", @@ -290,7 +291,6 @@ "./vs/workbench/contrib/terminal/electron-browser/terminalActions.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts similarity index 87% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts rename to src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index 734d13fd45aec..d0c1214879c6c 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -6,8 +6,7 @@ import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager'; @@ -15,7 +14,53 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ILinkMatcherOptions } from 'vscode-xterm'; +import { IFileService } from 'vs/platform/files/common/files'; + +// Since importing from vscode-xterm would be a layer breakage here the type is copied inline +// import { ILinkMatcherOptions } from 'vscode-xterm'; + +/** + * An object containing options for a link matcher. + */ +export interface ILinkMatcherOptions { + /** + * The index of the link from the regex.match(text) call. This defaults to 0 + * (for regular expressions without capture groups). + */ + matchIndex?: number; + + /** + * A callback that validates whether to create an individual link, pass + * whether the link is valid to the callback. + */ + validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; + + /** + * A callback that fires when the mouse hovers over a link for a moment. + */ + tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; + + /** + * A callback that fires when the mouse leaves a link. Note that this can + * happen even when tooltipCallback hasn't fired for the link yet. + */ + leaveCallback?: (event: MouseEvent, uri: string) => boolean | void; + + /** + * The priority of the link matcher, this defines the order in which the link + * matcher is evaluated relative to others, from highest to lowest. The + * default value is 0. + */ + priority?: number; + + /** + * A callback that fires when the mousedown and click events occur that + * determines whether a link will be activated upon click. This enables + * only activating a link when a certain modifier is held down, if not the + * mouse event will continue propagation (eg. double click to select word). + */ + willLinkActivate?: (event: MouseEvent, uri: string) => boolean; +} const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; @@ -74,6 +119,7 @@ export class TerminalLinkHandler { @IEditorService private readonly _editorService: IEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService, @ITerminalService private readonly _terminalService: ITerminalService, + @IFileService private readonly _fileService: IFileService ) { const baseLocalLinkClause = _platform === platform.Platform.Windows ? winLocalLinkClause : unixLocalLinkClause; // Append line and column number regex @@ -203,7 +249,7 @@ export class TerminalLinkHandler { if (!normalizedUrl) { return Promise.resolve(null); } - const resource = Uri.file(normalizedUrl); + const resource = URI.file(normalizedUrl); const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link); const selection: ITextEditorSelection = { startLineNumber: lineColumnInfo.lineNumber, @@ -223,7 +269,7 @@ export class TerminalLinkHandler { } private _handleHypertextLink(url: string): void { - const uri = Uri.parse(url); + const uri = URI.parse(url); this._openerService.open(uri); } @@ -288,7 +334,7 @@ export class TerminalLinkHandler { } // Ensure the file exists on disk, so an editor can be opened after clicking it - return pfs.fileExists(linkUrl).then(isFile => { + return this._fileService.existsFile(URI.file(linkUrl)).then(isFile => { if (!isFile) { return null; } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts index f3764101c3a64..22f9378da7b2d 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts @@ -31,7 +31,7 @@ import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalPr import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; -import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler'; +import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/electron-browser/terminalProcessManager'; import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/node/terminalCommandTracker'; import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts index 99edaab467177..5b708d68123ac 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Platform } from 'vs/base/common/platform'; -import { TerminalLinkHandler, LineColumnInfo } from 'vs/workbench/contrib/terminal/electron-browser/terminalLinkHandler'; +import { TerminalLinkHandler, LineColumnInfo } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import * as strings from 'vs/base/common/strings'; import * as path from 'vs/base/common/path'; import * as sinon from 'sinon'; @@ -39,7 +39,7 @@ interface LinkFormatInfo { suite('Workbench - TerminalLinkHandler', () => { suite('localLinkRegex', () => { test('Windows', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -111,7 +111,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -175,7 +175,7 @@ suite('Workbench - TerminalLinkHandler', () => { suite('preprocessPath', () => { test('Windows', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base'; let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { @@ -188,7 +188,7 @@ suite('Workbench - TerminalLinkHandler', () => { stub.restore(); }); test('Windows - spaces', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Windows, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base dir'; let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { @@ -202,7 +202,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('Linux', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); linkHandler.processCwd = '/base'; let stub = sinon.stub(path, 'join', function (arg1: string, arg2: string) { @@ -216,7 +216,7 @@ suite('Workbench - TerminalLinkHandler', () => { }); test('No Workspace', () => { - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); assert.equal(linkHandler.preprocessPath('./src/file1'), null); assert.equal(linkHandler.preprocessPath('src/file2'), null); @@ -226,7 +226,7 @@ suite('Workbench - TerminalLinkHandler', () => { test('gitDiffLinkRegex', () => { // The platform is irrelevant because the links generated by Git are the same format regardless of platform - const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!, null!); function assertAreGoodMatches(matches: RegExpMatchArray | null) { if (matches) { From 5d2a154da6e3c0f0e019b91b0e02e17beac74995 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 16:56:26 -0800 Subject: [PATCH 02/20] Add terminal-specific tslint rule to allow vscode-xterm imports --- .../terminal/browser/terminalLinkHandler.ts | 47 +------------------ tslint.json | 7 +++ 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index d0c1214879c6c..0c7246a703828 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -15,52 +15,7 @@ import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal' import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IFileService } from 'vs/platform/files/common/files'; - -// Since importing from vscode-xterm would be a layer breakage here the type is copied inline -// import { ILinkMatcherOptions } from 'vscode-xterm'; - -/** - * An object containing options for a link matcher. - */ -export interface ILinkMatcherOptions { - /** - * The index of the link from the regex.match(text) call. This defaults to 0 - * (for regular expressions without capture groups). - */ - matchIndex?: number; - - /** - * A callback that validates whether to create an individual link, pass - * whether the link is valid to the callback. - */ - validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; - - /** - * A callback that fires when the mouse hovers over a link for a moment. - */ - tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; - - /** - * A callback that fires when the mouse leaves a link. Note that this can - * happen even when tooltipCallback hasn't fired for the link yet. - */ - leaveCallback?: (event: MouseEvent, uri: string) => boolean | void; - - /** - * The priority of the link matcher, this defines the order in which the link - * matcher is evaluated relative to others, from highest to lowest. The - * default value is 0. - */ - priority?: number; - - /** - * A callback that fires when the mousedown and click events occur that - * determines whether a link will be activated upon click. This enables - * only activating a link when a certain modifier is held down, if not the - * mouse event will continue propagation (eg. double click to select word). - */ - willLinkActivate?: (event: MouseEvent, uri: string) => boolean; -} +import { ILinkMatcherOptions } from 'vscode-xterm'; const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; diff --git a/tslint.json b/tslint.json index 0383001a61bac..5365738399ac2 100644 --- a/tslint.json +++ b/tslint.json @@ -485,6 +485,13 @@ "assert" ] }, + { + "target": "**/vs/workbench/contrib/terminal/browser/**", + "restrictions": [ + "vscode-xterm", + "**/vs/**" + ] + }, { "target": "**/vs/code/node/**", "restrictions": [ From 8afa7773d5237bcfd8602d6afb2f5a92c6c21b57 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 17:16:21 -0800 Subject: [PATCH 03/20] Remove os dep in terminalEnvironment --- .../api/node/extHostTerminalService.ts | 3 ++- .../terminal/browser/terminalService.ts | 25 +++++++++++++++++++ .../terminal/common/terminalService.ts | 9 +++++-- .../terminalProcessManager.ts | 4 ++- .../electron-browser/terminalService.ts | 13 ++-------- .../terminal/node/terminalEnvironment.ts | 5 ++-- .../test/node/terminalEnvironment.test.ts | 23 ++++++++--------- 7 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalService.ts diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 4e10c68feb21c..156d7ad5c821b 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as os from 'os'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; @@ -429,7 +430,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // TODO: @daniel const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents); - const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, terminalConfig.cwd); + const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), activeWorkspaceRootUri, terminalConfig.cwd); // TODO: Pull in and resolve config settings // // Resolve env vars from config and shell diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts new file mode 100644 index 0000000000000..060ce2863aaea --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalService as CommonTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IStorageService } from 'vs/platform/storage/common/storage'; + +export abstract class TerminalService extends CommonTerminalService implements ITerminalService { + + constructor( + @IContextKeyService contextKeyService: IContextKeyService, + @IPanelService panelService: IPanelService, + @IPartService partService: IPartService, + @ILifecycleService lifecycleService: ILifecycleService, + @IStorageService storageService: IStorageService + ) { + super(contextKeyService, panelService, partService, lifecycleService, storageService); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 2af5a375481e6..ab3e50b188583 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -21,7 +21,9 @@ export abstract class TerminalService implements ITerminalService { protected _findWidgetVisible: IContextKey; protected _terminalContainer: HTMLElement; protected _terminalTabs: ITerminalTab[]; - protected abstract _terminalInstances: ITerminalInstance[]; + protected get _terminalInstances(): ITerminalInstance[] { + return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); + } private _findState: FindReplaceState; private _activeTabIndex: number; @@ -89,13 +91,16 @@ export abstract class TerminalService implements ITerminalService { protected abstract _showTerminalCloseConfirmation(): Promise; protected abstract _showNotEnoughSpaceToast(): void; public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; - public abstract createTerminalRenderer(name: string): ITerminalInstance; public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; public abstract selectDefaultWindowsShell(): Promise; public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; + public createTerminalRenderer(name: string): ITerminalInstance { + return this.createTerminal({ name, isRendererOnly: true }); + } + private _onBeforeShutdown(): boolean | Promise { if (this.terminalInstances.length === 0) { // No terminal instances, don't veto diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts index a3fa9af9e3dab..786c7f7f2f880 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts @@ -21,6 +21,7 @@ import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -61,6 +62,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, @IWindowService private readonly _windowService: IWindowService, @IWorkspaceConfigurationService private readonly _workspaceConfigurationService: IWorkspaceConfigurationService, + @IEnvironmentService private readonly _environmentSertvice: IEnvironmentService ) { this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { @@ -110,7 +112,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { } const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); - const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, this._configHelper.config.cwd); + const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentSertvice.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd); // Compel type system as process.env should not have any undefined entries let env: platform.IProcessEnvironment = {}; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index fbace8ec6fb82..f916502d8f545 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -13,7 +13,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, TERMINAL_PANEL_ID, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; -import { TerminalService as AbstractTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; +import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; import Severity from 'vs/base/common/severity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -30,15 +30,10 @@ import { URI } from 'vs/base/common/uri'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; import { coalesce } from 'vs/base/common/arrays'; -export class TerminalService extends AbstractTerminalService implements ITerminalService { +export class TerminalService extends BrowserTerminalService implements ITerminalService { private _configHelper: TerminalConfigHelper; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } - protected _terminalTabs: TerminalTab[]; - protected get _terminalInstances(): ITerminalInstance[] { - return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); - } - constructor( @IContextKeyService contextKeyService: IContextKeyService, @IPanelService panelService: IPanelService, @@ -98,10 +93,6 @@ export class TerminalService extends AbstractTerminalService implements ITermina return instance; } - public createTerminalRenderer(name: string): ITerminalInstance { - return this.createTerminal({ name, isRendererOnly: true }); - } - public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); this._onInstanceCreated.fire(instance); diff --git a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts index 612ea746d213f..668ec2ef2acbf 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; import pkg from 'vs/platform/product/node/package'; @@ -102,7 +101,7 @@ function _getLangEnvVariable(locale?: string) { return parts.join('_') + '.UTF-8'; } -export function getCwd(shell: IShellLaunchConfig, root?: Uri, customCwd?: string): string { +export function getCwd(shell: IShellLaunchConfig, userHome: string, root?: Uri, customCwd?: string): string { if (shell.cwd) { return (typeof shell.cwd === 'object') ? shell.cwd.fsPath : shell.cwd; } @@ -120,7 +119,7 @@ export function getCwd(shell: IShellLaunchConfig, root?: Uri, customCwd?: string // If there was no custom cwd or it was relative with no workspace if (!cwd) { - cwd = root ? root.fsPath : os.homedir(); + cwd = root ? root.fsPath : userHome; } return _sanitizeCwd(cwd); diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts index 0f9ecc1299a99..1549d0b2e5677 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import * as os from 'os'; import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; import { URI as Uri } from 'vs/base/common/uri'; @@ -101,32 +100,32 @@ suite('Workbench - TerminalEnvironment', () => { assert.equal(Uri.file(a).fsPath, Uri.file(b).fsPath); } - test('should default to os.homedir() for an empty workspace', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, undefined), os.homedir()); + test('should default to userHome for an empty workspace', () => { + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, undefined), '/userHome/'); }); test('should use to the workspace if it exists', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/foo'), undefined), '/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/foo'), undefined), '/foo'); }); test('should use an absolute custom cwd as is', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, '/foo'), '/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, '/foo'), '/foo'); }); test('should normalize a relative custom cwd against the workspace path', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/bar'), 'foo'), '/bar/foo'); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/bar'), './foo'), '/bar/foo'); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, Uri.file('/bar'), '../foo'), '/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/bar'), 'foo'), '/bar/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/bar'), './foo'), '/bar/foo'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', Uri.file('/bar'), '../foo'), '/foo'); }); test('should fall back for relative a custom cwd that doesn\'t have a workspace', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, 'foo'), os.homedir()); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, './foo'), os.homedir()); - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, undefined, '../foo'), os.homedir()); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, 'foo'), '/userHome/'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, './foo'), '/userHome/'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [] }, '/userHome/', undefined, '../foo'), '/userHome/'); }); test('should ignore custom cwd when told to ignore', () => { - assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, Uri.file('/bar'), '/foo'), '/bar'); + assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', Uri.file('/bar'), '/foo'), '/bar'); }); }); From badedf5d7d88fbb3adef5cd43c7a2cc9ad06a586 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 17:22:46 -0800 Subject: [PATCH 04/20] Move terminalEnvironment, node->common Part of #69115 --- src/tsconfig.strictNullChecks.json | 2 +- src/vs/workbench/api/node/extHostTerminalService.ts | 5 +++-- .../terminal/{node => common}/terminalEnvironment.ts | 5 ++--- .../terminal/electron-browser/terminalPanel.ts | 2 +- .../electron-browser/terminalProcessManager.ts | 10 ++++++---- .../terminal/test/node/terminalEnvironment.test.ts | 12 ++++++------ 6 files changed, 19 insertions(+), 17 deletions(-) rename src/vs/workbench/contrib/terminal/{node => common}/terminalEnvironment.ts (96%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 7412a04e395b9..6d5d4e9b3707c 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -286,6 +286,7 @@ "./vs/workbench/contrib/terminal/common/terminal.ts", "./vs/workbench/contrib/terminal/common/terminalColorRegistry.ts", "./vs/workbench/contrib/terminal/common/terminalCommands.ts", + "./vs/workbench/contrib/terminal/common/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/common/terminalMenu.ts", "./vs/workbench/contrib/terminal/common/terminalService.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalActions.ts", @@ -295,7 +296,6 @@ "./vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", "./vs/workbench/contrib/terminal/node/terminalCommandTracker.ts", - "./vs/workbench/contrib/terminal/node/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/node/terminalProcess.ts", "./vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts", "./vs/workbench/contrib/terminal/node/windowsShellHelper.ts", diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 156d7ad5c821b..6f80fc77c38a4 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import pkg from 'vs/platform/product/node/package'; import * as os from 'os'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +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/node/extHost.protocol'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; @@ -451,7 +452,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Continue env initialization, merging in the env from the launch // config and adding keys that are needed to create the process - terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, terminalConfig.get('setLocaleVariables')); + terminalEnvironment.addTerminalEnvironmentKeys(env, pkg.version, platform.locale, terminalConfig.get('setLocaleVariables')); // Fork the process and listen for messages this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env); diff --git a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts similarity index 96% rename from src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts rename to src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 668ec2ef2acbf..cf8e7fab8501f 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -5,7 +5,6 @@ import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import pkg from 'vs/platform/product/node/package'; import { URI as Uri } from 'vs/base/common/uri'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -50,9 +49,9 @@ function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: s } } -export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, locale: string | undefined, setLocaleVariables: boolean): void { +export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void { env['TERM_PROGRAM'] = 'vscode'; - env['TERM_PROGRAM_VERSION'] = pkg.version; + env['TERM_PROGRAM_VERSION'] = version ? version : null; if (setLocaleVariables) { env['LANG'] = _getLangEnvVariable(locale); } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts index b4e7019e8961e..7d301112c400f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { Action, IAction } from 'vs/base/common/actions'; import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts index 786c7f7f2f880..0a9923779e8f5 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { ILogService } from 'vs/platform/log/common/log'; @@ -22,6 +22,7 @@ import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/commo import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IProductService } from 'vs/platform/product/common/product'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -62,7 +63,8 @@ export class TerminalProcessManager implements ITerminalProcessManager { @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, @IWindowService private readonly _windowService: IWindowService, @IWorkspaceConfigurationService private readonly _workspaceConfigurationService: IWorkspaceConfigurationService, - @IEnvironmentService private readonly _environmentSertvice: IEnvironmentService + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IProductService private readonly _productService: IProductService ) { this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { @@ -112,7 +114,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { } const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); - const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentSertvice.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd); + const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd); // Compel type system as process.env should not have any undefined entries let env: platform.IProcessEnvironment = {}; @@ -143,7 +145,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI'); // Adding other env keys necessary to create the process - terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables); + terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables); } this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env); diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts index 1549d0b2e5677..6153a741193d7 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; +import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { URI as Uri } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; @@ -13,21 +13,21 @@ suite('Workbench - TerminalEnvironment', () => { test('addTerminalEnvironmentKeys', () => { const env = { FOO: 'bar' }; const locale = 'en-au'; - terminalEnvironment.addTerminalEnvironmentKeys(env, locale, true); + terminalEnvironment.addTerminalEnvironmentKeys(env, '1.2.3', locale, true); assert.equal(env['TERM_PROGRAM'], 'vscode'); - assert.equal(env['TERM_PROGRAM_VERSION'].search(/^\d+\.\d+\.\d+$/), 0); + assert.equal(env['TERM_PROGRAM_VERSION'], '1.2.3'); assert.equal(env['LANG'], 'en_AU.UTF-8', 'LANG is equal to the requested locale with UTF-8'); const env2 = { FOO: 'bar' }; - terminalEnvironment.addTerminalEnvironmentKeys(env2, undefined, true); + terminalEnvironment.addTerminalEnvironmentKeys(env2, '1.2.3', undefined, true); assert.equal(env2['LANG'], 'en_US.UTF-8', 'LANG is equal to en_US.UTF-8 as fallback.'); // More info on issue #14586 const env3 = { LANG: 'replace' }; - terminalEnvironment.addTerminalEnvironmentKeys(env3, undefined, true); + terminalEnvironment.addTerminalEnvironmentKeys(env3, '1.2.3', undefined, true); assert.equal(env3['LANG'], 'en_US.UTF-8', 'LANG is set to the fallback LANG'); const env4 = { LANG: 'en_US.UTF-8' }; - terminalEnvironment.addTerminalEnvironmentKeys(env3, undefined, true); + terminalEnvironment.addTerminalEnvironmentKeys(env3, '1.2.3', undefined, true); assert.equal(env4['LANG'], 'en_US.UTF-8', 'LANG is equal to the parent environment\'s LANG'); }); From 5b7bdaccdecbab4d19521369dc2d5f65cb094db4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 17:31:58 -0800 Subject: [PATCH 05/20] Move terminalActions, electron-browser->browser Part of #69115 --- src/tsconfig.strictNullChecks.json | 2 +- .../terminal/{electron-browser => browser}/terminalActions.ts | 4 ++-- .../terminal/electron-browser/terminal.contribution.ts | 2 +- .../contrib/terminal/electron-browser/terminalPanel.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/terminalActions.ts (99%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 6d5d4e9b3707c..19a576445ba02 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -278,6 +278,7 @@ "./vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts", "./vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts", + "./vs/workbench/contrib/terminal/browser/terminalActions.ts", "./vs/workbench/contrib/terminal/browser/terminalFindWidget.ts", "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", "./vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts", @@ -289,7 +290,6 @@ "./vs/workbench/contrib/terminal/common/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/common/terminalMenu.ts", "./vs/workbench/contrib/terminal/common/terminalService.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalActions.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts", diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts similarity index 99% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalActions.ts rename to src/vs/workbench/contrib/terminal/browser/terminalActions.ts index a202076042bee..ea11c54029cab 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as os from 'os'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -34,6 +33,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { isWindows } from 'vs/base/common/platform'; export const TERMINAL_PICKER_PREFIX = 'term '; @@ -659,7 +659,7 @@ export class RunSelectedTextInTerminalAction extends Action { if (selection.isEmpty()) { text = editor.getModel().getLineContent(selection.selectionStartLineNumber).trim(); } else { - const endOfLinePreference = os.EOL === '\n' ? EndOfLinePreference.LF : EndOfLinePreference.CRLF; + const endOfLinePreference = isWindows ? EndOfLinePreference.LF : EndOfLinePreference.CRLF; text = editor.getModel().getValueInRange(selection, endOfLinePreference); } instance.sendText(text, true); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index b15ee85b95b36..562568064b1e8 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -16,7 +16,7 @@ import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious, DeleteToLineStartTerminalAction } from 'vs/workbench/contrib/terminal/electron-browser/terminalActions'; +import { ClearSelectionTerminalAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious, DeleteToLineStartTerminalAction, TERMINAL_PICKER_PREFIX, KillTerminalAction, CopyTerminalSelectionAction, SelectAllTerminalAction, ToggleTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { TerminalService } from 'vs/workbench/contrib/terminal/electron-browser/terminalService'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts index 7d301112c400f..036053299f8de 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts @@ -17,7 +17,7 @@ import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/termin import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { editorHoverBackground, editorHoverBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry'; -import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/electron-browser/terminalActions'; +import { KillTerminalAction, SwitchTerminalAction, SwitchTerminalActionItem, CopyTerminalSelectionAction, TerminalPasteAction, ClearTerminalAction, SelectAllTerminalAction, CreateNewTerminalAction, SplitTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { Panel } from 'vs/workbench/browser/panel'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { URI } from 'vs/base/common/uri'; From 3760c273fa12e6b89274049bb546fd013ae21ec5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 17:34:36 -0800 Subject: [PATCH 06/20] Move terminalPanel, electron-browser->browser Part of #69115 --- src/tsconfig.strictNullChecks.json | 2 +- .../terminalPanel.ts | 15 ++++++--------- .../workbench/contrib/terminal/common/terminal.ts | 1 + .../electron-browser/terminal.contribution.ts | 2 +- .../terminal/electron-browser/terminalService.ts | 2 +- src/vs/workbench/workbench.main.ts | 2 +- 6 files changed, 11 insertions(+), 13 deletions(-) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/terminalPanel.ts (96%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 19a576445ba02..41f71cf9d5ced 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -281,6 +281,7 @@ "./vs/workbench/contrib/terminal/browser/terminalActions.ts", "./vs/workbench/contrib/terminal/browser/terminalFindWidget.ts", "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", + "./vs/workbench/contrib/terminal/browser/terminalPanel.ts", "./vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts", "./vs/workbench/contrib/terminal/browser/terminalTab.ts", "./vs/workbench/contrib/terminal/browser/terminalWidgetManager.ts", @@ -292,7 +293,6 @@ "./vs/workbench/contrib/terminal/common/terminalService.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", "./vs/workbench/contrib/terminal/node/terminalCommandTracker.ts", diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts similarity index 96% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts rename to src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 036053299f8de..60286fd0e37ad 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -24,7 +24,6 @@ import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; import { IStorageService } from 'vs/platform/storage/common/storage'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -82,14 +81,12 @@ export class TerminalPanel extends Panel { if (e.affectsConfiguration('terminal.integrated.fontFamily') || e.affectsConfiguration('editor.fontFamily')) { const configHelper = this._terminalService.configHelper; - if (configHelper instanceof TerminalConfigHelper) { - if (!configHelper.configFontIsMonospace()) { - const choices: IPromptChoice[] = [{ - label: nls.localize('terminal.useMonospace', "Use 'monospace'"), - run: () => this._configurationService.updateValue('terminal.integrated.fontFamily', 'monospace'), - }]; - this._notificationService.prompt(Severity.Warning, nls.localize('terminal.monospaceOnly', "The terminal only supports monospace fonts."), choices); - } + if (!configHelper.configFontIsMonospace()) { + const choices: IPromptChoice[] = [{ + label: nls.localize('terminal.useMonospace', "Use 'monospace'"), + run: () => this._configurationService.updateValue('terminal.integrated.fontFamily', 'monospace'), + }]; + this._notificationService.prompt(Severity.Warning, nls.localize('terminal.monospaceOnly', "The terminal only supports monospace fonts."), choices); } } })); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 5673030976d5a..57217bb480c7f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -107,6 +107,7 @@ export interface ITerminalConfiguration { export interface ITerminalConfigHelper { config: ITerminalConfiguration; + configFontIsMonospace(): boolean; getFont(): ITerminalFont; /** * Merges the default shell path and args into the provided launch configuration diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index 562568064b1e8..e703212b0b4a6 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -28,7 +28,7 @@ import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/qu import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { TerminalPanel } from 'vs/workbench/contrib/terminal/electron-browser/terminalPanel'; +import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index 1914cf4af95e3..aa6c650d9247b 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -18,7 +18,7 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-bro import Severity from 'vs/base/common/severity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; -import { TerminalPanel } from 'vs/workbench/contrib/terminal/electron-browser/terminalPanel'; +import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 7a1ce54df5f44..84ed6ce8f0281 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -154,7 +154,7 @@ import 'vs/workbench/contrib/output/browser/outputPanel'; // Terminal import 'vs/workbench/contrib/terminal/electron-browser/terminal.contribution'; import 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -import 'vs/workbench/contrib/terminal/electron-browser/terminalPanel'; +import 'vs/workbench/contrib/terminal/browser/terminalPanel'; // Relauncher import 'vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution'; From 2675ffeaafa5b91dd2102795543cde8e8f4d6075 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 17:39:47 -0800 Subject: [PATCH 07/20] Move parts of terminalService up to common/browser --- .../terminal/browser/terminalService.ts | 44 ++++++++++++- .../terminal/common/terminalService.ts | 33 ++++++++-- .../electron-browser/terminalService.ts | 65 ++----------------- 3 files changed, 74 insertions(+), 68 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 060ce2863aaea..7bf4664b9e66b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -3,13 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalService as CommonTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export abstract class TerminalService extends CommonTerminalService implements ITerminalService { @@ -18,8 +21,43 @@ export abstract class TerminalService extends CommonTerminalService implements I @IPanelService panelService: IPanelService, @IPartService partService: IPartService, @ILifecycleService lifecycleService: ILifecycleService, - @IStorageService storageService: IStorageService + @IStorageService storageService: IStorageService, + @INotificationService notificationService: INotificationService, + @IDialogService dialogService: IDialogService ) { - super(contextKeyService, panelService, partService, lifecycleService, storageService); + super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService); + } + + public focusFindWidget(): Promise { + return this.showPanel(false).then(() => { + const panel = this._panelService.getActivePanel() as TerminalPanel; + panel.focusFindWidget(); + this._findWidgetVisible.set(true); + }); + } + + public hideFindWidget(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.hideFindWidget(); + this._findWidgetVisible.reset(); + panel.focus(); + } + } + + public findNext(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.showFindWidget(); + panel.getFindWidget().find(false); + } + } + + public findPrevious(): void { + const panel = this._panelService.getActivePanel() as TerminalPanel; + if (panel && panel.getId() === TERMINAL_PANEL_ID) { + panel.showFindWidget(); + panel.getFindWidget().find(true); + } } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 792ffad09b7c9..1999fd8a6eccd 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; 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'; @@ -12,6 +13,8 @@ import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfi import { IStorageService } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; export abstract class TerminalService implements ITerminalService { public _serviceBrand: any; @@ -60,7 +63,9 @@ export abstract class TerminalService implements ITerminalService { @IPanelService protected readonly _panelService: IPanelService, @IPartService private readonly _partService: IPartService, @ILifecycleService lifecycleService: ILifecycleService, - @IStorageService protected readonly _storageService: IStorageService + @IStorageService protected readonly _storageService: IStorageService, + @INotificationService protected readonly _notificationService: INotificationService, + @IDialogService private readonly _dialogService: IDialogService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -88,11 +93,8 @@ export abstract class TerminalService implements ITerminalService { this.onInstancesChanged(() => updateTerminalContextKeys()); } - protected abstract _showTerminalCloseConfirmation(): Promise; - protected abstract _showNotEnoughSpaceToast(): void; public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; - public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance; public abstract selectDefaultWindowsShell(): Promise; public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; @@ -101,6 +103,11 @@ export abstract class TerminalService implements ITerminalService { return this.createTerminal({ name, isRendererOnly: true }); } + public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance { + const activeInstance = this.getActiveInstance(); + return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction); + } + private _onBeforeShutdown(): boolean | Promise { if (this.terminalInstances.length === 0) { // No terminal instances, don't veto @@ -373,4 +380,22 @@ export abstract class TerminalService implements ITerminalService { public setWorkspaceShellAllowed(isAllowed: boolean): void { this.configHelper.setWorkspaceShellAllowed(isAllowed); } + + protected _showTerminalCloseConfirmation(): Promise { + let message; + if (this.terminalInstances.length === 1) { + message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); + } else { + message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); + } + + return this._dialogService.confirm({ + message, + type: 'warning', + }).then(res => !res.confirmed); + } + + protected _showNotEnoughSpaceToast(): void { + this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); + } } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index aa6c650d9247b..b2739f6b45ab2 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -12,13 +12,12 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, TERMINAL_PANEL_ID, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; import Severity from 'vs/base/common/severity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; -import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -43,12 +42,12 @@ export class TerminalService extends BrowserTerminalService implements ITerminal @IConfigurationService private readonly _configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IQuickInputService private readonly _quickInputService: IQuickInputService, - @INotificationService private readonly _notificationService: INotificationService, - @IDialogService private readonly _dialogService: IDialogService, + @INotificationService notificationService: INotificationService, + @IDialogService dialogService: IDialogService, @IExtensionService private readonly _extensionService: IExtensionService, @IWindowService private readonly _windowService: IWindowService, ) { - super(contextKeyService, panelService, partService, lifecycleService, storageService); + super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService); this._terminalTabs = []; this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper); @@ -112,39 +111,6 @@ export class TerminalService extends BrowserTerminalService implements ITerminal }); } - public focusFindWidget(): Promise { - return this.showPanel(false).then(() => { - const panel = this._panelService.getActivePanel() as TerminalPanel; - panel.focusFindWidget(); - this._findWidgetVisible.set(true); - }); - } - - public hideFindWidget(): void { - const panel = this._panelService.getActivePanel() as TerminalPanel; - if (panel && panel.getId() === TERMINAL_PANEL_ID) { - panel.hideFindWidget(); - this._findWidgetVisible.reset(); - panel.focus(); - } - } - - public findNext(): void { - const panel = this._panelService.getActivePanel() as TerminalPanel; - if (panel && panel.getId() === TERMINAL_PANEL_ID) { - panel.showFindWidget(); - panel.getFindWidget().find(false); - } - } - - public findPrevious(): void { - const panel = this._panelService.getActivePanel() as TerminalPanel; - if (panel && panel.getId() === TERMINAL_PANEL_ID) { - panel.showFindWidget(); - panel.getFindWidget().find(true); - } - } - private _suggestShellChange(wasNewTerminalAction?: boolean): void { // Only suggest on Windows since $SHELL works great for macOS/Linux if (!platform.isWindows) { @@ -277,29 +243,6 @@ export class TerminalService extends BrowserTerminalService implements ITerminal }); } - public getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance { - const activeInstance = this.getActiveInstance(); - return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction); - } - - protected _showTerminalCloseConfirmation(): Promise { - let message; - if (this.terminalInstances.length === 1) { - message = nls.localize('terminalService.terminalCloseConfirmationSingular', "There is an active terminal session, do you want to kill it?"); - } else { - message = nls.localize('terminalService.terminalCloseConfirmationPlural', "There are {0} active terminal sessions, do you want to kill them?", this.terminalInstances.length); - } - - return this._dialogService.confirm({ - message, - type: 'warning', - }).then(res => !res.confirmed); - } - - protected _showNotEnoughSpaceToast(): void { - this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); - } - public setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void { this._configHelper.panelContainer = panelContainer; this._terminalContainer = terminalContainer; From 1d1dbdc494a7e7821ea77c0bae5f57cd7a5c9f96 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 17:55:20 -0800 Subject: [PATCH 08/20] Move TerminalConfigHelper, electron-browser->browser Part of #69115 --- src/tsconfig.strictNullChecks.json | 2 +- .../terminalConfigHelper.ts | 8 +-- .../contrib/terminal/common/terminal.ts | 6 ++ .../terminal/common/terminalService.ts | 2 +- .../electron-browser/terminalInstance.ts | 2 +- .../electron-browser/terminalService.ts | 7 +- .../contrib/terminal/node/terminal.ts | 9 +-- .../terminalConfigHelper.test.ts | 71 ++++++++++--------- 8 files changed, 58 insertions(+), 49 deletions(-) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/terminalConfigHelper.ts (97%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 41f71cf9d5ced..c0381030c46b6 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -279,6 +279,7 @@ "./vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts", "./vs/workbench/contrib/terminal/browser/terminalActions.ts", + "./vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/browser/terminalFindWidget.ts", "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", "./vs/workbench/contrib/terminal/browser/terminalPanel.ts", @@ -291,7 +292,6 @@ "./vs/workbench/contrib/terminal/common/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/common/terminalMenu.ts", "./vs/workbench/contrib/terminal/common/terminalService.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts similarity index 97% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts rename to src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index fd82c1a61fb06..91a780466ad60 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -10,9 +10,8 @@ import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/ed import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; -import { isFedora, isUbuntu } from 'vs/workbench/contrib/terminal/node/terminal'; import { Terminal as XTermTerminal } from 'vscode-xterm'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -31,6 +30,7 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { public config: ITerminalConfiguration; public constructor( + private readonly _linuxDistro: LinuxDistro, @IConfigurationService private readonly _configurationService: IConfigurationService, @IWorkspaceConfigurationService private readonly _workspaceConfigurationService: IWorkspaceConfigurationService, @INotificationService private readonly _notificationService: INotificationService, @@ -118,10 +118,10 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { // Work around bad font on Fedora/Ubuntu if (!this.config.fontFamily) { - if (isFedora) { + if (this._linuxDistro === LinuxDistro.Fedora) { fontFamily = '\'DejaVu Sans Mono\', monospace'; } - if (isUbuntu) { + if (this._linuxDistro === LinuxDistro.Ubuntu) { fontFamily = '\'Ubuntu Mono\', monospace'; // Ubuntu mono is somehow smaller, so set fontSize a bit larger to get the same perceived size. diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 57217bb480c7f..a31e4c2c714ef 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -687,4 +687,10 @@ export interface ITerminalProcessExtHostRequest { activeWorkspaceRootUri: URI; cols: number; rows: number; +} + +export enum LinuxDistro { + Fedora, + Ubuntu, + Unknown } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 1999fd8a6eccd..88b917fbf5262 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -23,7 +23,7 @@ export abstract class TerminalService implements ITerminalService { protected _terminalFocusContextKey: IContextKey; protected _findWidgetVisible: IContextKey; protected _terminalContainer: HTMLElement; - protected _terminalTabs: ITerminalTab[]; + protected _terminalTabs: ITerminalTab[] = []; protected get _terminalInstances(): ITerminalInstance[] { return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts index 22f9378da7b2d..7341a54add6d5 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts @@ -30,7 +30,7 @@ import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/ter import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/electron-browser/terminalProcessManager'; import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/node/terminalCommandTracker'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index b2739f6b45ab2..483d22ccf8906 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -14,10 +14,10 @@ import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import Severity from 'vs/base/common/severity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getDefaultShell, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -49,8 +49,7 @@ export class TerminalService extends BrowserTerminalService implements ITerminal ) { super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService); - this._terminalTabs = []; - this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper); + this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, linuxDistro); ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => { // if the request to open files is coming in from the integrated terminal (identified though // the termProgram variable) and we are instructed to wait for editors close, wait for the diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 73b41c24e5de9..96b165c26148e 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -8,6 +8,7 @@ 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 { Event } from 'vs/base/common/event'; +import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; /** * An interface representing a raw terminal child process, this contains a subset of the @@ -78,6 +79,7 @@ function getTerminalDefaultShellWindows(): string { return _TERMINAL_DEFAULT_SHELL_WINDOWS; } +let detectedDistro = LinuxDistro.Unknown; if (platform.isLinux) { const file = '/etc/os-release'; fileExists(file).then(exists => { @@ -87,13 +89,12 @@ if (platform.isLinux) { readFile(file).then(b => { const contents = b.toString(); if (/NAME="?Fedora"?/.test(contents)) { - isFedora = true; + detectedDistro = LinuxDistro.Fedora; } else if (/NAME="?Ubuntu"?/.test(contents)) { - isUbuntu = true; + detectedDistro = LinuxDistro.Ubuntu; } }); }); } -export let isFedora = false; -export let isUbuntu = false; \ No newline at end of file +export const linuxDistro = detectedDistro; diff --git a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts index 4ee63fee36003..e0110601c8248 100644 --- a/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/electron-browser/terminalConfigHelper'; +import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { isFedora, isUbuntu } from 'vs/workbench/contrib/terminal/node/terminal'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; suite('Workbench - TerminalConfigHelper', () => { let fixture: HTMLElement; @@ -21,22 +21,24 @@ suite('Workbench - TerminalConfigHelper', () => { configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily'); configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } }); // Recreate config helper as onDidChangeConfiguration isn't implemented in TestConfigurationService - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); - configHelper.panelContainer = fixture; - if (isFedora) { - assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); - } else if (isUbuntu) { - assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); - } else { - assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); - } + configHelper = new TerminalConfigHelper(LinuxDistro.Fedora, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); }); test('TerminalConfigHelper - getFont fontSize', function () { @@ -52,7 +54,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 10 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize'); @@ -65,13 +67,14 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; - if (isUbuntu) { - assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); - } else { - assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); - } + assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); + configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); @@ -81,7 +84,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: 1500 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().fontSize, 25, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it'); @@ -94,13 +97,13 @@ suite('Workbench - TerminalConfigHelper', () => { fontSize: null } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Ubuntu, configurationService, null!, null!, null!); + configHelper.panelContainer = fixture; + assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); + + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; - if (isUbuntu) { - assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); - } else { - assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); - } + assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); }); test('TerminalConfigHelper - getFont lineHeight', function () { @@ -116,7 +119,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 2 } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight'); @@ -130,7 +133,7 @@ suite('Workbench - TerminalConfigHelper', () => { lineHeight: 0 } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.getFont().lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set'); }); @@ -143,7 +146,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -155,7 +158,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'sans-serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -167,7 +170,7 @@ suite('Workbench - TerminalConfigHelper', () => { fontFamily: 'serif' } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); @@ -183,7 +186,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -199,7 +202,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -215,7 +218,7 @@ suite('Workbench - TerminalConfigHelper', () => { } }); - let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!); + let configHelper = new TerminalConfigHelper(LinuxDistro.Unknown, configurationService, null!, null!, null!); configHelper.panelContainer = fixture; assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); From b5dcee0a3185de2e019cc8127cbc5f91ab2b1b29 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 18:01:47 -0800 Subject: [PATCH 09/20] Move terminal CSS and images to browser Part of #69115 --- .../media/configure-inverse.svg | 0 .../{electron-browser => browser}/media/configure.svg | 0 .../{electron-browser => browser}/media/kill-inverse.svg | 0 .../terminal/{electron-browser => browser}/media/kill.svg | 0 .../{electron-browser => browser}/media/new-inverse.svg | 0 .../terminal/{electron-browser => browser}/media/new.svg | 0 .../{electron-browser => browser}/media/scrollbar.css | 0 .../media/split-horizontal-inverse.svg | 0 .../media/split-horizontal.svg | 0 .../{electron-browser => browser}/media/split-inverse.svg | 0 .../{electron-browser => browser}/media/split.svg | 0 .../{electron-browser => browser}/media/terminal.css | 0 .../{electron-browser => browser}/media/widgets.css | 0 .../{electron-browser => browser}/media/xterm.css | 0 .../terminal/electron-browser/terminal.contribution.ts | 8 ++++---- 15 files changed, 4 insertions(+), 4 deletions(-) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/configure-inverse.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/configure.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/kill-inverse.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/kill.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/new-inverse.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/new.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/scrollbar.css (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/split-horizontal-inverse.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/split-horizontal.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/split-inverse.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/split.svg (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/terminal.css (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/widgets.css (100%) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/media/xterm.css (100%) diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/configure-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/configure-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/configure-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/configure-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/configure.svg b/src/vs/workbench/contrib/terminal/browser/media/configure.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/configure.svg rename to src/vs/workbench/contrib/terminal/browser/media/configure.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/kill-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/kill-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/kill-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/kill.svg b/src/vs/workbench/contrib/terminal/browser/media/kill.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/kill.svg rename to src/vs/workbench/contrib/terminal/browser/media/kill.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/new-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/new-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/new-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/new-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/new.svg b/src/vs/workbench/contrib/terminal/browser/media/new.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/new.svg rename to src/vs/workbench/contrib/terminal/browser/media/new.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/scrollbar.css b/src/vs/workbench/contrib/terminal/browser/media/scrollbar.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/scrollbar.css rename to src/vs/workbench/contrib/terminal/browser/media/scrollbar.css diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/split-horizontal-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/split-horizontal-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal.svg b/src/vs/workbench/contrib/terminal/browser/media/split-horizontal.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split-horizontal.svg rename to src/vs/workbench/contrib/terminal/browser/media/split-horizontal.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split-inverse.svg b/src/vs/workbench/contrib/terminal/browser/media/split-inverse.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split-inverse.svg rename to src/vs/workbench/contrib/terminal/browser/media/split-inverse.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/split.svg b/src/vs/workbench/contrib/terminal/browser/media/split.svg similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/split.svg rename to src/vs/workbench/contrib/terminal/browser/media/split.svg diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/terminal.css rename to src/vs/workbench/contrib/terminal/browser/media/terminal.css diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/widgets.css b/src/vs/workbench/contrib/terminal/browser/media/widgets.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/widgets.css rename to src/vs/workbench/contrib/terminal/browser/media/widgets.css diff --git a/src/vs/workbench/contrib/terminal/electron-browser/media/xterm.css b/src/vs/workbench/contrib/terminal/browser/media/xterm.css similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/media/xterm.css rename to src/vs/workbench/contrib/terminal/browser/media/xterm.css diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index e703212b0b4a6..e278b7ee47ec8 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/scrollbar'; -import 'vs/css!./media/terminal'; -import 'vs/css!./media/xterm'; -import 'vs/css!./media/widgets'; +import 'vs/css!../browser/media/scrollbar'; +import 'vs/css!../browser/media/terminal'; +import 'vs/css!../browser/media/xterm'; +import 'vs/css!../browser/media/widgets'; import * as nls from 'vs/nls'; import * as panel from 'vs/workbench/browser/panel'; import * as platform from 'vs/base/common/platform'; From 00f5bdb0ba79ad1d7a93815afde7486640df708e Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 18:04:05 -0800 Subject: [PATCH 10/20] Move terminalCommandTracker, node->browser Part of #69115 --- src/tsconfig.strictNullChecks.json | 4 ++-- .../terminal/{node => browser}/terminalCommandTracker.ts | 0 .../contrib/terminal/electron-browser/terminalInstance.ts | 2 +- .../{node => electron-browser}/terminalCommandTracker.test.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/vs/workbench/contrib/terminal/{node => browser}/terminalCommandTracker.ts (100%) rename src/vs/workbench/contrib/terminal/test/{node => electron-browser}/terminalCommandTracker.test.ts (99%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index c0381030c46b6..e3974e6fdcc0b 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -279,6 +279,7 @@ "./vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts", "./vs/workbench/contrib/terminal/browser/terminalActions.ts", + "./vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts", "./vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/browser/terminalFindWidget.ts", "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", @@ -295,14 +296,13 @@ "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", - "./vs/workbench/contrib/terminal/node/terminalCommandTracker.ts", "./vs/workbench/contrib/terminal/node/terminalProcess.ts", "./vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts", "./vs/workbench/contrib/terminal/node/windowsShellHelper.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts", + "./vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalConfigHelper.test.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalLinkHandler.test.ts", - "./vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts", "./vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts", "./vs/workbench/contrib/themes/browser/themes.contribution.ts", "./vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution.ts", diff --git a/src/vs/workbench/contrib/terminal/node/terminalCommandTracker.ts b/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts similarity index 100% rename from src/vs/workbench/contrib/terminal/node/terminalCommandTracker.ts rename to src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts index 7341a54add6d5..dced8d25d784b 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts @@ -33,7 +33,7 @@ import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/termin import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/electron-browser/terminalProcessManager'; -import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/node/terminalCommandTracker'; +import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker'; import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ISearchOptions, Terminal as XTermTerminal } from 'vscode-xterm'; diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts similarity index 99% rename from src/vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts rename to src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts index 324781f164c75..ec94ff8a518bc 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalCommandTracker.test.ts +++ b/src/vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Terminal, TerminalCore } from 'vscode-xterm'; -import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/node/terminalCommandTracker'; +import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker'; import { isWindows } from 'vs/base/common/platform'; interface TestTerminalCore extends TerminalCore { From 415c08825d9b3cdd7530ccf4e37c3b19af231888 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 18:20:50 -0800 Subject: [PATCH 11/20] Move a lot of terminalService to common/browser --- .../terminal/browser/terminalService.ts | 107 ++++++++++++++- .../terminal/common/terminalService.ts | 30 ++++- .../electron-browser/terminalService.ts | 124 ++---------------- 3 files changed, 140 insertions(+), 121 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 7bf4664b9e66b..051667c8a6736 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -3,16 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITerminalService, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, IShellLaunchConfig, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalService as CommonTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; export abstract class TerminalService extends CommonTerminalService implements ITerminalService { @@ -23,9 +30,99 @@ export abstract class TerminalService extends CommonTerminalService implements I @ILifecycleService lifecycleService: ILifecycleService, @IStorageService storageService: IStorageService, @INotificationService notificationService: INotificationService, - @IDialogService dialogService: IDialogService + @IDialogService dialogService: IDialogService, + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @IWindowService private _windowService: IWindowService, + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService ) { - super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService); + super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService, extensionService, fileService); + } + + protected abstract _getDefaultShell(p: platform.Platform): string; + + public createTerminal(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean): ITerminalInstance { + const terminalTab = this._instantiationService.createInstance(TerminalTab, + this._terminalFocusContextKey, + this.configHelper, + this._terminalContainer, + shell); + this._terminalTabs.push(terminalTab); + const instance = terminalTab.terminalInstances[0]; + terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); + terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); + this._initInstanceListeners(instance); + if (this.terminalInstances.length === 1) { + // It's the first instance so it should be made active automatically + this.setActiveInstanceByIndex(0); + } + this._onInstancesChanged.fire(); + this._suggestShellChange(wasNewTerminalAction); + return instance; + } + + private _suggestShellChange(wasNewTerminalAction?: boolean): void { + // Only suggest on Windows since $SHELL works great for macOS/Linux + if (!platform.isWindows) { + return; + } + + if (this._windowService.getConfiguration().remoteAuthority) { + // Don't suggest if the opened workspace is remote + return; + } + + // Only suggest when the terminal instance is being created by an explicit user action to + // launch a terminal, as opposed to something like tasks, debug, panel restore, etc. + if (!wasNewTerminalAction) { + return; + } + + if (this._windowService.getConfiguration().remoteAuthority) { + // Don't suggest if the opened workspace is remote + return; + } + + // Don't suggest if the user has explicitly opted out + const neverSuggest = this._storageService.getBoolean(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, StorageScope.GLOBAL, false); + if (neverSuggest) { + return; + } + + // Never suggest if the setting is non-default already (ie. they set the setting manually) + if (this.configHelper.config.shell.windows !== this._getDefaultShell(platform.Platform.Windows)) { + this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL); + return; + } + + this._notificationService.prompt( + Severity.Info, + nls.localize('terminal.integrated.chooseWindowsShellInfo', "You can change the default terminal shell by selecting the customize button."), + [{ + label: nls.localize('customize', "Customize"), + run: () => { + this.selectDefaultWindowsShell().then(shell => { + if (!shell) { + return Promise.resolve(null); + } + // Launch a new instance with the newly selected shell + const instance = this.createTerminal({ + executable: shell, + args: this.configHelper.config.shellArgs.windows + }); + if (instance) { + this.setActiveInstance(instance); + } + return Promise.resolve(null); + }); + } + }, + { + label: nls.localize('never again', "Don't Show Again"), + isSecondary: true, + run: () => this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL) + }] + ); } public focusFindWidget(): Promise { diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 88b917fbf5262..3d3a3baac49c1 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -15,6 +15,8 @@ import { URI } from 'vs/base/common/uri'; import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; export abstract class TerminalService implements ITerminalService { public _serviceBrand: any; @@ -65,7 +67,9 @@ export abstract class TerminalService implements ITerminalService { @ILifecycleService lifecycleService: ILifecycleService, @IStorageService protected readonly _storageService: IStorageService, @INotificationService protected readonly _notificationService: INotificationService, - @IDialogService private readonly _dialogService: IDialogService + @IDialogService private readonly _dialogService: IDialogService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IFileService private readonly _fileService: IFileService ) { this._activeTabIndex = 0; this._isShuttingDown = false; @@ -97,7 +101,6 @@ export abstract class TerminalService implements ITerminalService { public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; public abstract selectDefaultWindowsShell(): Promise; public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; public createTerminalRenderer(name: string): ITerminalInstance { return this.createTerminal({ name, isRendererOnly: true }); @@ -108,6 +111,16 @@ export abstract class TerminalService implements ITerminalService { return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction); } + public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void { + // Ensure extension host is ready before requesting a process + this._extensionService.whenInstalledExtensionsRegistered().then(() => { + // TODO: MainThreadTerminalService is not ready at this point, fix this + setTimeout(() => { + this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows }); + }, 500); + }); + } + private _onBeforeShutdown(): boolean | Promise { if (this.terminalInstances.length === 0) { // No terminal instances, don't veto @@ -398,4 +411,17 @@ export abstract class TerminalService implements ITerminalService { protected _showNotEnoughSpaceToast(): void { this._notificationService.info(nls.localize('terminal.minWidth', "Not enough space to split terminal.")); } + + protected _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { + if (potentialPaths.length === 0) { + return Promise.resolve(null); + } + const current = potentialPaths.shift(); + return this._fileService.existsFile(URI.file(current!)).then(exists => { + if (!exists) { + return this._validateShellPaths(label, potentialPaths); + } + return [label, current] as [string, string]; + }); + } } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index 483d22ccf8906..95f089ef3fd6f 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -12,22 +12,20 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; -import Severity from 'vs/base/common/severity'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { getDefaultShell, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; -import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ipcRenderer as ipc } from 'electron'; import { IOpenFileRequest, IWindowService } from 'vs/platform/windows/common/windows'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstance'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { URI } from 'vs/base/common/uri'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; import { coalesce } from 'vs/base/common/arrays'; +import { IFileService } from 'vs/platform/files/common/files'; export class TerminalService extends BrowserTerminalService implements ITerminalService { private _configHelper: TerminalConfigHelper; @@ -40,14 +38,15 @@ export class TerminalService extends BrowserTerminalService implements ITerminal @IStorageService storageService: IStorageService, @ILifecycleService lifecycleService: ILifecycleService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IQuickInputService private readonly _quickInputService: IQuickInputService, @INotificationService notificationService: INotificationService, @IDialogService dialogService: IDialogService, - @IExtensionService private readonly _extensionService: IExtensionService, - @IWindowService private readonly _windowService: IWindowService, + @IExtensionService extensionService: IExtensionService, + @IWindowService windowService: IWindowService, + @IFileService fileService: IFileService ) { - super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService); + super(contextKeyService, panelService, partService, lifecycleService, storageService, notificationService, dialogService, instantiationService, windowService, extensionService, fileService); this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, linuxDistro); ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => { @@ -74,104 +73,14 @@ export class TerminalService extends BrowserTerminalService implements ITerminal }); } - public createTerminal(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean): ITerminalInstance { - const terminalTab = this._instantiationService.createInstance(TerminalTab, - this._terminalFocusContextKey, - this._configHelper, - this._terminalContainer, - shell); - this._terminalTabs.push(terminalTab); - const instance = terminalTab.terminalInstances[0]; - terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); - terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); - this._initInstanceListeners(instance); - if (this.terminalInstances.length === 1) { - // It's the first instance so it should be made active automatically - this.setActiveInstanceByIndex(0); - } - this._onInstancesChanged.fire(); - this._suggestShellChange(wasNewTerminalAction); - return instance; - } - public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); this._onInstanceCreated.fire(instance); return instance; } - public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void { - // Ensure extension host is ready before requesting a process - this._extensionService.whenInstalledExtensionsRegistered().then(() => { - // TODO: MainThreadTerminalService is not ready at this point, fix this - setTimeout(() => { - this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows }); - }, 500); - }); - } - - private _suggestShellChange(wasNewTerminalAction?: boolean): void { - // Only suggest on Windows since $SHELL works great for macOS/Linux - if (!platform.isWindows) { - return; - } - - if (this._windowService.getConfiguration().remoteAuthority) { - // Don't suggest if the opened workspace is remote - return; - } - - // Only suggest when the terminal instance is being created by an explicit user action to - // launch a terminal, as opposed to something like tasks, debug, panel restore, etc. - if (!wasNewTerminalAction) { - return; - } - - if (this._windowService.getConfiguration().remoteAuthority) { - // Don't suggest if the opened workspace is remote - return; - } - - // Don't suggest if the user has explicitly opted out - const neverSuggest = this._storageService.getBoolean(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, StorageScope.GLOBAL, false); - if (neverSuggest) { - return; - } - - // Never suggest if the setting is non-default already (ie. they set the setting manually) - if (this._configHelper.config.shell.windows !== getDefaultShell(platform.Platform.Windows)) { - this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL); - return; - } - - this._notificationService.prompt( - Severity.Info, - nls.localize('terminal.integrated.chooseWindowsShellInfo', "You can change the default terminal shell by selecting the customize button."), - [{ - label: nls.localize('customize', "Customize"), - run: () => { - this.selectDefaultWindowsShell().then(shell => { - if (!shell) { - return Promise.resolve(null); - } - // Launch a new instance with the newly selected shell - const instance = this.createTerminal({ - executable: shell, - args: this._configHelper.config.shellArgs.windows - }); - if (instance) { - this.setActiveInstance(instance); - } - return Promise.resolve(null); - }); - } - }, - { - label: nls.localize('never again', "Don't Show Again"), - isSecondary: true, - run: () => this._storageService.store(NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, true, StorageScope.GLOBAL) - }] - ); + protected _getDefaultShell(p: platform.Platform): string { + return getDefaultShell(p); } public selectDefaultWindowsShell(): Promise { @@ -229,19 +138,6 @@ export class TerminalService extends BrowserTerminalService implements ITerminal }); } - private _validateShellPaths(label: string, potentialPaths: string[]): Promise<[string, string] | null> { - if (potentialPaths.length === 0) { - return Promise.resolve(null); - } - const current = potentialPaths.shift(); - return pfs.fileExists(current!).then(exists => { - if (!exists) { - return this._validateShellPaths(label, potentialPaths); - } - return [label, current] as [string, string]; - }); - } - public setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void { this._configHelper.panelContainer = panelContainer; this._terminalContainer = terminalContainer; From d9af6b90a069fb18fc458e49046e0a57e812329d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 19:18:01 -0800 Subject: [PATCH 12/20] Remove execFile dep in terminal instance, unify 2 path prep functions --- .../terminal/browser/terminalFindWidget.css | 4 --- .../contrib/terminal/browser/terminalPanel.ts | 7 ++-- .../terminal/common/terminalEnvironment.ts | 29 +++++----------- .../electron-browser/terminalInstance.ts | 34 +++---------------- .../electron-browser/terminalService.ts | 4 +-- .../contrib/terminal/node/terminal.ts | 26 ++++++++++++++ .../test/node/terminalEnvironment.test.ts | 10 ------ 7 files changed, 46 insertions(+), 68 deletions(-) delete mode 100644 src/vs/workbench/contrib/terminal/browser/terminalFindWidget.css diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.css b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.css deleted file mode 100644 index a4a092d83492f..0000000000000 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.css +++ /dev/null @@ -1,4 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 60286fd0e37ad..6731067284784 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -6,7 +6,6 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; -import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { Action, IAction } from 'vs/base/common/actions'; import { IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -281,7 +280,7 @@ export class TerminalPanel extends Panel { event.stopPropagation(); } })); - this._register(dom.addDisposableListener(this._parentDomElement, dom.EventType.DROP, (e: DragEvent) => { + this._register(dom.addDisposableListener(this._parentDomElement, dom.EventType.DROP, async (e: DragEvent) => { if (e.target === this._parentDomElement || dom.isAncestor(e.target as HTMLElement, this._parentDomElement)) { if (!e.dataTransfer) { return; @@ -303,7 +302,9 @@ export class TerminalPanel extends Panel { const terminal = this._terminalService.getActiveInstance(); if (terminal) { - terminal.sendText(terminalEnvironment.preparePathForTerminal(path), false); + return terminal.preparePathForTerminalAsync(path).then(preparedPath => { + terminal.sendText(preparedPath, false); + }); } } })); diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index cf8e7fab8501f..5480374bba89f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -132,26 +132,15 @@ function _sanitizeCwd(cwd: string): string { return cwd; } -/** - * Adds quotes to a path if it contains whitespaces - */ -export function preparePathForTerminal(path: string): string { - if (platform.isWindows) { - if (/\s+/.test(path)) { - return `"${path}"`; - } - return path; +export function escapeNonWindowsPath(path: string): string { + let newPath = path; + if (newPath.indexOf('\\') !== 0) { + newPath = newPath.replace(/\\/g, '\\\\'); } - path = path.replace(/(%5C|\\)/g, '\\\\'); - const charsToEscape = [ - ' ', '\'', '"', '?', ':', ';', '!', '*', '(', ')', '{', '}', '[', ']' - ]; - for (let i = 0; i < path.length; i++) { - const indexOfChar = charsToEscape.indexOf(path.charAt(i)); - if (indexOfChar >= 0) { - path = `${path.substring(0, i)}\\${path.charAt(i)}${path.substring(i + 1)}`; - i++; // Skip char due to escape char being added - } + if (!newPath && (newPath.indexOf('"') !== -1)) { + newPath = '\'' + newPath + '\''; + } else if (newPath.indexOf(' ') !== -1) { + newPath = newPath.replace(/ /g, '\\ '); } - return path; + return newPath; } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts index dced8d25d784b..7fe8f6286f0a0 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { execFile } from 'child_process'; -import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -38,6 +36,8 @@ import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsSh import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ISearchOptions, Terminal as XTermTerminal } from 'vscode-xterm'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; +import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { getWslPath, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -829,10 +829,8 @@ export class TerminalInstance implements ITerminalInstance { if (platform.isWindows) { // 17063 is the build number where wsl path was introduced. // Update Windows uriPath to be executed in WSL. - if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (TerminalInstance.getWindowsBuildNumber() >= 17063)) { - execFile('bash.exe', ['-c', 'echo $(wslpath ' + this._escapeNonWindowsPath(originalPath) + ')'], {}, (error, stdout, stderr) => { - c(this._escapeNonWindowsPath(stdout.trim())); - }); + if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (getWindowsBuildNumber() >= 17063)) { + c(getWslPath(originalPath)); return; } else if (hasSpace) { c('"' + originalPath + '"'); @@ -841,32 +839,10 @@ export class TerminalInstance implements ITerminalInstance { } return; } - c(this._escapeNonWindowsPath(originalPath)); + c(escapeNonWindowsPath(originalPath)); }); } - private _escapeNonWindowsPath(path: string): string { - let newPath = path; - if (newPath.indexOf('\\') !== 0) { - newPath = newPath.replace(/\\/g, '\\\\'); - } - if (!newPath && (newPath.indexOf('"') !== -1)) { - newPath = '\'' + newPath + '\''; - } else if (newPath.indexOf(' ') !== -1) { - newPath = newPath.replace(/ /g, '\\ '); - } - return newPath; - } - - public static getWindowsBuildNumber(): number { - const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); - let buildNumber: number = 0; - if (osVersion && osVersion.length === 4) { - buildNumber = parseInt(osVersion[3]); - } - return buildNumber; - } - public setVisible(visible: boolean): void { this._isVisible = visible; if (this._wrapperElement) { diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index 95f089ef3fd6f..86451a2b64fe6 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -16,7 +16,7 @@ import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfi import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { getDefaultShell, linuxDistro } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getDefaultShell, linuxDistro, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ipcRenderer as ipc } from 'electron'; @@ -108,7 +108,7 @@ export class TerminalService extends BrowserTerminalService implements ITerminal let useWSLexe = false; - if (TerminalInstance.getWindowsBuildNumber() >= 16299) { + if (getWindowsBuildNumber() >= 16299) { useWSLexe = true; } diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 96b165c26148e..a7b7667302319 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -9,6 +9,8 @@ import * as processes from 'vs/base/node/processes'; import { readFile, fileExists } from 'vs/base/node/pfs'; import { Event } from 'vs/base/common/event'; import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { execFile } from 'child_process'; +import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; /** * An interface representing a raw terminal child process, this contains a subset of the @@ -98,3 +100,27 @@ if (platform.isLinux) { } export const linuxDistro = detectedDistro; + +export function getWindowsBuildNumber(): number { + const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); + let buildNumber: number = 0; + if (osVersion && osVersion.length === 4) { + buildNumber = parseInt(osVersion[3]); + } + return buildNumber; +} + +/** + * Converts a path to a path on WSL using the wslpath utility. + * @param path The original path. + */ +export function getWslPath(path: string): Promise { + if (getWindowsBuildNumber() < 17063) { + throw new Error('wslpath does not exist on Windows build < 17063'); + } + return new Promise(c => { + execFile('bash.exe', ['-c', 'echo $(wslpath ' + escapeNonWindowsPath(path) + ')'], {}, (error, stdout, stderr) => { + c(escapeNonWindowsPath(stdout.trim())); + }); + }); +} diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts index 6153a741193d7..58a8af973ecf8 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/node/terminalEnvironment.test.ts @@ -128,14 +128,4 @@ suite('Workbench - TerminalEnvironment', () => { assertPathsMatch(terminalEnvironment.getCwd({ executable: undefined, args: [], ignoreConfigurationCwd: true }, '/userHome/', Uri.file('/bar'), '/foo'), '/bar'); }); }); - - test('preparePathForTerminal', () => { - if (platform.isWindows) { - assert.equal(terminalEnvironment.preparePathForTerminal('C:\\foo'), 'C:\\foo'); - assert.equal(terminalEnvironment.preparePathForTerminal('C:\\foo bar'), '"C:\\foo bar"'); - return; - } - assert.equal(terminalEnvironment.preparePathForTerminal('/a/\\foo bar"\'? ;\'?? :'), '/a/\\\\foo\\ bar\\"\\\'\\?\\ \\;\\\'\\?\\?\\ \\ \\:'); - assert.equal(terminalEnvironment.preparePathForTerminal('/\\\'"?:;!*(){}[]'), '/\\\\\\\'\\"\\?\\:\\;\\!\\*\\(\\)\\{\\}\\[\\]'); - }); }); From 877f0905acceb2299720d640bd781f624688aa0c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 19:20:07 -0800 Subject: [PATCH 13/20] Move terminalProcessManager to node --- src/tsconfig.strictNullChecks.json | 2 +- .../contrib/terminal/electron-browser/terminalInstance.ts | 2 +- .../{electron-browser => node}/terminalProcessManager.ts | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/vs/workbench/contrib/terminal/{electron-browser => node}/terminalProcessManager.ts (100%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index e3974e6fdcc0b..4db865ea10a93 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -294,10 +294,10 @@ "./vs/workbench/contrib/terminal/common/terminalMenu.ts", "./vs/workbench/contrib/terminal/common/terminalService.ts", "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", "./vs/workbench/contrib/terminal/node/terminalProcess.ts", "./vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts", + "./vs/workbench/contrib/terminal/node/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/windowsShellHelper.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts", diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts index 7fe8f6286f0a0..62242b6373e49 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts @@ -30,7 +30,7 @@ import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGR import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; -import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/electron-browser/terminalProcessManager'; +import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/node/terminalProcessManager'; import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker'; import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalProcessManager.ts rename to src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts From 23fbca34b08707f2d050c3a0326acd0808415679 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 19:51:18 -0800 Subject: [PATCH 14/20] Break node dependency in TerminalInstance --- .../contrib/terminal/browser/terminal.ts | 18 +++++ .../terminal/browser/terminalActions.ts | 2 +- .../contrib/terminal/browser/terminalPanel.ts | 2 +- .../contrib/terminal/common/terminal.ts | 30 +++++---- .../terminal/common/terminalService.ts | 1 + .../electron-browser/terminal.contribution.ts | 3 + .../electron-browser/terminalInstance.ts | 66 ++----------------- .../terminalInstanceService.ts | 50 ++++++++++++++ .../electron-browser/terminalService.ts | 42 +++++++++++- .../terminal/node/windowsShellHelper.ts | 4 +- 10 files changed, 141 insertions(+), 77 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminal.ts create mode 100644 src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts new file mode 100644 index 0000000000000..6fec000ba2df3 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Terminal as XTermTerminal } from 'vscode-xterm'; +import { ITerminalInstance, IWindowsShellHelper, ITerminalProcessManager, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ITerminalInstanceService = createDecorator('terminalInstanceService'); + +export interface ITerminalInstanceService { + _serviceBrand: any; + + getXtermConstructor(): Promise; + createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper; + createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index ea11c54029cab..663829a2683dc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -696,7 +696,7 @@ export class RunActiveFileInTerminalAction extends Action { return Promise.resolve(undefined); } - return instance.preparePathForTerminalAsync(uri.fsPath).then(path => { + return this.terminalService.preparePathForTerminalAsync(uri.fsPath, instance.shellLaunchConfig.executable, instance.title).then(path => { instance.sendText(path, true); return this.terminalService.showPanel(); }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts index 6731067284784..b248acd1af392 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalPanel.ts @@ -302,7 +302,7 @@ export class TerminalPanel extends Panel { const terminal = this._terminalService.getActiveInstance(); if (terminal) { - return terminal.preparePathForTerminalAsync(path).then(preparedPath => { + return this._terminalService.preparePathForTerminalAsync(path, terminal.shellLaunchConfig.executable, terminal.title).then(preparedPath => { terminal.sendText(preparedPath, false); }); } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index a31e4c2c714ef..c10fdf3a012b0 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -13,8 +13,6 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState'; export const TERMINAL_PANEL_ID = 'workbench.panel.terminal'; -export const TERMINAL_SERVICE_ID = 'terminalService'; - /** A context key that is set when there is at least one opened integrated terminal. */ export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); /** A context key that is set when the integrated terminal has focus. */ @@ -47,7 +45,7 @@ export const NEVER_MEASURE_RENDER_TIME_STORAGE_KEY = 'terminal.integrated.neverM // trying to create the corressponding object on the ext host. export const EXT_HOST_CREATION_DELAY = 100; -export const ITerminalService = createDecorator(TERMINAL_SERVICE_ID); +export const ITerminalService = createDecorator('terminalService'); export const TerminalCursorStyle = { BLOCK: 'block', @@ -255,6 +253,17 @@ export interface ITerminalService { selectDefaultWindowsShell(): Promise; setWorkspaceShellAllowed(isAllowed: boolean): void; + /** + * Takes a path and returns the properly escaped path to send to the terminal. + * On Windows, this included trying to prepare the path for WSL if needed. + * + * @param executable The executable off the shellLaunchConfig + * @param title The terminal's title + * @param path The path to be escaped and formatted. + * @returns An escaped version of the path to be execuded in the terminal. + */ + preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise; + requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; } @@ -525,15 +534,6 @@ export interface ITerminalInstance { */ sendText(text: string, addNewLine: boolean): void; - /** - * Takes a path and returns the properly escaped path to send to the terminal. - * On Windows, this included trying to prepare the path for WSL if needed. - * - * @param path The path to be escaped and formatted. - * @returns An escaped version of the path to be execuded in the terminal. - */ - preparePathForTerminalAsync(path: string): Promise; - /** * Write text directly to the terminal, skipping the process if it exists. * @param text The text to write. @@ -693,4 +693,8 @@ export enum LinuxDistro { Fedora, Ubuntu, Unknown -} \ No newline at end of file +} + +export interface IWindowsShellHelper extends IDisposable { + getShellName(): Promise; +} diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 3d3a3baac49c1..859f4208b955d 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -101,6 +101,7 @@ export abstract class TerminalService implements ITerminalService { public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; public abstract selectDefaultWindowsShell(): Promise; public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; + public abstract preparePathForTerminalAsync(path: string, executable: string, title: string): Promise; public createTerminalRenderer(name: string): ITerminalInstance { return this.createTerminal({ name, isRendererOnly: true }); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index e278b7ee47ec8..a3e9ce8ad5f39 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -33,6 +33,8 @@ import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/ter import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstance'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstanceService'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); @@ -291,6 +293,7 @@ configurationRegistry.registerConfiguration({ }); registerSingleton(ITerminalService, TerminalService, true); +registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); (Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( TerminalPanel, diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts index 62242b6373e49..08845402186d3 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts @@ -25,19 +25,16 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager'; -import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; -import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/node/terminalProcessManager'; import { TerminalCommandTracker } from 'vs/workbench/contrib/terminal/browser/terminalCommandTracker'; -import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ISearchOptions, Terminal as XTermTerminal } from 'vscode-xterm'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -import { getWslPath, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -151,8 +148,6 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ 'workbench.action.toggleMaximizedPanel' ]; -let Terminal: typeof XTermTerminal; - export class TerminalInstance implements ITerminalInstance { private static readonly EOL_REGEX = /\r?\n/g; @@ -175,7 +170,7 @@ export class TerminalInstance implements ITerminalInstance { private _cols: number; private _rows: number; private _dimensionsOverride: ITerminalDimensions; - private _windowsShellHelper: WindowsShellHelper | undefined; + private _windowsShellHelper: IWindowsShellHelper | undefined; private _xtermReadyPromise: Promise; private _titleReadyPromise: Promise; private _titleReadyComplete: (title: string) => any; @@ -240,6 +235,7 @@ export class TerminalInstance implements ITerminalInstance { private readonly _configHelper: TerminalConfigHelper, private _container: HTMLElement, private _shellLaunchConfig: IShellLaunchConfig, + @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @INotificationService private readonly _notificationService: INotificationService, @@ -398,17 +394,7 @@ export class TerminalInstance implements ITerminalInstance { * Create xterm.js instance and attach data listeners. */ protected async _createXterm(): Promise { - if (!Terminal) { - Terminal = (await import('vscode-xterm')).Terminal; - // Enable xterm.js addons - Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search')); - Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks')); - Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/winptyCompat/winptyCompat')); - // Localize strings - Terminal.strings.blankLine = nls.localize('terminal.integrated.a11yBlankLine', 'Blank line'); - Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); - Terminal.strings.tooMuchOutput = nls.localize('terminal.integrated.a11yTooMuchOutput', 'Too much output to announce, navigate to rows manually to read'); - } + const Terminal = await this._terminalInstanceService.getXtermConstructor(); const font = this._configHelper.getFont(undefined, true); const config = this._configHelper.config; this._xterm = new Terminal({ @@ -805,44 +791,6 @@ export class TerminalInstance implements ITerminalInstance { } } - public preparePathForTerminalAsync(originalPath: string): Promise { - return new Promise(c => { - const exe = this.shellLaunchConfig.executable; - if (!exe) { - c(originalPath); - return; - } - - const hasSpace = originalPath.indexOf(' ') !== -1; - - const pathBasename = path.basename(exe, '.exe'); - const isPowerShell = pathBasename === 'pwsh' || - this.title === 'pwsh' || - pathBasename === 'powershell' || - this.title === 'powershell'; - - if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { - c(`& '${originalPath.replace(/'/g, '\'\'')}'`); - return; - } - - if (platform.isWindows) { - // 17063 is the build number where wsl path was introduced. - // Update Windows uriPath to be executed in WSL. - if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (getWindowsBuildNumber() >= 17063)) { - c(getWslPath(originalPath)); - return; - } else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - return; - } - c(escapeNonWindowsPath(originalPath)); - }); - } - public setVisible(visible: boolean): void { this._isVisible = visible; if (this._wrapperElement) { @@ -907,7 +855,7 @@ export class TerminalInstance implements ITerminalInstance { } protected _createProcess(): void { - this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper); + this._processManager = this._terminalInstanceService.createTerminalProcessManager(this._id, this._configHelper); this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this)); this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)); this._processManager.onProcessData(data => this._onData.fire(data)); @@ -924,7 +872,7 @@ export class TerminalInstance implements ITerminalInstance { this._processManager.ptyProcessReady.then(() => { this._xtermReadyPromise.then(() => { if (!this._isDisposed) { - this._windowsShellHelper = new WindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm); + this._terminalInstanceService.createWindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm); } }); }); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts new file mode 100644 index 0000000000000..727e5ab620150 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { Terminal as XTermTerminal } from 'vscode-xterm'; +import { ITerminalInstance, IWindowsShellHelper, ITerminalConfigHelper, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/node/terminalProcessManager'; + +let Terminal: typeof XTermTerminal; + +/** + * A service used by TerminalInstance that allows it to break its dependency on electron-browser and + * node layers, while at the same time avoiding a cyclic dependency on ITerminalService. + */ +export class TerminalInstanceService implements ITerminalInstanceService { + public _serviceBrand: any; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + } + + async getXtermConstructor(): Promise { + if (!Terminal) { + Terminal = (await import('vscode-xterm')).Terminal; + // Enable xterm.js addons + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/search/search')); + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/webLinks/webLinks')); + Terminal.applyAddon(require.__$__nodeRequire('vscode-xterm/lib/addons/winptyCompat/winptyCompat')); + // Localize strings + Terminal.strings.blankLine = nls.localize('terminal.integrated.a11yBlankLine', 'Blank line'); + Terminal.strings.promptLabel = nls.localize('terminal.integrated.a11yPromptLabel', 'Terminal input'); + Terminal.strings.tooMuchOutput = nls.localize('terminal.integrated.a11yTooMuchOutput', 'Too much output to announce, navigate to rows manually to read'); + } + return Terminal; + } + + createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper { + return new WindowsShellHelper(shellProcessId, instance, xterm); + } + + createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager { + return this._instantiationService.createInstance(TerminalProcessManager, id, configHelper); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index 86451a2b64fe6..44011e664d2c9 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -16,7 +16,7 @@ import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfi import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { getDefaultShell, linuxDistro, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getDefaultShell, linuxDistro, getWindowsBuildNumber, getWslPath } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ipcRenderer as ipc } from 'electron'; @@ -26,6 +26,8 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; import { coalesce } from 'vs/base/common/arrays'; import { IFileService } from 'vs/platform/files/common/files'; +import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { basename } from 'vs/base/common/path'; export class TerminalService extends BrowserTerminalService implements ITerminalService { private _configHelper: TerminalConfigHelper; @@ -143,4 +145,42 @@ export class TerminalService extends BrowserTerminalService implements ITerminal this._terminalContainer = terminalContainer; this._terminalTabs.forEach(tab => tab.attachToElement(this._terminalContainer)); } + + public preparePathForTerminalAsync(originalPath: string, executable: string, title: string): Promise { + return new Promise(c => { + const exe = executable; + if (!exe) { + c(originalPath); + return; + } + + const hasSpace = originalPath.indexOf(' ') !== -1; + + const pathBasename = basename(exe, '.exe'); + const isPowerShell = pathBasename === 'pwsh' || + title === 'pwsh' || + pathBasename === 'powershell' || + title === 'powershell'; + + if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { + c(`& '${originalPath.replace(/'/g, '\'\'')}'`); + return; + } + + if (platform.isWindows) { + // 17063 is the build number where wsl path was introduced. + // Update Windows uriPath to be executed in WSL. + if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (getWindowsBuildNumber() >= 17063)) { + c(getWslPath(originalPath)); + return; + } else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + return; + } + c(escapeNonWindowsPath(originalPath)); + }); + } } diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts index 80233f87d4de8..d841d02369590 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts @@ -5,7 +5,7 @@ import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { ITerminalInstance } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { Terminal as XTermTerminal } from 'vscode-xterm'; import WindowsProcessTreeType = require('windows-process-tree'); @@ -24,7 +24,7 @@ const SHELL_EXECUTABLES = [ let windowsProcessTree: typeof WindowsProcessTreeType; -export class WindowsShellHelper { +export class WindowsShellHelper implements IWindowsShellHelper { private _onCheckShell: Emitter | undefined>; private _isDisposed: boolean; private _currentRequest: Promise | null; From b5a06ebb62ba322549cf2c1f1c318a6d69ebeefa Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 19:52:20 -0800 Subject: [PATCH 15/20] Move terminalInstance, electron-browser->browser Part of #69115 --- src/tsconfig.strictNullChecks.json | 2 +- .../terminal/{electron-browser => browser}/terminalInstance.ts | 0 .../contrib/terminal/electron-browser/terminal.contribution.ts | 2 +- .../contrib/terminal/electron-browser/terminalService.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/workbench/contrib/terminal/{electron-browser => browser}/terminalInstance.ts (100%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 4db865ea10a93..b1be79b7e9fe3 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -282,6 +282,7 @@ "./vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts", "./vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts", "./vs/workbench/contrib/terminal/browser/terminalFindWidget.ts", + "./vs/workbench/contrib/terminal/browser/terminalInstance.ts", "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", "./vs/workbench/contrib/terminal/browser/terminalPanel.ts", "./vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts", @@ -293,7 +294,6 @@ "./vs/workbench/contrib/terminal/common/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/common/terminalMenu.ts", "./vs/workbench/contrib/terminal/common/terminalService.ts", - "./vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", "./vs/workbench/contrib/terminal/node/terminalProcess.ts", "./vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts", diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts similarity index 100% rename from src/vs/workbench/contrib/terminal/electron-browser/terminalInstance.ts rename to src/vs/workbench/contrib/terminal/browser/terminalInstance.ts diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index a3e9ce8ad5f39..d4cec6691ce75 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -32,7 +32,7 @@ import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPan import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; -import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstance'; +import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstanceService'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index 44011e664d2c9..2db2dcb327223 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -21,7 +21,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ipcRenderer as ipc } from 'electron'; import { IOpenFileRequest, IWindowService } from 'vs/platform/windows/common/windows'; -import { TerminalInstance } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstance'; +import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; import { coalesce } from 'vs/base/common/arrays'; From 03f23ddbc6fb37fcaba2877281c2593d05f24065 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 19:55:42 -0800 Subject: [PATCH 16/20] Remove duplicate getWindowsBuildNumber definitions --- .../tasks/electron-browser/terminalTaskSystem.ts | 13 ++----------- .../contrib/terminal/node/terminalProcess.ts | 13 ++----------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts index bfaaf9c091ad8..025c9479cab0a 100644 --- a/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts @@ -9,7 +9,6 @@ import * as Objects from 'vs/base/common/objects'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import * as Async from 'vs/base/common/async'; -import * as os from 'os'; import { IStringDictionary, values } from 'vs/base/common/collections'; import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; @@ -42,6 +41,7 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { URI } from 'vs/base/common/uri'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { Schemas } from 'vs/base/common/network'; +import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; interface TerminalData { terminal: ITerminalInstance; @@ -745,7 +745,7 @@ export class TerminalTaskSystem implements ITaskSystem { if (!shellSpecified) { toAdd.push('-Command'); } - } else if ((basename === 'bash.exe') || (basename === 'zsh.exe') || ((basename === 'wsl.exe') && (this.getWindowsBuildNumber() < 17763))) { // See https://github.com/Microsoft/vscode/issues/67855 + } else if ((basename === 'bash.exe') || (basename === 'zsh.exe') || ((basename === 'wsl.exe') && (getWindowsBuildNumber() < 17763))) { // See https://github.com/Microsoft/vscode/issues/67855 windowsShellArgs = false; if (!shellSpecified) { toAdd.push('-c'); @@ -1213,15 +1213,6 @@ export class TerminalTaskSystem implements ITaskSystem { return result; } - private getWindowsBuildNumber(): number { - const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); - let buildNumber: number = 0; - if (osVersion && osVersion.length === 4) { - buildNumber = parseInt(osVersion[3]); - } - return buildNumber; - } - private registerLinkMatchers(terminal: ITerminalInstance, problemMatchers: ProblemMatcher[]): number[] { let result: number[] = []; /* diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index fbebae679ee2b..4aa6a76b158d3 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -9,7 +9,7 @@ import * as platform from 'vs/base/common/platform'; import * as pty from 'node-pty'; import * as fs from 'fs'; import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalChildProcess } from 'vs/workbench/contrib/terminal/node/terminal'; +import { ITerminalChildProcess, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; import { exec } from 'child_process'; @@ -51,7 +51,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { } this._initialCwd = cwd; - const useConpty = windowsEnableConpty && process.platform === 'win32' && this._getWindowsBuildNumber() >= 18309; + const useConpty = windowsEnableConpty && process.platform === 'win32' && getWindowsBuildNumber() >= 18309; const options: pty.IPtyForkOptions = { name: shellName, cwd, @@ -106,15 +106,6 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { this._onProcessTitleChanged.dispose(); } - private _getWindowsBuildNumber(): number { - const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); - let buildNumber: number = 0; - if (osVersion && osVersion.length === 4) { - buildNumber = parseInt(osVersion[3]); - } - return buildNumber; - } - private _setupTitlePolling() { // Send initial timeout async to give event listeners a chance to init setTimeout(() => { From 6d5ab922e68f689e05b39556ef9fbb1220f21db6 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 19:59:25 -0800 Subject: [PATCH 17/20] Move terminalProcessExtHostProxy, node->common --- src/tsconfig.strictNullChecks.json | 2 +- .../contrib/terminal/common/terminal.ts | 24 ++++++++++++++++++ .../terminalProcessExtHostProxy.ts | 3 +-- .../contrib/terminal/node/terminal.ts | 25 ------------------- .../contrib/terminal/node/terminalProcess.ts | 4 +-- .../terminal/node/terminalProcessManager.ts | 5 ++-- 6 files changed, 30 insertions(+), 33 deletions(-) rename src/vs/workbench/contrib/terminal/{node => common}/terminalProcessExtHostProxy.ts (96%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index b1be79b7e9fe3..13f40695a3c44 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -293,10 +293,10 @@ "./vs/workbench/contrib/terminal/common/terminalCommands.ts", "./vs/workbench/contrib/terminal/common/terminalEnvironment.ts", "./vs/workbench/contrib/terminal/common/terminalMenu.ts", + "./vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts", "./vs/workbench/contrib/terminal/common/terminalService.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", "./vs/workbench/contrib/terminal/node/terminalProcess.ts", - "./vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts", "./vs/workbench/contrib/terminal/node/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/windowsShellHelper.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts", diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index c10fdf3a012b0..ec0303a0ea3eb 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -698,3 +698,27 @@ export enum LinuxDistro { export interface IWindowsShellHelper extends IDisposable { getShellName(): Promise; } + +/** + * An interface representing a raw terminal child process, this contains a subset of the + * child_process.ChildProcess node.js interface. + */ +export interface ITerminalChildProcess { + onProcessData: Event; + onProcessExit: Event; + onProcessIdReady: Event; + onProcessTitleChanged: Event; + + /** + * Shutdown the terminal process. + * + * @param immediate When true the process will be killed immediately, otherwise the process will + * be given some time to make sure no additional data comes through. + */ + shutdown(immediate: boolean): void; + input(data: string): void; + resize(cols: number, rows: number): void; + + getInitialCwd(): Promise; + getCwd(): Promise; +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts similarity index 96% rename from src/vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts rename to src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts index 5eb61629e4898..84131aa9eddaa 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITerminalChildProcess } from 'vs/workbench/contrib/terminal/node/terminal'; import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index a7b7667302319..6200b9bf734d7 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -7,35 +7,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 { Event } from 'vs/base/common/event'; import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import { execFile } from 'child_process'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -/** - * An interface representing a raw terminal child process, this contains a subset of the - * child_process.ChildProcess node.js interface. - */ -export interface ITerminalChildProcess { - onProcessData: Event; - onProcessExit: Event; - onProcessIdReady: Event; - onProcessTitleChanged: Event; - - /** - * Shutdown the terminal process. - * - * @param immediate When true the process will be killed immediately, otherwise the process will - * be given some time to make sure no additional data comes through. - */ - shutdown(immediate: boolean): void; - input(data: string): void; - resize(cols: number, rows: number): void; - - getInitialCwd(): Promise; - getCwd(): Promise; -} - export function getDefaultShell(p: platform.Platform): string { if (p === platform.Platform.Windows) { if (platform.isWindows) { diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index 4aa6a76b158d3..3d40e7ed80445 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -9,9 +9,9 @@ import * as platform from 'vs/base/common/platform'; import * as pty from 'node-pty'; import * as fs from 'fs'; import { Event, Emitter } from 'vs/base/common/event'; -import { ITerminalChildProcess, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { exec } from 'child_process'; export class TerminalProcess implements ITerminalChildProcess, IDisposable { diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts index 0a9923779e8f5..d46fcf801fc28 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts @@ -6,12 +6,11 @@ import * as platform from 'vs/base/common/platform'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { ILogService } from 'vs/platform/log/common/log'; import { Emitter, Event } from 'vs/base/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { ITerminalChildProcess } from 'vs/workbench/contrib/terminal/node/terminal'; -import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/node/terminalProcessExtHostProxy'; +import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; From 437f7f67cb1f8d867a82445e3bdf97f545222675 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 20:10:07 -0800 Subject: [PATCH 18/20] Move TerminalProcessManager, node->browser --- src/tsconfig.strictNullChecks.json | 2 +- src/vs/base/common/processes.ts | 29 ++++++++++++++++ src/vs/base/node/processes.ts | 27 --------------- src/vs/base/test/common/processes.test.ts | 33 +++++++++++++++++++ .../test/node/processes/processes.test.ts | 25 -------------- .../api/node/extHostTerminalService.ts | 2 +- .../contrib/terminal/browser/terminal.ts | 4 ++- .../terminalProcessManager.ts | 9 ++--- .../terminalInstanceService.ts | 21 ++++++++---- 9 files changed, 86 insertions(+), 66 deletions(-) create mode 100644 src/vs/base/test/common/processes.test.ts rename src/vs/workbench/contrib/terminal/{node => browser}/terminalProcessManager.ts (96%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 13f40695a3c44..8d70756d2f6cd 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -285,6 +285,7 @@ "./vs/workbench/contrib/terminal/browser/terminalInstance.ts", "./vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts", "./vs/workbench/contrib/terminal/browser/terminalPanel.ts", + "./vs/workbench/contrib/terminal/browser/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/browser/terminalQuickOpen.ts", "./vs/workbench/contrib/terminal/browser/terminalTab.ts", "./vs/workbench/contrib/terminal/browser/terminalWidgetManager.ts", @@ -297,7 +298,6 @@ "./vs/workbench/contrib/terminal/common/terminalService.ts", "./vs/workbench/contrib/terminal/node/terminal.ts", "./vs/workbench/contrib/terminal/node/terminalProcess.ts", - "./vs/workbench/contrib/terminal/node/terminalProcessManager.ts", "./vs/workbench/contrib/terminal/node/windowsShellHelper.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalColorRegistry.test.ts", "./vs/workbench/contrib/terminal/test/electron-browser/terminalCommandTracker.test.ts", diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts index 7eef74d894f0e..b42d8e25605ea 100644 --- a/src/vs/base/common/processes.ts +++ b/src/vs/base/common/processes.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IProcessEnvironment } from 'vs/base/common/platform'; + /** * Options to be passed to the external program or shell. */ @@ -84,3 +86,30 @@ export const enum TerminateResponseCode { AccessDenied = 2, ProcessNotFound = 3, } + +/** + * Sanitizes a VS Code process environment by removing all Electron/VS Code-related values. + */ +export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve: string[]): void { + const set = preserve.reduce((set, key) => { + set[key] = true; + return set; + }, {} as Record); + const keysToRemove = [ + /^ELECTRON_.+$/, + /^GOOGLE_API_KEY$/, + /^VSCODE_.+$/, + /^SNAP(|_.*)$/ + ]; + const envKeys = Object.keys(env); + envKeys + .filter(key => !set[key]) + .forEach(envKey => { + for (let i = 0; i < keysToRemove.length; i++) { + if (envKey.search(keysToRemove[i]) !== -1) { + delete env[envKey]; + break; + } + } + }); +} diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index dfaa90424991b..9836e6ccfbb5a 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -72,33 +72,6 @@ export function getWindowsShell(): string { return process.env['comspec'] || 'cmd.exe'; } -/** - * Sanitizes a VS Code process environment by removing all Electron/VS Code-related values. - */ -export function sanitizeProcessEnvironment(env: Platform.IProcessEnvironment, ...preserve: string[]): void { - const set = preserve.reduce((set, key) => { - set[key] = true; - return set; - }, {} as Record); - const keysToRemove = [ - /^ELECTRON_.+$/, - /^GOOGLE_API_KEY$/, - /^VSCODE_.+$/, - /^SNAP(|_.*)$/ - ]; - const envKeys = Object.keys(env); - envKeys - .filter(key => !set[key]) - .forEach(envKey => { - for (let i = 0; i < keysToRemove.length; i++) { - if (envKey.search(keysToRemove[i]) !== -1) { - delete env[envKey]; - break; - } - } - }); -} - export abstract class AbstractProcess { private cmd: string; private args: string[]; diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts new file mode 100644 index 0000000000000..cd54b6fd7241d --- /dev/null +++ b/src/vs/base/test/common/processes.test.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as processes from 'vs/base/common/processes'; + +suite('Processes', () => { + test('sanitizeProcessEnvironment', () => { + let env = { + FOO: 'bar', + ELECTRON_ENABLE_STACK_DUMPING: 'x', + ELECTRON_ENABLE_LOGGING: 'x', + ELECTRON_NO_ASAR: 'x', + ELECTRON_NO_ATTACH_CONSOLE: 'x', + ELECTRON_RUN_AS_NODE: 'x', + GOOGLE_API_KEY: 'x', + VSCODE_CLI: 'x', + VSCODE_DEV: 'x', + VSCODE_IPC_HOOK: 'x', + VSCODE_LOGS: 'x', + VSCODE_NLS_CONFIG: 'x', + VSCODE_PORTABLE: 'x', + VSCODE_PID: 'x', + VSCODE_NODE_CACHED_DATA_DIR: 'x', + VSCODE_NEW_VAR: 'x' + }; + processes.sanitizeProcessEnvironment(env); + assert.equal(env['FOO'], 'bar'); + assert.equal(Object.keys(env).length, 1); + }); +}); diff --git a/src/vs/base/test/node/processes/processes.test.ts b/src/vs/base/test/node/processes/processes.test.ts index 10199cf5bf38c..76719506d6e61 100644 --- a/src/vs/base/test/node/processes/processes.test.ts +++ b/src/vs/base/test/node/processes/processes.test.ts @@ -84,29 +84,4 @@ suite('Processes', () => { } }); }); - - - test('sanitizeProcessEnvironment', () => { - let env = { - FOO: 'bar', - ELECTRON_ENABLE_STACK_DUMPING: 'x', - ELECTRON_ENABLE_LOGGING: 'x', - ELECTRON_NO_ASAR: 'x', - ELECTRON_NO_ATTACH_CONSOLE: 'x', - ELECTRON_RUN_AS_NODE: 'x', - GOOGLE_API_KEY: 'x', - VSCODE_CLI: 'x', - VSCODE_DEV: 'x', - VSCODE_IPC_HOOK: 'x', - VSCODE_LOGS: 'x', - VSCODE_NLS_CONFIG: 'x', - VSCODE_PORTABLE: 'x', - VSCODE_PID: 'x', - VSCODE_NODE_CACHED_DATA_DIR: 'x', - VSCODE_NEW_VAR: 'x' - }; - processes.sanitizeProcessEnvironment(env); - assert.equal(env['FOO'], 'bar'); - assert.equal(Object.keys(env).length, 1); - }); }); diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 6f80fc77c38a4..070d8874c2265 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -16,7 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { timeout } from 'vs/base/common/async'; -import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; +import { sanitizeProcessEnvironment } from 'vs/base/common/processes'; const RENDERER_NO_PROCESS_ID = -1; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 6fec000ba2df3..23114676b6474 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { Terminal as XTermTerminal } from 'vscode-xterm'; -import { ITerminalInstance, IWindowsShellHelper, ITerminalProcessManager, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, IWindowsShellHelper, ITerminalProcessManager, ITerminalConfigHelper, ITerminalChildProcess, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IProcessEnvironment } from 'vs/base/common/platform'; export const ITerminalInstanceService = createDecorator('terminalInstanceService'); @@ -15,4 +16,5 @@ export interface ITerminalInstanceService { getXtermConstructor(): Promise; createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper; createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager; + createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess; } diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts similarity index 96% rename from src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts rename to src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index d46fcf801fc28..18d6d58d78b93 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -12,16 +12,16 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; -import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; +import { sanitizeProcessEnvironment } from 'vs/base/common/processes'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IProductService } from 'vs/platform/product/common/product'; +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -63,7 +63,8 @@ export class TerminalProcessManager implements ITerminalProcessManager { @IWindowService private readonly _windowService: IWindowService, @IWorkspaceConfigurationService private readonly _workspaceConfigurationService: IWorkspaceConfigurationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IProductService private readonly _productService: IProductService + @IProductService private readonly _productService: IProductService, + @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService ) { this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { @@ -148,7 +149,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { } this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env); - this._process = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty); + this._process = this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty); } this.processState = ProcessState.LAUNCHING; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index 727e5ab620150..d9d128e5f9205 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -6,16 +6,19 @@ import * as nls from 'vs/nls'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { Terminal as XTermTerminal } from 'vscode-xterm'; -import { ITerminalInstance, IWindowsShellHelper, ITerminalConfigHelper, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalInstance, IWindowsShellHelper, ITerminalConfigHelper, ITerminalProcessManager, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/node/terminalProcessManager'; +import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; let Terminal: typeof XTermTerminal; /** - * A service used by TerminalInstance that allows it to break its dependency on electron-browser and - * node layers, while at the same time avoiding a cyclic dependency on ITerminalService. + * A service used by TerminalInstance (and components owned by it) that allows it to break its + * dependency on electron-browser and node layers, while at the same time avoiding a cyclic + * dependency on ITerminalService. */ export class TerminalInstanceService implements ITerminalInstanceService { public _serviceBrand: any; @@ -25,7 +28,7 @@ export class TerminalInstanceService implements ITerminalInstanceService { ) { } - async getXtermConstructor(): Promise { + public async getXtermConstructor(): Promise { if (!Terminal) { Terminal = (await import('vscode-xterm')).Terminal; // Enable xterm.js addons @@ -40,11 +43,15 @@ export class TerminalInstanceService implements ITerminalInstanceService { return Terminal; } - createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper { + public createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper { return new WindowsShellHelper(shellProcessId, instance, xterm); } - createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager { + public createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager { return this._instantiationService.createInstance(TerminalProcessManager, id, configHelper); } + + public createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess { + return new TerminalProcess(shellLaunchConfig, cwd, cols, rows, env, windowsEnableConpty); + } } \ No newline at end of file From ff5ae22403cdf705734d7822c89affca78564688 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 20:22:09 -0800 Subject: [PATCH 19/20] Remove more from electron-browser/terminalService --- .../contrib/terminal/browser/terminal.ts | 4 ++ .../terminal/browser/terminalConfigHelper.ts | 5 +- .../terminal/browser/terminalService.ts | 19 +++++- .../terminal/common/terminalService.ts | 45 ++++++++++++- .../electron-browser/terminalService.ts | 67 +++++-------------- .../contrib/terminal/node/terminal.ts | 17 ----- 6 files changed, 85 insertions(+), 72 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 23114676b6474..ff7f6b95f5b6f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -18,3 +18,7 @@ export interface ITerminalInstanceService { createTerminalProcessManager(id: number, configHelper: ITerminalConfigHelper): ITerminalProcessManager; createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess; } + +export interface IBrowserTerminalConfigHelper extends ITerminalConfigHelper { + panelContainer: HTMLElement; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 91a780466ad60..f4f0476caa015 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -10,10 +10,11 @@ import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/ed import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ITerminalConfiguration, ITerminalConfigHelper, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalConfiguration, ITerminalFont, IShellLaunchConfig, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, TERMINAL_CONFIG_SECTION, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, MINIMUM_LETTER_SPACING, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; import Severity from 'vs/base/common/severity'; import { Terminal as XTermTerminal } from 'vscode-xterm'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; const MINIMUM_FONT_SIZE = 6; const MAXIMUM_FONT_SIZE = 25; @@ -22,7 +23,7 @@ const MAXIMUM_FONT_SIZE = 25; * Encapsulates terminal configuration logic, the primary purpose of this file is so that platform * specific test cases can be written. */ -export class TerminalConfigHelper implements ITerminalConfigHelper { +export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { public panelContainer: HTMLElement; private _charMeasureElement: HTMLElement; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 051667c8a6736..28a01868122c1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -5,9 +5,9 @@ import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, IShellLaunchConfig, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, IShellLaunchConfig, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalService as CommonTerminalService } from 'vs/workbench/contrib/terminal/common/terminalService'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -20,8 +20,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; +import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; +import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; export abstract class TerminalService extends CommonTerminalService implements ITerminalService { + protected _configHelper: IBrowserTerminalConfigHelper; constructor( @IContextKeyService contextKeyService: IContextKeyService, @@ -41,6 +44,12 @@ export abstract class TerminalService extends CommonTerminalService implements I protected abstract _getDefaultShell(p: platform.Platform): string; + public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { + const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); + this._onInstanceCreated.fire(instance); + return instance; + } + public createTerminal(shell: IShellLaunchConfig = {}, wasNewTerminalAction?: boolean): ITerminalInstance { const terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalFocusContextKey, @@ -157,4 +166,10 @@ export abstract class TerminalService extends CommonTerminalService implements I panel.getFindWidget().find(true); } } + + public setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void { + this._configHelper.panelContainer = panelContainer; + this._terminalContainer = terminalContainer; + this._terminalTabs.forEach(tab => tab.attachToElement(this._terminalContainer)); + } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/common/terminalService.ts b/src/vs/workbench/contrib/terminal/common/terminalService.ts index 859f4208b955d..b2d81641a844c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalService.ts @@ -17,6 +17,9 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; +import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { isWindows } from 'vs/base/common/platform'; +import { basename } from 'vs/base/common/path'; export abstract class TerminalService implements ITerminalService { public _serviceBrand: any; @@ -97,11 +100,13 @@ export abstract class TerminalService implements ITerminalService { this.onInstancesChanged(() => updateTerminalContextKeys()); } + protected abstract _getWslPath(path: string): Promise; + protected abstract _getWindowsBuildNumber(): number; + public abstract createTerminal(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance; public abstract createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance; public abstract selectDefaultWindowsShell(): Promise; public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; - public abstract preparePathForTerminalAsync(path: string, executable: string, title: string): Promise; public createTerminalRenderer(name: string): ITerminalInstance { return this.createTerminal({ name, isRendererOnly: true }); @@ -425,4 +430,42 @@ export abstract class TerminalService implements ITerminalService { return [label, current] as [string, string]; }); } + + public preparePathForTerminalAsync(originalPath: string, executable: string, title: string): Promise { + return new Promise(c => { + const exe = executable; + if (!exe) { + c(originalPath); + return; + } + + const hasSpace = originalPath.indexOf(' ') !== -1; + + const pathBasename = basename(exe, '.exe'); + const isPowerShell = pathBasename === 'pwsh' || + title === 'pwsh' || + pathBasename === 'powershell' || + title === 'powershell'; + + if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { + c(`& '${originalPath.replace(/'/g, '\'\'')}'`); + return; + } + + if (isWindows) { + // 17063 is the build number where wsl path was introduced. + // Update Windows uriPath to be executed in WSL. + if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (this._getWindowsBuildNumber() >= 17063)) { + c(this._getWslPath(originalPath)); + return; + } else if (hasSpace) { + c('"' + originalPath + '"'); + } else { + c(originalPath); + } + return; + } + c(escapeNonWindowsPath(originalPath)); + }); + } } diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts index 2db2dcb327223..34dc08eec10a9 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalService.ts @@ -6,31 +6,29 @@ import * as nls from 'vs/nls'; import * as pfs from 'vs/base/node/pfs'; import * as platform from 'vs/base/common/platform'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalService, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalService as BrowserTerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { getDefaultShell, linuxDistro, getWindowsBuildNumber, getWslPath } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getDefaultShell, linuxDistro, getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ipcRenderer as ipc } from 'electron'; import { IOpenFileRequest, IWindowService } from 'vs/platform/windows/common/windows'; -import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; import { coalesce } from 'vs/base/common/arrays'; import { IFileService } from 'vs/platform/files/common/files'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; -import { basename } from 'vs/base/common/path'; +import { execFile } from 'child_process'; export class TerminalService extends BrowserTerminalService implements ITerminalService { - private _configHelper: TerminalConfigHelper; public get configHelper(): ITerminalConfigHelper { return this._configHelper; } constructor( @@ -75,12 +73,6 @@ export class TerminalService extends BrowserTerminalService implements ITerminal }); } - public createInstance(terminalFocusContextKey: IContextKey, configHelper: ITerminalConfigHelper, container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig, doCreateProcess: boolean): ITerminalInstance { - const instance = this._instantiationService.createInstance(TerminalInstance, terminalFocusContextKey, configHelper, container, shellLaunchConfig); - this._onInstanceCreated.fire(instance); - return instance; - } - protected _getDefaultShell(p: platform.Platform): string { return getDefaultShell(p); } @@ -140,47 +132,22 @@ export class TerminalService extends BrowserTerminalService implements ITerminal }); } - public setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void { - this._configHelper.panelContainer = panelContainer; - this._terminalContainer = terminalContainer; - this._terminalTabs.forEach(tab => tab.attachToElement(this._terminalContainer)); + protected _getWindowsBuildNumber(): number { + return getWindowsBuildNumber(); } - public preparePathForTerminalAsync(originalPath: string, executable: string, title: string): Promise { + /** + * Converts a path to a path on WSL using the wslpath utility. + * @param path The original path. + */ + protected _getWslPath(path: string): Promise { + if (getWindowsBuildNumber() < 17063) { + throw new Error('wslpath does not exist on Windows build < 17063'); + } return new Promise(c => { - const exe = executable; - if (!exe) { - c(originalPath); - return; - } - - const hasSpace = originalPath.indexOf(' ') !== -1; - - const pathBasename = basename(exe, '.exe'); - const isPowerShell = pathBasename === 'pwsh' || - title === 'pwsh' || - pathBasename === 'powershell' || - title === 'powershell'; - - if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { - c(`& '${originalPath.replace(/'/g, '\'\'')}'`); - return; - } - - if (platform.isWindows) { - // 17063 is the build number where wsl path was introduced. - // Update Windows uriPath to be executed in WSL. - if (((exe.indexOf('wsl') !== -1) || ((exe.indexOf('bash.exe') !== -1) && (exe.indexOf('git') === -1))) && (getWindowsBuildNumber() >= 17063)) { - c(getWslPath(originalPath)); - return; - } else if (hasSpace) { - c('"' + originalPath + '"'); - } else { - c(originalPath); - } - return; - } - c(escapeNonWindowsPath(originalPath)); + execFile('bash.exe', ['-c', 'echo $(wslpath ' + escapeNonWindowsPath(path) + ')'], {}, (error, stdout, stderr) => { + c(escapeNonWindowsPath(stdout.trim())); + }); }); } } diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 6200b9bf734d7..493fad0b8332e 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -8,8 +8,6 @@ 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 { execFile } from 'child_process'; -import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; export function getDefaultShell(p: platform.Platform): string { if (p === platform.Platform.Windows) { @@ -84,18 +82,3 @@ export function getWindowsBuildNumber(): number { } return buildNumber; } - -/** - * Converts a path to a path on WSL using the wslpath utility. - * @param path The original path. - */ -export function getWslPath(path: string): Promise { - if (getWindowsBuildNumber() < 17063) { - throw new Error('wslpath does not exist on Windows build < 17063'); - } - return new Promise(c => { - execFile('bash.exe', ['-c', 'echo $(wslpath ' + escapeNonWindowsPath(path) + ')'], {}, (error, stdout, stderr) => { - c(escapeNonWindowsPath(stdout.trim())); - }); - }); -} From 3a10272f1e43a8c4548d7f31704595a3831b2e41 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Mar 2019 20:39:47 -0800 Subject: [PATCH 20/20] Create browser/terminal.contribution.ts --- .../terminal/browser/terminal.contribution.ts | 519 ++++++++++++++++++ .../terminal/browser/terminalFindWidget.ts | 1 - .../electron-browser/terminal.contribution.ts | 511 +---------------- src/vs/workbench/workbench.main.ts | 1 + 4 files changed, 526 insertions(+), 506 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts new file mode 100644 index 0000000000000..a420a5355b376 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -0,0 +1,519 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import * as platform from 'vs/base/common/platform'; +import 'vs/css!./media/scrollbar'; +import 'vs/css!./media/terminal'; +import 'vs/css!./media/widgets'; +import 'vs/css!./media/xterm'; +import * as nls from 'vs/nls'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionBarExtensions, IActionBarRegistry, Scope } from 'vs/workbench/browser/actions'; +import * as panel from 'vs/workbench/browser/panel'; +import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { AllowWorkspaceShellTerminalCommand, ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, DisallowWorkspaceShellTerminalCommand, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; +import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; +import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle } from 'vs/workbench/contrib/terminal/common/terminal'; +import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; +import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; +import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; + +const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); + +const inTerminalsPicker = 'inTerminalPicker'; + +quickOpenRegistry.registerQuickOpenHandler( + new QuickOpenHandlerDescriptor( + TerminalPickerHandler, + TerminalPickerHandler.ID, + TERMINAL_PICKER_PREFIX, + inTerminalsPicker, + nls.localize('quickOpen.terminal', "Show All Opened Terminals") + ) +); + +const quickOpenNavigateNextInTerminalPickerId = 'workbench.action.quickOpenNavigateNextInTerminalPicker'; +CommandsRegistry.registerCommand( + { id: quickOpenNavigateNextInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigateNextInTerminalPickerId, true) }); + +const quickOpenNavigatePreviousInTerminalPickerId = 'workbench.action.quickOpenNavigatePreviousInTerminalPicker'; +CommandsRegistry.registerCommand( + { id: quickOpenNavigatePreviousInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInTerminalPickerId, false) }); + + +const configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ + id: 'terminal', + order: 100, + title: nls.localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), + type: 'object', + properties: { + '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', + items: { + type: 'string' + }, + default: [] + }, + 'terminal.integrated.shellArgs.osx': { + markdownDescription: nls.localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'array', + items: { + type: 'string' + }, + // Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This + // is the reason terminals on macOS typically run login shells by default which set up + // the environment. See http://unix.stackexchange.com/a/119675/115410 + default: ['-l'] + }, + 'terminal.integrated.shellArgs.windows': { + markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + 'anyOf': [ + { + type: 'array', + items: { + type: 'string', + markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") + }, + }, + { + type: 'string', + markdownDescription: nls.localize('terminal.integrated.shellArgs.windows.string', "The command line arguments in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6) to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") + } + ], + default: [] + }, + 'terminal.integrated.macOptionIsMeta': { + description: nls.localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), + type: 'boolean', + default: false + }, + 'terminal.integrated.macOptionClickForcesSelection': { + description: nls.localize('terminal.integrated.macOptionClickForcesSelection', "Controls whether to force selection when using Option+click on macOS. This will force a regular (line) selection and disallow the use of column selection mode. This enables copying and pasting using the regular terminal selection, for example, when mouse mode is enabled in tmux."), + type: 'boolean', + default: false + }, + 'terminal.integrated.copyOnSelection': { + description: nls.localize('terminal.integrated.copyOnSelection', "Controls whether text selected in the terminal will be copied to the clipboard."), + type: 'boolean', + default: false + }, + 'terminal.integrated.drawBoldTextInBrightColors': { + description: nls.localize('terminal.integrated.drawBoldTextInBrightColors', "Controls whether bold text in the terminal will always use the \"bright\" ANSI color variant."), + type: 'boolean', + default: true + }, + 'terminal.integrated.fontFamily': { + markdownDescription: nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to `#editor.fontFamily#`'s value."), + type: 'string' + }, + // TODO: Support font ligatures + // 'terminal.integrated.fontLigatures': { + // 'description': nls.localize('terminal.integrated.fontLigatures', "Controls whether font ligatures are enabled in the terminal."), + // 'type': 'boolean', + // 'default': false + // }, + 'terminal.integrated.fontSize': { + description: nls.localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."), + type: 'number', + default: EDITOR_FONT_DEFAULTS.fontSize + }, + 'terminal.integrated.letterSpacing': { + description: nls.localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."), + type: 'number', + default: DEFAULT_LETTER_SPACING + }, + 'terminal.integrated.lineHeight': { + description: nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this number is multiplied by the terminal font size to get the actual line-height in pixels."), + type: 'number', + default: DEFAULT_LINE_HEIGHT + }, + 'terminal.integrated.fontWeight': { + type: 'string', + enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + description: nls.localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."), + default: 'normal' + }, + 'terminal.integrated.fontWeightBold': { + type: 'string', + enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + description: nls.localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."), + default: 'bold' + }, + 'terminal.integrated.cursorBlinking': { + description: nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), + type: 'boolean', + default: false + }, + 'terminal.integrated.cursorStyle': { + description: nls.localize('terminal.integrated.cursorStyle', "Controls the style of terminal cursor."), + enum: [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE], + default: TerminalCursorStyle.BLOCK + }, + 'terminal.integrated.scrollback': { + description: nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."), + type: 'number', + default: 1000 + }, + 'terminal.integrated.setLocaleVariables': { + markdownDescription: nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal."), + type: 'boolean', + default: true + }, + 'terminal.integrated.rendererType': { + type: 'string', + enum: ['auto', 'canvas', 'dom'], + enumDescriptions: [ + nls.localize('terminal.integrated.rendererType.auto', "Let VS Code guess which renderer to use."), + nls.localize('terminal.integrated.rendererType.canvas', "Use the standard GPU/canvas-based renderer"), + nls.localize('terminal.integrated.rendererType.dom', "Use the fallback DOM-based renderer.") + ], + default: 'auto', + description: nls.localize('terminal.integrated.rendererType', "Controls how the terminal is rendered.") + }, + 'terminal.integrated.rightClickBehavior': { + type: 'string', + enum: ['default', 'copyPaste', 'selectWord'], + enumDescriptions: [ + nls.localize('terminal.integrated.rightClickBehavior.default', "Show the context menu."), + nls.localize('terminal.integrated.rightClickBehavior.copyPaste', "Copy when there is a selection, otherwise paste."), + nls.localize('terminal.integrated.rightClickBehavior.selectWord', "Select the word under the cursor and show the context menu.") + ], + default: platform.isMacintosh ? 'selectWord' : platform.isWindows ? 'copyPaste' : 'default', + description: nls.localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click.") + }, + 'terminal.integrated.cwd': { + description: nls.localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), + type: 'string', + default: undefined + }, + 'terminal.integrated.confirmOnExit': { + description: nls.localize('terminal.integrated.confirmOnExit', "Controls whether to confirm on exit if there are active terminal sessions."), + type: 'boolean', + default: false + }, + 'terminal.integrated.enableBell': { + description: nls.localize('terminal.integrated.enableBell', "Controls whether the terminal bell is enabled."), + type: 'boolean', + default: false + }, + 'terminal.integrated.commandsToSkipShell': { + description: nls.localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open.\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')), + type: 'array', + items: { + type: 'string' + }, + default: [] + }, + 'terminal.integrated.env.osx': { + markdownDescription: nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] + }, + default: {} + }, + 'terminal.integrated.env.linux': { + markdownDescription: nls.localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] + }, + default: {} + }, + 'terminal.integrated.env.windows': { + markdownDescription: nls.localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] + }, + default: {} + }, + 'terminal.integrated.showExitAlert': { + description: nls.localize('terminal.integrated.showExitAlert', "Controls whether to show the alert \"The terminal process terminated with exit code\" when exit code is non-zero."), + type: 'boolean', + default: true + }, + 'terminal.integrated.splitCwd': { + description: nls.localize('terminal.integrated.splitCwd', "Controls the working directory a split terminal starts with."), + type: 'string', + enum: ['workspaceRoot', 'initial', 'inherited'], + enumDescriptions: [ + nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the working directory. In a multi-root workspace a choice for which root folder to use is offered."), + nls.localize('terminal.integrated.splitCwd.initial', "A new split terminal will use the working directory that the parent terminal started with."), + nls.localize('terminal.integrated.splitCwd.inherited', "On macOS and Linux, a new split terminal will use the working directory of the parent terminal. On Windows, this behaves the same as initial."), + ], + default: 'inherited' + }, + 'terminal.integrated.windowsEnableConpty': { + description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 18309+). Winpty will be used if this is false."), + type: 'boolean', + default: false + } + } +}); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenTermAction, QuickOpenTermAction.ID, QuickOpenTermAction.LABEL), 'Terminal: Switch Active Terminal', nls.localize('terminal', "Terminal")); +const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); +actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTermContributor); + +(Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( + TerminalPanel, + TERMINAL_PANEL_ID, + nls.localize('terminal', "Terminal"), + 'terminal', + 40, + TERMINAL_COMMAND_ID.TOGGLE +)); + +// On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl +const category = nls.localize('terminalCategory', "Terminal"); +const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_C, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } +}), 'Terminal: Create New Integrated Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearSelectionTerminalAction, ClearSelectionTerminalAction.ID, ClearSelectionTerminalAction.LABEL, { + primary: KeyCode.Escape, + linux: { primary: KeyCode.Escape } +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE)), 'Terminal: Escape selection', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewInActiveWorkspaceTerminalAction, CreateNewInActiveWorkspaceTerminalAction.ID, CreateNewInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (In Active Workspace)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL), 'Terminal: Focus Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextTerminalAction, FocusNextTerminalAction.ID, FocusNextTerminalAction.LABEL), 'Terminal: Focus Next Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTerminalAction, FocusPreviousTerminalAction.ID, FocusPreviousTerminalAction.LABEL), 'Terminal: Focus Previous Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_V, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V }, + // Don't apply to Mac since cmd+v works + mac: { primary: 0 } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { + // Don't use ctrl+a by default as that would override the common go to start + // of prompt shell binding + primary: 0, + // Technically this doesn't need to be here as it will fall back to this + // behavior anyway when handed to xterm.js, having this handled by VS Code + // makes it easier for users to see how it works though. + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunSelectedTextInTerminalAction, RunSelectedTextInTerminalAction.ID, RunSelectedTextInTerminalAction.LABEL), 'Terminal: Run Selected Text In Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunActiveFileInTerminalAction, RunActiveFileInTerminalAction.ID, RunActiveFileInTerminalAction.LABEL), 'Terminal: Run Active File In Active Terminal', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK, + mac: { primary: KeyMod.WinCtrl | KeyCode.US_BACKTICK } +}), 'View: Toggle Integrated Terminal', nls.localize('viewCategory', "View")); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownTerminalAction, ScrollDownTerminalAction.ID, ScrollDownTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Line)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownPageTerminalAction, ScrollDownPageTerminalAction.ID, ScrollDownPageTerminalAction.LABEL, { + primary: KeyMod.Shift | KeyCode.PageDown, + mac: { primary: KeyCode.PageDown } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Page)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToBottomTerminalAction, ScrollToBottomTerminalAction.ID, ScrollToBottomTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.End, + linux: { primary: KeyMod.Shift | KeyCode.End } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Bottom', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpTerminalAction, ScrollUpTerminalAction.ID, ScrollUpTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Line)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpPageTerminalAction, ScrollUpPageTerminalAction.ID, ScrollUpPageTerminalAction.LABEL, { + primary: KeyMod.Shift | KeyCode.PageUp, + mac: { primary: KeyCode.PageUp } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Page)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToTopTerminalAction, ScrollToTopTerminalAction.ID, ScrollToTopTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Home, + linux: { primary: KeyMod.Shift | KeyCode.Home } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Top', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); +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); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.KEY_F +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Focus Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape] +}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + mac: { primary: KeyMod.Alt | KeyCode.Backspace } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Left', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.Delete, + mac: { primary: KeyMod.Alt | KeyCode.Delete } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line Start', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineEndTerminalAction, MoveToLineEndTerminalAction.ID, MoveToLineEndTerminalAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line End', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL, { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, + secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5], + mac: { + primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, + secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5] + } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitInActiveWorkspaceTerminalAction, SplitInActiveWorkspaceTerminalAction.ID, SplitInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Split Terminal (In Active Workspace)', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousPaneTerminalAction, FocusPreviousPaneTerminalAction.ID, FocusPreviousPaneTerminalAction.LABEL, { + primary: KeyMod.Alt | KeyCode.LeftArrow, + secondary: [KeyMod.Alt | KeyCode.UpArrow], + mac: { + primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.LeftArrow, + secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.UpArrow] + } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Previous Pane', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextPaneTerminalAction, FocusNextPaneTerminalAction.ID, FocusNextPaneTerminalAction.LABEL, { + primary: KeyMod.Alt | KeyCode.RightArrow, + secondary: [KeyMod.Alt | KeyCode.DownArrow], + mac: { + primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.RightArrow, + secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.DownArrow] + } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Next Pane', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneLeftTerminalAction, ResizePaneLeftTerminalAction.ID, ResizePaneLeftTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Left', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneRightTerminalAction, ResizePaneRightTerminalAction.ID, ResizePaneRightTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Right', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneUpTerminalAction, ResizePaneUpTerminalAction.ID, ResizePaneUpTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.UpArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Up', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneDownTerminalAction, ResizePaneDownTerminalAction.ID, ResizePaneDownTerminalAction.LABEL, { + primary: 0, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Down', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToPreviousCommandAction, ScrollToPreviousCommandAction.ID, ScrollToPreviousCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Previous Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToNextCommandAction, ScrollToNextCommandAction.ID, ScrollToNextCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Next Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousCommandAction, SelectToPreviousCommandAction.ID, SelectToPreviousCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Previous Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextCommandAction, SelectToNextCommandAction.ID, SelectToNextCommandAction.LABEL, { + primary: 0, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_R, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find by regex'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID_TERMINAL_FOCUS, ToggleRegexCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_R, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find by regex', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_W, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find whole word'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID_TERMINAL_FOCUS, ToggleWholeWordCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_W, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find whole word', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_C, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find match case'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID_TERMINAL_FOCUS, ToggleCaseSensitiveCommand.LABEL, { + primary: KeyMod.Alt | KeyCode.KEY_C, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find match case', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID_TERMINAL_FOCUS, FindNext.LABEL, { + primary: KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { + primary: KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { + primary: KeyMod.Shift | KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { + primary: KeyMod.Shift | KeyCode.F3, + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, +}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); + + +const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({ + id: SendSequenceTerminalCommand.ID, + precondition: null, + description: { + description: `Send Custom Sequence To Terminal`, + args: [{ + name: 'args', + schema: { + 'type': 'object', + 'required': ['text'], + 'properties': { + 'text': { + 'type': 'string' + } + }, + } + }] + } +}); +sendSequenceTerminalCommand.register(); + +setupTerminalCommands(); +setupTerminalMenu(); + +registerColors(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index e5a54d0ba7419..db30d122c39cb 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./terminalFindWidget'; import { SimpleFindWidget } from 'vs/editor/contrib/find/simpleFindWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts index d4cec6691ce75..a55b93caa40c7 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminal.contribution.ts @@ -3,66 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!../browser/media/scrollbar'; -import 'vs/css!../browser/media/terminal'; -import 'vs/css!../browser/media/xterm'; -import 'vs/css!../browser/media/widgets'; -import * as nls from 'vs/nls'; -import * as panel from 'vs/workbench/browser/panel'; import * as platform from 'vs/base/common/platform'; +import * as nls from 'vs/nls'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TerminalCursorStyle, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, DEFAULT_LINE_HEIGHT, DEFAULT_LETTER_SPACING, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/terminal/common/terminal'; -import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ClearSelectionTerminalAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction, SendSequenceTerminalCommand, ToggleRegexCommand, ToggleWholeWordCommand, ToggleCaseSensitiveCommand, FindNext, FindPrevious, DeleteToLineStartTerminalAction, TERMINAL_PICKER_PREFIX, KillTerminalAction, CopyTerminalSelectionAction, SelectAllTerminalAction, ToggleTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { TerminalService } from 'vs/workbench/contrib/terminal/electron-browser/terminalService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; -import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; -import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; -import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands'; -import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; -import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; -import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstanceService'; +import { Registry } from 'vs/platform/registry/common/platform'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; - -const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); - -const inTerminalsPicker = 'inTerminalPicker'; - -quickOpenRegistry.registerQuickOpenHandler( - new QuickOpenHandlerDescriptor( - TerminalPickerHandler, - TerminalPickerHandler.ID, - TERMINAL_PICKER_PREFIX, - inTerminalsPicker, - nls.localize('quickOpen.terminal', "Show All Opened Terminals") - ) -); - -const quickOpenNavigateNextInTerminalPickerId = 'workbench.action.quickOpenNavigateNextInTerminalPicker'; -CommandsRegistry.registerCommand( - { id: quickOpenNavigateNextInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigateNextInTerminalPickerId, true) }); - -const quickOpenNavigatePreviousInTerminalPickerId = 'workbench.action.quickOpenNavigatePreviousInTerminalPicker'; -CommandsRegistry.registerCommand( - { id: quickOpenNavigatePreviousInTerminalPickerId, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInTerminalPickerId, false) }); - - -const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenTermAction, QuickOpenTermAction.ID, QuickOpenTermAction.LABEL), 'Terminal: Switch Active Terminal', nls.localize('terminal', "Terminal")); -const actionBarRegistry = Registry.as(ActionBarExtensions.Actionbar); -actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTermContributor); +import { ITerminalService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/electron-browser/terminalInstanceService'; +import { TerminalService } from 'vs/workbench/contrib/terminal/electron-browser/terminalService'; +import { getDefaultShell } from 'vs/workbench/contrib/terminal/node/terminal'; const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ @@ -76,467 +26,18 @@ configurationRegistry.registerConfiguration({ type: 'string', default: getDefaultShell(platform.Platform.Linux) }, - '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', - items: { - type: 'string' - }, - default: [] - }, 'terminal.integrated.shell.osx': { markdownDescription: nls.localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on macOS. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), type: 'string', default: getDefaultShell(platform.Platform.Mac) }, - 'terminal.integrated.shellArgs.osx': { - markdownDescription: nls.localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), - type: 'array', - items: { - type: 'string' - }, - // Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This - // is the reason terminals on macOS typically run login shells by default which set up - // the environment. See http://unix.stackexchange.com/a/119675/115410 - default: ['-l'] - }, 'terminal.integrated.shell.windows': { markdownDescription: nls.localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), type: 'string', default: getDefaultShell(platform.Platform.Windows) - }, - 'terminal.integrated.shellArgs.windows': { - markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), - 'anyOf': [ - { - type: 'array', - items: { - type: 'string', - markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") - }, - }, - { - type: 'string', - markdownDescription: nls.localize('terminal.integrated.shellArgs.windows.string', "The command line arguments in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6) to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration).") - } - ], - default: [] - }, - 'terminal.integrated.macOptionIsMeta': { - description: nls.localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), - type: 'boolean', - default: false - }, - 'terminal.integrated.macOptionClickForcesSelection': { - description: nls.localize('terminal.integrated.macOptionClickForcesSelection', "Controls whether to force selection when using Option+click on macOS. This will force a regular (line) selection and disallow the use of column selection mode. This enables copying and pasting using the regular terminal selection, for example, when mouse mode is enabled in tmux."), - type: 'boolean', - default: false - }, - 'terminal.integrated.copyOnSelection': { - description: nls.localize('terminal.integrated.copyOnSelection', "Controls whether text selected in the terminal will be copied to the clipboard."), - type: 'boolean', - default: false - }, - 'terminal.integrated.drawBoldTextInBrightColors': { - description: nls.localize('terminal.integrated.drawBoldTextInBrightColors', "Controls whether bold text in the terminal will always use the \"bright\" ANSI color variant."), - type: 'boolean', - default: true - }, - 'terminal.integrated.fontFamily': { - markdownDescription: nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to `#editor.fontFamily#`'s value."), - type: 'string' - }, - // TODO: Support font ligatures - // 'terminal.integrated.fontLigatures': { - // 'description': nls.localize('terminal.integrated.fontLigatures', "Controls whether font ligatures are enabled in the terminal."), - // 'type': 'boolean', - // 'default': false - // }, - 'terminal.integrated.fontSize': { - description: nls.localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."), - type: 'number', - default: EDITOR_FONT_DEFAULTS.fontSize - }, - 'terminal.integrated.letterSpacing': { - description: nls.localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."), - type: 'number', - default: DEFAULT_LETTER_SPACING - }, - 'terminal.integrated.lineHeight': { - description: nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this number is multiplied by the terminal font size to get the actual line-height in pixels."), - type: 'number', - default: DEFAULT_LINE_HEIGHT - }, - 'terminal.integrated.fontWeight': { - type: 'string', - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: nls.localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."), - default: 'normal' - }, - 'terminal.integrated.fontWeightBold': { - type: 'string', - enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - description: nls.localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."), - default: 'bold' - }, - 'terminal.integrated.cursorBlinking': { - description: nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), - type: 'boolean', - default: false - }, - 'terminal.integrated.cursorStyle': { - description: nls.localize('terminal.integrated.cursorStyle', "Controls the style of terminal cursor."), - enum: [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE], - default: TerminalCursorStyle.BLOCK - }, - 'terminal.integrated.scrollback': { - description: nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."), - type: 'number', - default: 1000 - }, - 'terminal.integrated.setLocaleVariables': { - markdownDescription: nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal."), - type: 'boolean', - default: true - }, - 'terminal.integrated.rendererType': { - type: 'string', - enum: ['auto', 'canvas', 'dom'], - enumDescriptions: [ - nls.localize('terminal.integrated.rendererType.auto', "Let VS Code guess which renderer to use."), - nls.localize('terminal.integrated.rendererType.canvas', "Use the standard GPU/canvas-based renderer"), - nls.localize('terminal.integrated.rendererType.dom', "Use the fallback DOM-based renderer.") - ], - default: 'auto', - description: nls.localize('terminal.integrated.rendererType', "Controls how the terminal is rendered.") - }, - 'terminal.integrated.rightClickBehavior': { - type: 'string', - enum: ['default', 'copyPaste', 'selectWord'], - enumDescriptions: [ - nls.localize('terminal.integrated.rightClickBehavior.default', "Show the context menu."), - nls.localize('terminal.integrated.rightClickBehavior.copyPaste', "Copy when there is a selection, otherwise paste."), - nls.localize('terminal.integrated.rightClickBehavior.selectWord', "Select the word under the cursor and show the context menu.") - ], - default: platform.isMacintosh ? 'selectWord' : platform.isWindows ? 'copyPaste' : 'default', - description: nls.localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click.") - }, - 'terminal.integrated.cwd': { - description: nls.localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), - type: 'string', - default: undefined - }, - 'terminal.integrated.confirmOnExit': { - description: nls.localize('terminal.integrated.confirmOnExit', "Controls whether to confirm on exit if there are active terminal sessions."), - type: 'boolean', - default: false - }, - 'terminal.integrated.enableBell': { - description: nls.localize('terminal.integrated.enableBell', "Controls whether the terminal bell is enabled."), - type: 'boolean', - default: false - }, - 'terminal.integrated.commandsToSkipShell': { - description: nls.localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open.\nDefault Skipped Commands:\n\n{0}", DEFAULT_COMMANDS_TO_SKIP_SHELL.sort().map(command => `- ${command}`).join('\n')), - type: 'array', - items: { - type: 'string' - }, - default: [] - }, - 'terminal.integrated.env.osx': { - markdownDescription: nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), - type: 'object', - additionalProperties: { - type: ['string', 'null'] - }, - default: {} - }, - 'terminal.integrated.env.linux': { - markdownDescription: nls.localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), - type: 'object', - additionalProperties: { - type: ['string', 'null'] - }, - default: {} - }, - 'terminal.integrated.env.windows': { - markdownDescription: nls.localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), - type: 'object', - additionalProperties: { - type: ['string', 'null'] - }, - default: {} - }, - 'terminal.integrated.showExitAlert': { - description: nls.localize('terminal.integrated.showExitAlert', "Controls whether to show the alert \"The terminal process terminated with exit code\" when exit code is non-zero."), - type: 'boolean', - default: true - }, - 'terminal.integrated.splitCwd': { - description: nls.localize('terminal.integrated.splitCwd', "Controls the working directory a split terminal starts with."), - type: 'string', - enum: ['workspaceRoot', 'initial', 'inherited'], - enumDescriptions: [ - nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the working directory. In a multi-root workspace a choice for which root folder to use is offered."), - nls.localize('terminal.integrated.splitCwd.initial', "A new split terminal will use the working directory that the parent terminal started with."), - nls.localize('terminal.integrated.splitCwd.inherited', "On macOS and Linux, a new split terminal will use the working directory of the parent terminal. On Windows, this behaves the same as initial."), - ], - default: 'inherited' - }, - 'terminal.integrated.windowsEnableConpty': { - description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 18309+). Winpty will be used if this is false."), - type: 'boolean', - default: false } } }); registerSingleton(ITerminalService, TerminalService, true); registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); - -(Registry.as(panel.Extensions.Panels)).registerPanel(new panel.PanelDescriptor( - TerminalPanel, - TERMINAL_PANEL_ID, - nls.localize('terminal', "Terminal"), - 'terminal', - 40, - TERMINAL_COMMAND_ID.TOGGLE -)); - -// On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl -const category = nls.localize('terminalCategory', "Terminal"); -const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.LABEL), 'Terminal: Kill the Active Terminal Instance', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } -}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKTICK, - mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_BACKTICK } -}), 'Terminal: Create New Integrated Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearSelectionTerminalAction, ClearSelectionTerminalAction.ID, ClearSelectionTerminalAction.LABEL, { - primary: KeyCode.Escape, - linux: { primary: KeyCode.Escape } -}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE)), 'Terminal: Escape selection', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(CreateNewInActiveWorkspaceTerminalAction, CreateNewInActiveWorkspaceTerminalAction.ID, CreateNewInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Create New Integrated Terminal (In Active Workspace)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL), 'Terminal: Focus Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextTerminalAction, FocusNextTerminalAction.ID, FocusNextTerminalAction.LABEL), 'Terminal: Focus Next Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousTerminalAction, FocusPreviousTerminalAction.ID, FocusPreviousTerminalAction.LABEL), 'Terminal: Focus Previous Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V }, - // Don't apply to Mac since cmd+v works - mac: { primary: 0 } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL, { - // Don't use ctrl+a by default as that would override the common go to start - // of prompt shell binding - primary: 0, - // Technically this doesn't need to be here as it will fall back to this - // behavior anyway when handed to xterm.js, having this handled by VS Code - // makes it easier for users to see how it works though. - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_A } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select All', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunSelectedTextInTerminalAction, RunSelectedTextInTerminalAction.ID, RunSelectedTextInTerminalAction.LABEL), 'Terminal: Run Selected Text In Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(RunActiveFileInTerminalAction, RunActiveFileInTerminalAction.ID, RunActiveFileInTerminalAction.LABEL), 'Terminal: Run Active File In Active Terminal', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTerminalAction, ToggleTerminalAction.ID, ToggleTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.US_BACKTICK, - mac: { primary: KeyMod.WinCtrl | KeyCode.US_BACKTICK } -}), 'View: Toggle Integrated Terminal', nls.localize('viewCategory', "View")); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownTerminalAction, ScrollDownTerminalAction.ID, ScrollDownTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Line)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollDownPageTerminalAction, ScrollDownPageTerminalAction.ID, ScrollDownPageTerminalAction.LABEL, { - primary: KeyMod.Shift | KeyCode.PageDown, - mac: { primary: KeyCode.PageDown } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Down (Page)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToBottomTerminalAction, ScrollToBottomTerminalAction.ID, ScrollToBottomTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.End, - linux: { primary: KeyMod.Shift | KeyCode.End } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Bottom', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpTerminalAction, ScrollUpTerminalAction.ID, ScrollUpTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Line)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollUpPageTerminalAction, ScrollUpPageTerminalAction.ID, ScrollUpPageTerminalAction.LABEL, { - primary: KeyMod.Shift | KeyCode.PageUp, - mac: { primary: KeyCode.PageUp } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll Up (Page)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToTopTerminalAction, ScrollToTopTerminalAction.ID, ScrollToTopTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Home, - linux: { primary: KeyMod.Shift | KeyCode.Home } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll to Top', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_K } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); -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); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusTerminalFindWidgetAction, FocusTerminalFindWidgetAction.ID, FocusTerminalFindWidgetAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Focus Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(HideTerminalFindWidgetAction, HideTerminalFindWidgetAction.ID, HideTerminalFindWidgetAction.LABEL, { - primary: KeyCode.Escape, - secondary: [KeyMod.Shift | KeyCode.Escape] -}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Backspace, - mac: { primary: KeyMod.Alt | KeyCode.Backspace } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Left', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Delete, - mac: { primary: KeyMod.Alt | KeyCode.Delete } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line Start', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveToLineEndTerminalAction, MoveToLineEndTerminalAction.ID, MoveToLineEndTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line End', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, - secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5], - mac: { - primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH, - secondary: [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_5] - } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Split', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SplitInActiveWorkspaceTerminalAction, SplitInActiveWorkspaceTerminalAction.ID, SplitInActiveWorkspaceTerminalAction.LABEL), 'Terminal: Split Terminal (In Active Workspace)', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousPaneTerminalAction, FocusPreviousPaneTerminalAction.ID, FocusPreviousPaneTerminalAction.LABEL, { - primary: KeyMod.Alt | KeyCode.LeftArrow, - secondary: [KeyMod.Alt | KeyCode.UpArrow], - mac: { - primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.LeftArrow, - secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.UpArrow] - } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Previous Pane', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextPaneTerminalAction, FocusNextPaneTerminalAction.ID, FocusNextPaneTerminalAction.LABEL, { - primary: KeyMod.Alt | KeyCode.RightArrow, - secondary: [KeyMod.Alt | KeyCode.DownArrow], - mac: { - primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.RightArrow, - secondary: [KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.DownArrow] - } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Focus Next Pane', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneLeftTerminalAction, ResizePaneLeftTerminalAction.ID, ResizePaneLeftTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Left', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneRightTerminalAction, ResizePaneRightTerminalAction.ID, ResizePaneRightTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Right', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneUpTerminalAction, ResizePaneUpTerminalAction.ID, ResizePaneUpTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.UpArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Up', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ResizePaneDownTerminalAction, ResizePaneDownTerminalAction.ID, ResizePaneDownTerminalAction.LABEL, { - primary: 0, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Resize Pane Down', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToPreviousCommandAction, ScrollToPreviousCommandAction.ID, ScrollToPreviousCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Previous Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToNextCommandAction, ScrollToNextCommandAction.ID, ScrollToNextCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Scroll To Next Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousCommandAction, SelectToPreviousCommandAction.ID, SelectToPreviousCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Previous Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextCommandAction, SelectToNextCommandAction.ID, SelectToNextCommandAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID, ToggleRegexCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_R, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find by regex'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRegexCommand, ToggleRegexCommand.ID_TERMINAL_FOCUS, ToggleRegexCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_R, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find by regex', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID, ToggleWholeWordCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_W, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find whole word'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWholeWordCommand, ToggleWholeWordCommand.ID_TERMINAL_FOCUS, ToggleWholeWordCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_W, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_W } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find whole word', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID, ToggleCaseSensitiveCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_C, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Toggle find match case'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCaseSensitiveCommand, ToggleCaseSensitiveCommand.ID_TERMINAL_FOCUS, ToggleCaseSensitiveCommand.LABEL, { - primary: KeyMod.Alt | KeyCode.KEY_C, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Toggle find match case', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID_TERMINAL_FOCUS, FindNext.LABEL, { - primary: KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, { - primary: KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next'); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, { - primary: KeyMod.Shift | KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category); -actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, { - primary: KeyMod.Shift | KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, -}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); - - -const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({ - id: SendSequenceTerminalCommand.ID, - precondition: null, - description: { - description: `Send Custom Sequence To Terminal`, - args: [{ - name: 'args', - schema: { - 'type': 'object', - 'required': ['text'], - 'properties': { - 'text': { - 'type': 'string' - } - }, - } - }] - } -}); -sendSequenceTerminalCommand.register(); - -setupTerminalCommands(); -setupTerminalMenu(); - -registerColors(); diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 84ed6ce8f0281..69bf6bcd8cee7 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -152,6 +152,7 @@ import 'vs/workbench/contrib/output/electron-browser/output.contribution'; import 'vs/workbench/contrib/output/browser/outputPanel'; // Terminal +import 'vs/workbench/contrib/terminal/browser/terminal.contribution'; import 'vs/workbench/contrib/terminal/electron-browser/terminal.contribution'; import 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import 'vs/workbench/contrib/terminal/browser/terminalPanel';