diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 08bd75646ec..871087e2be6 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest } from 'vs/workbench/contrib/terminal/common/terminal'; import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; @@ -352,17 +352,17 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape * Encapsulates temporary tracking of data events from terminal instances, once disposed all * listeners are removed. */ -class TerminalDataEventTracker extends DisposableStore { +class TerminalDataEventTracker extends Disposable { constructor( private readonly _callback: (id: number, data: string) => void, @ITerminalService private readonly _terminalService: ITerminalService ) { super(); - this._terminalService.terminalInstances.forEach(instance => this._register(instance)); - this.add(this._terminalService.onInstanceCreated(instance => this._register(instance))); + this._terminalService.terminalInstances.forEach(instance => this._registerInstance(instance)); + this._register(this._terminalService.onInstanceCreated(instance => this._registerInstance(instance))); } - private _register(instance: ITerminalInstance): void { - this.add(instance.onData(e => this._callback(instance.id, e))); + private _registerInstance(instance: ITerminalInstance): void { + this._register(instance.onData(e => this._callback(instance.id, e))); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index e3723366157..1270b142228 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -90,6 +90,7 @@ export abstract class SimpleFindWidget extends Widget { this._findInput.setRegex(this._state.isRegex); this._findInput.setWholeWords(this._state.wholeWord); this._findInput.setCaseSensitive(this._state.matchCase); + this.findFirst(); })); this._register(this._findInput.onKeyDown((e) => { @@ -165,6 +166,7 @@ export abstract class SimpleFindWidget extends Widget { protected abstract onInputChanged(): boolean; protected abstract find(previous: boolean): void; + protected abstract findFirst(): void; protected abstract onFocusTrackerFocus(): void; protected abstract onFocusTrackerBlur(): void; protected abstract onFindInputFocusTrackerFocus(): void; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index 2714994ea2a..87ca61f2d55 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -78,4 +78,14 @@ export class TerminalFindWidget extends SimpleFindWidget { protected onFindInputFocusTrackerBlur() { this._findInputFocused.reset(); } + + public findFirst() { + const instance = this._terminalService.getActiveInstance(); + if (instance) { + if (instance.hasSelection()) { + instance.clearSelection(); + } + instance.findPrevious(this.inputValue, { regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue() }); + } + } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index ca4d3207848..cd0b782993a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -9,7 +9,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { debounce } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import * as lifecycle from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; import * as nls from 'vs/nls'; @@ -165,7 +165,7 @@ interface IGridDimensions { rows: number; } -export class TerminalInstance implements ITerminalInstance { +export class TerminalInstance extends Disposable implements ITerminalInstance { private static readonly EOL_REGEX = /\r?\n/g; private static _lastKnownCanvasDimensions: ICanvasDimensions | undefined; @@ -173,7 +173,7 @@ export class TerminalInstance implements ITerminalInstance { private static _idCounter = 1; private _processManager: ITerminalProcessManager; - private _pressAnyKeyToCloseListener: lifecycle.IDisposable | undefined; + private _pressAnyKeyToCloseListener: IDisposable | undefined; private _id: number; private _isExiting: boolean; @@ -196,8 +196,7 @@ export class TerminalInstance implements ITerminalInstance { private _titleReadyPromise: Promise; private _titleReadyComplete: (title: string) => any; - private readonly _disposables = new lifecycle.DisposableStore(); - private _messageTitleDisposable: lifecycle.IDisposable | undefined; + private _messageTitleDisposable: IDisposable | undefined; private _widgetManager: TerminalWidgetManager; private _linkHandler: TerminalLinkHandler; @@ -273,6 +272,8 @@ export class TerminalInstance implements ITerminalInstance { @IStorageService private readonly _storageService: IStorageService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { + super(); + this._skipTerminalCommands = []; this._isExiting = false; this._hadFocusOnExit = false; @@ -315,8 +316,8 @@ export class TerminalInstance implements ITerminalInstance { })); } - public addDisposable(disposable: lifecycle.IDisposable): void { - this._disposables.add(disposable); + public addDisposable(disposable: IDisposable): void { + this._register(disposable); } private _initDimensions(): void { @@ -506,7 +507,7 @@ export class TerminalInstance implements ITerminalInstance { this._commandTrackerAddon = new CommandTrackerAddon(); this._xterm.loadAddon(this._commandTrackerAddon); - this._disposables.add(this._themeService.onThemeChange(theme => this._updateTheme(xterm, theme))); + this._register(this._themeService.onThemeChange(theme => this._updateTheme(xterm, theme))); return xterm; } @@ -590,7 +591,7 @@ export class TerminalInstance implements ITerminalInstance { return true; }); - this._disposables.add(dom.addDisposableListener(xterm.element, 'mousedown', () => { + this._register(dom.addDisposableListener(xterm.element, 'mousedown', () => { // We need to listen to the mouseup event on the document since the user may release // the mouse button anywhere outside of _xterm.element. const listener = dom.addDisposableListener(document, 'mouseup', () => { @@ -602,7 +603,7 @@ export class TerminalInstance implements ITerminalInstance { })); // xterm.js currently drops selection on keyup as we need to handle this case. - this._disposables.add(dom.addDisposableListener(xterm.element, 'keyup', () => { + this._register(dom.addDisposableListener(xterm.element, 'keyup', () => { // Wait until keyup has propagated through the DOM before evaluating // the new selection state. setTimeout(() => this._refreshSelectionContextKey(), 0); @@ -612,7 +613,7 @@ export class TerminalInstance implements ITerminalInstance { const focusTrap: HTMLElement = document.createElement('div'); focusTrap.setAttribute('tabindex', '0'); dom.addClass(focusTrap, 'focus-trap'); - this._disposables.add(dom.addDisposableListener(focusTrap, 'focus', () => { + this._register(dom.addDisposableListener(focusTrap, 'focus', () => { let currentElement = focusTrap; while (!dom.hasClass(currentElement, 'part')) { currentElement = currentElement.parentElement!; @@ -622,18 +623,18 @@ export class TerminalInstance implements ITerminalInstance { })); xtermHelper.insertBefore(focusTrap, xterm.textarea); - this._disposables.add(dom.addDisposableListener(xterm.textarea, 'focus', () => { + this._register(dom.addDisposableListener(xterm.textarea, 'focus', () => { this._terminalFocusContextKey.set(true); this._onFocused.fire(this); })); - this._disposables.add(dom.addDisposableListener(xterm.textarea, 'blur', () => { + this._register(dom.addDisposableListener(xterm.textarea, 'blur', () => { this._terminalFocusContextKey.reset(); this._refreshSelectionContextKey(); })); - this._disposables.add(dom.addDisposableListener(xterm.element, 'focus', () => { + this._register(dom.addDisposableListener(xterm.element, 'focus', () => { this._terminalFocusContextKey.set(true); })); - this._disposables.add(dom.addDisposableListener(xterm.element, 'blur', () => { + this._register(dom.addDisposableListener(xterm.element, 'blur', () => { this._terminalFocusContextKey.reset(); this._refreshSelectionContextKey(); })); @@ -777,11 +778,11 @@ export class TerminalInstance implements ITerminalInstance { public dispose(immediate?: boolean): void { this._logService.trace(`terminalInstance#dispose (id: ${this.id})`); - lifecycle.dispose(this._windowsShellHelper); + dispose(this._windowsShellHelper); this._windowsShellHelper = undefined; - this._linkHandler = lifecycle.dispose(this._linkHandler); - this._commandTrackerAddon = lifecycle.dispose(this._commandTrackerAddon); - this._widgetManager = lifecycle.dispose(this._widgetManager); + this._linkHandler = dispose(this._linkHandler); + this._commandTrackerAddon = dispose(this._commandTrackerAddon); + this._widgetManager = dispose(this._widgetManager); if (this._xterm && this._xterm.element) { this._hadFocusOnExit = dom.hasClass(this._xterm.element, 'focus'); @@ -811,7 +812,7 @@ export class TerminalInstance implements ITerminalInstance { this._isDisposed = true; this._onDisposed.fire(this); } - this._disposables.dispose(); + super.dispose(); } public forceRedraw(): void { @@ -1335,12 +1336,10 @@ export class TerminalInstance implements ITerminalInstance { } else { // If the title has not been set by the API or the rename command, unregister the handler that // automatically updates the terminal name - if (this._messageTitleDisposable) { - lifecycle.dispose(this._messageTitleDisposable); - lifecycle.dispose(this._windowsShellHelper); - this._messageTitleDisposable = undefined; - this._windowsShellHelper = undefined; - } + dispose(this._messageTitleDisposable); + this._messageTitleDisposable = undefined; + dispose(this._windowsShellHelper); + this._windowsShellHelper = undefined; } const didTitleChange = title !== this._title; const oldTitle = this._title; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 29e4aa6fbfc..826122750b8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -20,6 +20,7 @@ import { IProductService } from 'vs/platform/product/common/product'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { Disposable } from 'vs/base/common/lifecycle'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -42,7 +43,7 @@ enum ProcessType { * - Pty Process: The pseudoterminal master process (or the winpty agent process) * - Shell Process: The pseudoterminal slave process (ie. the shell) */ -export class TerminalProcessManager implements ITerminalProcessManager { +export class TerminalProcessManager extends Disposable implements ITerminalProcessManager { public processState: ProcessState = ProcessState.UNINITIALIZED; public ptyProcessReady: Promise; public shellProcessId: number | undefined; @@ -57,19 +58,19 @@ export class TerminalProcessManager implements ITerminalProcessManager { private _latencyLastMeasured: number = 0; private _initialCwd: string | undefined; - private readonly _onProcessReady = new Emitter(); + private readonly _onProcessReady = this._register(new Emitter()); public get onProcessReady(): Event { return this._onProcessReady.event; } - private readonly _onBeforeProcessData = new Emitter(); + private readonly _onBeforeProcessData = this._register(new Emitter()); public get onBeforeProcessData(): Event { return this._onBeforeProcessData.event; } - private readonly _onProcessData = new Emitter(); + private readonly _onProcessData = this._register(new Emitter()); public get onProcessData(): Event { return this._onProcessData.event; } - private readonly _onProcessTitle = new Emitter(); + private readonly _onProcessTitle = this._register(new Emitter()); public get onProcessTitle(): Event { return this._onProcessTitle.event; } - private readonly _onProcessExit = new Emitter(); + private readonly _onProcessExit = this._register(new Emitter()); public get onProcessExit(): Event { return this._onProcessExit.event; } - private readonly _onProcessOverrideDimensions = new Emitter(); + private readonly _onProcessOverrideDimensions = this._register(new Emitter()); public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } - private readonly _onProcessOverrideShellLaunchConfig = new Emitter(); + private readonly _onProcessOverrideShellLaunchConfig = this._register(new Emitter()); public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessOverrideShellLaunchConfig.event; } constructor( @@ -86,6 +87,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService ) { + super(); this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`); @@ -104,6 +106,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { this._process.shutdown(immediate); this._process = null; } + super.dispose(); } public async createProcess( diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index aefb287ded3..4b49a00187d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -220,9 +220,9 @@ export class TerminalTab extends Disposable implements ITerminalTab { public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } - private readonly _onDisposed: Emitter = new Emitter(); + private readonly _onDisposed: Emitter = this._register(new Emitter()); public readonly onDisposed: Event = this._onDisposed.event; - private readonly _onInstancesChanged: Emitter = new Emitter(); + private readonly _onInstancesChanged: Emitter = this._register(new Emitter()); public readonly onInstancesChanged: Event = this._onInstancesChanged.event; constructor( diff --git a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts index 80923d84486..dfc07d6efe7 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy.ts @@ -18,13 +18,13 @@ export class TerminalProcessExtHostProxy extends Disposable implements ITerminal public readonly onProcessData: Event = this._onProcessData.event; private readonly _onProcessExit = this._register(new Emitter()); public readonly onProcessExit: Event = this._onProcessExit.event; - private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>(); + private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>()); public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } private readonly _onProcessTitleChanged = this._register(new Emitter()); public readonly onProcessTitleChanged: Event = this._onProcessTitleChanged.event; - private readonly _onProcessOverrideDimensions = new Emitter(); + private readonly _onProcessOverrideDimensions = this._register(new Emitter()); public get onProcessOverrideDimensions(): Event { return this._onProcessOverrideDimensions.event; } - private readonly _onProcessResolvedShellLaunchConfig = new Emitter(); + private readonly _onProcessResolvedShellLaunchConfig = this._register(new Emitter()); public get onProcessResolvedShellLaunchConfig(): Event { return this._onProcessResolvedShellLaunchConfig.event; } private readonly _onInput = this._register(new Emitter()); diff --git a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts index 74d4598d6f5..2c2635f7c1c 100644 --- a/src/vs/workbench/contrib/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/contrib/terminal/node/terminalProcess.ts @@ -10,7 +10,7 @@ import * as pty from 'node-pty'; import * as fs from 'fs'; import { Event, Emitter } from 'vs/base/common/event'; import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/terminal'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IShellLaunchConfig, ITerminalChildProcess, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE } from 'vs/workbench/contrib/terminal/common/terminal'; import { exec } from 'child_process'; import { ILogService } from 'vs/platform/log/common/log'; @@ -18,7 +18,7 @@ import { stat } from 'vs/base/node/pfs'; import { findExecutable } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; import { URI } from 'vs/base/common/uri'; -export class TerminalProcess implements ITerminalChildProcess, IDisposable { +export class TerminalProcess extends Disposable implements ITerminalChildProcess { private _exitCode: number; private _closeTimeout: any; private _ptyProcess: pty.IPty | undefined; @@ -28,13 +28,13 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _titleInterval: NodeJS.Timer | null = null; private _initialCwd: string; - private readonly _onProcessData = new Emitter(); + private readonly _onProcessData = this._register(new Emitter()); public get onProcessData(): Event { return this._onProcessData.event; } - private readonly _onProcessExit = new Emitter(); + private readonly _onProcessExit = this._register(new Emitter()); public get onProcessExit(): Event { return this._onProcessExit.event; } - private readonly _onProcessReady = new Emitter<{ pid: number, cwd: string }>(); + private readonly _onProcessReady = this._register(new Emitter<{ pid: number, cwd: string }>()); public get onProcessReady(): Event<{ pid: number, cwd: string }> { return this._onProcessReady.event; } - private readonly _onProcessTitleChanged = new Emitter(); + private readonly _onProcessTitleChanged = this._register(new Emitter()); public get onProcessTitleChanged(): Event { return this._onProcessTitleChanged.event; } constructor( @@ -46,6 +46,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { windowsEnableConpty: boolean, @ILogService private readonly _logService: ILogService ) { + super(); let shellName: string; if (os.platform() === 'win32') { shellName = path.basename(shellLaunchConfig.executable || ''); @@ -143,6 +144,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { this._onProcessExit.dispose(); this._onProcessReady.dispose(); this._onProcessTitleChanged.dispose(); + super.dispose(); } private _setupTitlePolling(ptyProcess: pty.IPty) { diff --git a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts index 030dcd89100..18c24058910 100644 --- a/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts +++ b/src/vs/workbench/contrib/terminal/node/windowsShellHelper.ts @@ -8,6 +8,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { ITerminalInstance, IWindowsShellHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { Terminal as XTermTerminal } from 'xterm'; import WindowsProcessTreeType = require('windows-process-tree'); +import { Disposable } from 'vs/base/common/lifecycle'; const SHELL_EXECUTABLES = [ 'cmd.exe', @@ -24,8 +25,8 @@ const SHELL_EXECUTABLES = [ let windowsProcessTree: typeof WindowsProcessTreeType; -export class WindowsShellHelper implements IWindowsShellHelper { - private _onCheckShell: Emitter | undefined> = new Emitter | undefined>(); +export class WindowsShellHelper extends Disposable implements IWindowsShellHelper { + private _onCheckShell: Emitter | undefined> = this._register(new Emitter | undefined>()); private _isDisposed: boolean; private _currentRequest: Promise | undefined; private _newLineFeed: boolean = false; @@ -35,6 +36,8 @@ export class WindowsShellHelper implements IWindowsShellHelper { private _terminalInstance: ITerminalInstance, private _xterm: XTermTerminal ) { + super(); + if (!platform.isWindows) { throw new Error(`WindowsShellHelper cannot be instantiated on ${platform.platform}`); } @@ -111,6 +114,7 @@ export class WindowsShellHelper implements IWindowsShellHelper { public dispose(): void { this._isDisposed = true; + super.dispose(); } /** diff --git a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts index 6f31cd49704..0d8e6804bc0 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewFindWidget.ts @@ -54,4 +54,6 @@ export class WebviewFindWidget extends SimpleFindWidget { protected onFindInputFocusTrackerFocus() { } protected onFindInputFocusTrackerBlur() { } + + protected findFirst() { } }