diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index fec3a0f0f41f3f81c281971121c5eb517b511af6..f57f336bfef008209c87fc2d22bac20a209786e4 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -176,7 +176,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape public $startLinkProvider(): void { this._linkProvider?.dispose(); - // TODO: Verify sharing a link provider works fine with removal of intersecting links this._linkProvider = this._terminalService.registerLinkProvider(new ExtensionTerminalLinkProvider(this._proxy)); } @@ -432,10 +431,7 @@ class ExtensionTerminalLinkProvider implements ITerminalExternalLinkProvider { startIndex: dto.startIndex, length: dto.length, label: dto.label, - activate(text: string) { - console.log('Activated! ' + text); - proxy.$activateLink(instance.id, dto.id); - } + activate: () => proxy.$activateLink(instance.id, dto.id) })); } } diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index aebd2ec79492a40acff1625bebd844b665c4666d..ec5c5bfe8b79153d294c59754eac848e6c0f25d8 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -627,8 +627,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ // when new links are provided. this._terminalLinkCache.delete(terminalId); - // TODO: Store link activate callback - // TODO: Discard of links when appropriate const result: ITerminalLinkDto[] = []; const context: vscode.TerminalLinkContext = { terminal, line }; const promises: vscode.ProviderResult<{ provider: vscode.TerminalLinkProvider, links: vscode.TerminalLink[] }>[] = []; diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts index 74d21c5c98342f08cec8d11b3dfc4caf466f3c89..6eb7c6b1ff1d12bf88fe7ddd4591190d3c91cde8 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalExternalLinkProviderAdapter.ts @@ -50,7 +50,6 @@ export class TerminalExternalLinkProviderAdapter extends TerminalBaseLinkProvide return []; } - // TODO: Add handling default handling of links via the target property on the ext host return externalLinks.map(link => { const bufferRange = convertLinkRangeToBuffer(lines, this._xterm.cols, { startColumn: link.startIndex + 1, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index ae0d3cd44300c1d830923ba0cb1486a7e1ff8eb2..6d8dbe9e0df4b6be8eb6cdd427f43dfead271dd3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -257,6 +257,7 @@ export interface ITerminalInstance { onFocused: Event; onProcessIdReady: Event; + onLinksReady: Event; onRequestExtHostProcess: Event; onDimensionsChanged: Event; onMaximumDimensionsChanged: Event; @@ -295,12 +296,11 @@ export interface ITerminalInstance { readonly exitCode: number | undefined; + readonly areLinksReady: boolean; + /** A promise that resolves when the terminal's pty/process have been created. */ processReady: Promise; - /** Whether xterm.js has been created. */ - isXtermReady: boolean; - /** A promise that resolves when xterm.js has been created. */ xtermReady: Promise; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 481e2ead62c0214177db911885c49e94a96813ca..78af804b970750cd915b6cfa82e904e3705c04f5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -95,6 +95,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _xtermReadyPromise: Promise; private _titleReadyPromise: Promise; private _titleReadyComplete: ((title: string) => any) | undefined; + private _areLinksReady: boolean = false; private _messageTitleDisposable: IDisposable | undefined; @@ -127,7 +128,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // TODO: How does this work with detached processes? // TODO: Should this be an event as it can fire twice? public get processReady(): Promise { return this._processManager.ptyProcessReady; } - public get isXtermReady(): boolean { return !!this._xterm; } + public get areLinksReady(): boolean { return this._areLinksReady; } public get xtermReady(): Promise { return this._xtermReadyPromise.then(() => { }); } public get exitCode(): number | undefined { return this._exitCode; } public get title(): string { return this._title; } @@ -146,6 +147,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public get onFocused(): Event { return this._onFocused.event; } private readonly _onProcessIdReady = new Emitter(); public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } + private readonly _onLinksReady = new Emitter(); + public get onLinksReady(): Event { return this._onLinksReady.event; } private readonly _onTitleChanged = new Emitter(); public get onTitleChanged(): Event { return this._onTitleChanged.event; } private readonly _onData = new Emitter(); @@ -203,7 +206,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._logService.trace(`terminalInstance#ctor (id: ${this.id})`, this._shellLaunchConfig); this._initDimensions(); - this._createProcess(); + this._createProcessManager(); this._xtermReadyPromise = this._createXterm(); this._xtermReadyPromise.then(() => { @@ -211,6 +214,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (_container) { this._attachToElement(_container); } + + this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows, this._accessibilityService.isScreenReaderOptimized()).then(error => { + if (error) { + this._onProcessExit(error); + } + }); }); this.addDisposable(this._configurationService.onDidChangeConfiguration(e => { @@ -418,6 +427,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { e.terminal = this; this._onBeforeHandleLink.fire(e); }); + this._areLinksReady = true; + this._onLinksReady.fire(this); }); this._commandTrackerAddon = new CommandTrackerAddon(); @@ -870,9 +881,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._terminalHasTextContextKey.set(isActive && this.hasSelection()); } - protected _createProcess(): void { + protected _createProcessManager(): void { this._processManager = this._instantiationService.createInstance(TerminalProcessManager, this._id, this._configHelper); - this._processManager.onProcessReady(() => this._onProcessIdReady.fire(this)); + this._processManager.onProcessReady(() => { + console.log('_processManager.onProcessReady'); + this._onProcessIdReady.fire(this); + }); this._processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)); this._processManager.onProcessData(data => this._onData.fire(data)); this._processManager.onProcessOverrideDimensions(e => this.setDimensions(e)); @@ -916,14 +930,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); }); } - - // Create the process asynchronously to allow the terminal's container to be created so - // dimensions are accurate - this._processManager.createProcess(this._shellLaunchConfig, this._cols, this._rows, this._accessibilityService.isScreenReaderOptimized()).then(error => { - if (error) { - this._onProcessExit(error); - } - }); } private getShellType(executable: string): TerminalShellType { @@ -1110,7 +1116,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Launch the process unless this is only a renderer. // In the renderer only cases, we still need to set the title correctly. const oldTitle = this._title; - this._createProcess(); + this._createProcessManager(); if (oldTitle !== this._title) { this.setTitle(this._title, TitleEventSource.Process); @@ -1497,7 +1503,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public registerLinkProvider(provider: ITerminalExternalLinkProvider): IDisposable { if (!this._linkManager) { - throw new Error('TerminalInstance.registerLinkProvider before xterm was created'); + throw new Error('TerminalInstance.registerLinkProvider before link manager was ready'); } return this._linkManager.registerExternalLinkProvider(this, provider); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 4fed2b313c6762d7322ed3e2c63888cb5e06a2a3..a9fb065be2d2e4168bcfd0f759fe677288b33b25 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -55,6 +55,7 @@ export class TerminalService implements ITerminalService { private _activeTabIndex: number; private _linkHandlers: { [key: string]: TerminalLinkHandlerCallback } = {}; private _linkProviders: Set = new Set(); + private _linkProviderDisposables: Map = new Map(); public get activeTabIndex(): number { return this._activeTabIndex; } public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } @@ -73,6 +74,8 @@ export class TerminalService implements ITerminalService { public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } private readonly _onInstanceProcessIdReady = new Emitter(); public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } + private readonly _onInstanceLinksReady = new Emitter(); + public get onInstanceLinksReady(): Event { return this._onInstanceLinksReady.event; } private readonly _onInstanceRequestSpawnExtHostProcess = new Emitter(); public get onInstanceRequestSpawnExtHostProcess(): Event { return this._onInstanceRequestSpawnExtHostProcess.event; } private readonly _onInstanceRequestStartExtensionTerminal = new Emitter(); @@ -131,7 +134,7 @@ export class TerminalService implements ITerminalService { const instance = this.getActiveInstance(); this._onActiveInstanceChanged.fire(instance ? instance : undefined); }); - this.onInstanceXtermReady(instance => this._setInstanceLinkProviders(instance)); + this.onInstanceLinksReady(instance => this._setInstanceLinkProviders(instance)); this._handleContextKeys(); } @@ -433,6 +436,7 @@ export class TerminalService implements ITerminalService { instance.addDisposable(instance.onDisposed(this._onInstanceDisposed.fire, this._onInstanceDisposed)); instance.addDisposable(instance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged)); instance.addDisposable(instance.onProcessIdReady(this._onInstanceProcessIdReady.fire, this._onInstanceProcessIdReady)); + instance.addDisposable(instance.onLinksReady(this._onInstanceLinksReady.fire, this._onInstanceLinksReady)); instance.addDisposable(instance.onDimensionsChanged(() => this._onInstanceDimensionsChanged.fire(instance))); instance.addDisposable(instance.onMaximumDimensionsChanged(() => this._onInstanceMaximumDimensionsChanged.fire(instance))); instance.addDisposable(instance.onFocus(this._onActiveInstanceChanged.fire, this._onActiveInstanceChanged)); @@ -483,20 +487,18 @@ export class TerminalService implements ITerminalService { } public registerLinkProvider(linkProvider: ITerminalExternalLinkProvider): IDisposable { - // TODO: Register it from the main thread class const disposables: IDisposable[] = []; this._linkProviders.add(linkProvider); for (const instance of this.terminalInstances) { // Only register immediately when xterm is ready - if (instance.isXtermReady) { + if (instance.areLinksReady) { disposables.push(instance.registerLinkProvider(linkProvider)); } } - console.log('registerLinkProvider register'); + this._linkProviderDisposables.set(linkProvider, disposables); return { dispose: () => { - console.log('registerLinkProvider dispose'); - // TODO: Remove from xterm instances + const disposables = this._linkProviderDisposables.get(linkProvider) || []; for (const disposable of disposables) { disposable.dispose(); } @@ -507,7 +509,9 @@ export class TerminalService implements ITerminalService { private _setInstanceLinkProviders(instance: ITerminalInstance): void { for (const linkProvider of this._linkProviders) { - instance.registerLinkProvider(linkProvider); + const disposables = this._linkProviderDisposables.get(linkProvider); + const provider = instance.registerLinkProvider(linkProvider); + disposables?.push(provider); } }