diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 421a285c25c..6dbe9fb8999 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1086,6 +1086,22 @@ declare module 'vscode' { //#endregion + //#region Terminal link handlers https://github.com/microsoft/vscode/issues/91606 + + export namespace window { + export function registerTerminalLinkHandler(handler: TerminalLinkHandler): Disposable; + } + + export interface TerminalLinkHandler { + /** + * @return true when the link was handled (and should not be considered by + * other providers including the default), false when the link was not handled. + */ + handleLink(terminal: Terminal, link: string): ProviderResult; + } + + //#endregion + //#region Joh -> exclusive document filters export interface DocumentFilter { diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 372db8a6af2..323cdc2fa47 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore, Disposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { 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'; import { URI } from 'vs/base/common/uri'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { ITerminalInstanceService, ITerminalService, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; @@ -23,6 +23,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape private readonly _terminalProcesses = new Map>(); private readonly _terminalProcessesReady = new Map void>(); private _dataEventTracker: TerminalDataEventTracker | undefined; + private _linkHandler: IDisposable | undefined; constructor( extHostContext: IExtHostContext, @@ -146,6 +147,24 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } } + public $startHandlingLinks(): void { + console.log('start'); + this._linkHandler?.dispose(); + this._linkHandler = this._terminalService.addLinkHandler(this._remoteAuthority || '', e => this._handleLink(e)); + } + + public $stopHandlingLinks(): void { + console.log('stop'); + this._linkHandler?.dispose(); + } + + private async _handleLink(e: ITerminalBeforeHandleLinkEvent): Promise { + if (!e.terminal) { + return false; + } + return this._proxy.$handleLink(e.terminal.id, e.link); + } + private _onActiveTerminalChanged(terminalId: number | null): void { this._proxy.$acceptActiveTerminalChanged(terminalId); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index a95e4ac7b1e..d8d58647f09 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -564,6 +564,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs); }, + registerTerminalLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable { + checkProposedApiEnabled(extension); + return extHostTerminalService.registerLinkHandler(handler); + }, registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension); }, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2e93f3aa9b8..45812459a6f 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -431,6 +431,8 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $show(terminalId: number, preserveFocus: boolean): void; $startSendingDataEvents(): void; $stopSendingDataEvents(): void; + $startHandlingLinks(): void; + $stopHandlingLinks(): void; // Process $sendProcessTitle(terminalId: number, title: string): void; @@ -1310,6 +1312,7 @@ export interface ExtHostTerminalServiceShape { $acceptWorkspacePermissionsChanged(isAllowed: boolean): void; $getAvailableShells(): Promise; $getDefaultShellAndArgs(useAutomationShell: boolean): Promise; + $handleLink(id: number, link: string): Promise; } export interface ExtHostSCMShape { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index bccacd0543f..94eb74c8447 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -14,6 +14,7 @@ import { timeout } from 'vs/base/common/async'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable as VSCodeDisposable } from './extHostTypes'; export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { @@ -34,6 +35,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape { attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void; getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string; getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string; + registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable } export const IExtHostTerminalService = createDecorator('IExtHostTerminalService'); @@ -535,6 +537,39 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ return id; } + private _linkHandlers: Set = new Set(); + public registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable { + this._linkHandlers.add(handler); + if (this._linkHandlers.size === 1) { + this._proxy.$startHandlingLinks(); + } + return new VSCodeDisposable(() => { + this._linkHandlers.delete(handler); + if (this._linkHandlers.size === 0) { + this._proxy.$stopHandlingLinks(); + } + }); + } + + public async $handleLink(id: number, link: string): Promise { + const terminal = this._getTerminalById(id); + if (!terminal) { + return false; + } + + // Call each handler synchronously so multiple handlers aren't triggered at once + const it = this._linkHandlers.values(); + let next = it.next(); + while (!next.done) { + const handled = await next.value.handleLink(terminal, link); + if (handled) { + return true; + } + next = it.next(); + } + return false; + } + private _onProcessExit(id: number, exitCode: number | undefined): void { this._bufferer.stopBuffering(id); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index bac468cd33a..e28e9e597e5 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -131,6 +131,14 @@ export interface ITerminalService { findNext(): void; findPrevious(): void; + /** + * Link handlers can be registered here to allow intercepting links clicked in the terminal. + * When a link is clicked, the link will be considered handled when the first interceptor + * resolves with true. It will be considered not handled when _all_ link handlers resolve with + * false, or 3 seconds have elapsed. + */ + addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable; + selectDefaultWindowsShell(): Promise; setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; @@ -179,6 +187,18 @@ export enum WindowsShellType { } export type TerminalShellType = WindowsShellType | undefined; +export const LINK_INTERCEPT_THRESHOLD = 3000; + +export interface ITerminalBeforeHandleLinkEvent { + terminal?: ITerminalInstance; + /** The text of the link */ + link: string; + /** Call with whether the link was handled by the interceptor */ + resolve(wasHandled: boolean): void; +} + +export type TerminalLinkHandlerCallback = (e: ITerminalBeforeHandleLinkEvent) => Promise; + export interface ITerminalInstance { /** * The ID of the terminal instance, this is an arbitrary number only used to identify the @@ -240,6 +260,11 @@ export interface ITerminalInstance { */ onExit: Event; + /** + * Attach a listener to intercept and handle link clicks in the terminal. + */ + onBeforeHandleLink: Event; + readonly exitCode: number | undefined; processReady: Promise; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index c79d87d2150..153e95815ab 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -30,7 +30,7 @@ import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGR import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { TerminalLinkHandler } from 'vs/workbench/contrib/terminal/browser/terminalLinkHandler'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalInstanceService, ITerminalInstance, TerminalShellType } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalInstance, TerminalShellType, ITerminalBeforeHandleLinkEvent } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalProcessManager } from 'vs/workbench/contrib/terminal/browser/terminalProcessManager'; import { Terminal as XTermTerminal, IBuffer, ITerminalAddon } from 'xterm'; import { SearchAddon, ISearchOptions } from 'xterm-addon-search'; @@ -250,28 +250,30 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public get commandTracker(): CommandTrackerAddon | undefined { return this._commandTrackerAddon; } public get navigationMode(): INavigationMode | undefined { return this._navigationModeAddon; } - private readonly _onExit = new Emitter(); + private readonly _onExit = this._register(new Emitter()); public get onExit(): Event { return this._onExit.event; } - private readonly _onDisposed = new Emitter(); + private readonly _onDisposed = this._register(new Emitter()); public get onDisposed(): Event { return this._onDisposed.event; } - private readonly _onFocused = new Emitter(); + private readonly _onFocused = this._register(new Emitter()); public get onFocused(): Event { return this._onFocused.event; } - private readonly _onProcessIdReady = new Emitter(); + private readonly _onProcessIdReady = this._register(new Emitter()); public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } - private readonly _onTitleChanged = new Emitter(); + private readonly _onTitleChanged = this._register(new Emitter()); public get onTitleChanged(): Event { return this._onTitleChanged.event; } - private readonly _onData = new Emitter(); + private readonly _onData = this._register(new Emitter()); public get onData(): Event { return this._onData.event; } - private readonly _onLineData = new Emitter(); + private readonly _onLineData = this._register(new Emitter()); public get onLineData(): Event { return this._onLineData.event; } - private readonly _onRequestExtHostProcess = new Emitter(); + private readonly _onRequestExtHostProcess = this._register(new Emitter()); public get onRequestExtHostProcess(): Event { return this._onRequestExtHostProcess.event; } - private readonly _onDimensionsChanged = new Emitter(); + private readonly _onDimensionsChanged = this._register(new Emitter()); public get onDimensionsChanged(): Event { return this._onDimensionsChanged.event; } - private readonly _onMaximumDimensionsChanged = new Emitter(); + private readonly _onMaximumDimensionsChanged = this._register(new Emitter()); public get onMaximumDimensionsChanged(): Event { return this._onMaximumDimensionsChanged.event; } - private readonly _onFocus = new Emitter(); + private readonly _onFocus = this._register(new Emitter()); public get onFocus(): Event { return this._onFocus.event; } + private readonly _onBeforeHandleLink = this._register(new Emitter()); + public get onBeforeHandleLink(): Event { return this._onBeforeHandleLink.event; } public constructor( private readonly _terminalFocusContextKey: IContextKey, @@ -523,6 +525,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); } this._linkHandler = this._instantiationService.createInstance(TerminalLinkHandler, xterm, this._processManager, this._configHelper); + this._linkHandler.onBeforeHandleLink(e => { + console.log('terminalinstance fire'); + e.terminal = this; + this._onBeforeHandleLink.fire(e); + }); }); this._commandTrackerAddon = new CommandTrackerAddon(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts index 18169644b84..114ec81dbf4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalLinkHandler.ts @@ -16,9 +16,11 @@ import { IFileService } from 'vs/platform/files/common/files'; import { Terminal, ILinkMatcherOptions, IViewportRange } from 'xterm'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { posix, win32 } from 'vs/base/common/path'; -import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstanceService, ITerminalBeforeHandleLinkEvent, LINK_INTERCEPT_THRESHOLD } from 'vs/workbench/contrib/terminal/browser/terminal'; import { OperatingSystem, isMacintosh } from 'vs/base/common/platform'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; +import { Emitter, Event } from 'vs/base/common/event'; +import { ILogService } from 'vs/platform/log/common/log'; const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; @@ -74,6 +76,18 @@ export class TerminalLinkHandler { private _gitDiffPostImagePattern: RegExp; private readonly _tooltipCallback: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void; private readonly _leaveCallback: () => void; + private _hasBeforeHandleLinkListeners = false; + + private readonly _onBeforeHandleLink = new Emitter({ + onFirstListenerAdd: () => this._hasBeforeHandleLinkListeners = true, + onLastListenerRemove: () => this._hasBeforeHandleLinkListeners = false + }); + /** + * Allows intercepting links and handling them outside of the default link handler. When fired + * the listener has a set amount of time to handle the link or the default handler will fire. + * This was designed to only be handled by a single listener. + */ + public get onBeforeHandleLink(): Event { return this._onBeforeHandleLink.event; } constructor( private _xterm: Terminal, @@ -83,7 +97,8 @@ export class TerminalLinkHandler { @IEditorService private readonly _editorService: IEditorService, @IConfigurationService private readonly _configurationService: IConfigurationService, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, - @IFileService private readonly _fileService: IFileService + @IFileService private readonly _fileService: IFileService, + @ILogService private readonly _logService: ILogService ) { // Matches '--- a/src/file1', capturing 'src/file1' in group 1 this._gitDiffPreImagePattern = /^--- a\/(\S*)/; @@ -213,6 +228,7 @@ export class TerminalLinkHandler { public dispose(): void { this._hoverDisposables.dispose(); + this._onBeforeHandleLink.dispose(); } private _wrapLinkHandler(handler: (uri: string) => boolean | void): XtermLinkMatcherHandler { @@ -245,10 +261,33 @@ export class TerminalLinkHandler { } private _handleLocalLink(link: string): PromiseLike { - return this._resolvePath(link).then(resolvedLink => { + return this._resolvePath(link).then(async resolvedLink => { if (!resolvedLink) { return Promise.resolve(null); } + + // Allow the link to be intercepted if there are listeners + if (this._hasBeforeHandleLinkListeners) { + const wasHandled = await new Promise(r => { + const timeoutId = setTimeout(() => { + canceled = true; + this._logService.error('An extension intecepted a terminal link but did not return'); + r(false); + }, LINK_INTERCEPT_THRESHOLD); + let canceled = false; + const resolve = (handled: boolean) => { + if (!canceled) { + clearTimeout(timeoutId); + r(handled); + } + }; + this._onBeforeHandleLink.fire({ link, resolve }); + }); + if (wasHandled) { + return; + } + } + const lineColumnInfo: LineColumnInfo = this.extractLineColumnInfo(link); const selection: ITextEditorSelection = { startLineNumber: lineColumnInfo.lineNumber, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 2dfb87a8d90..1ffb1e804f4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -15,7 +15,7 @@ import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; -import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, ITerminalInstance, ITerminalTab, TerminalShellType, WindowsShellType, TerminalLinkHandlerCallback, LINK_INTERCEPT_THRESHOLD } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; @@ -30,6 +30,7 @@ import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; import { find } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; import { IViewsService } from 'vs/workbench/common/views'; +import { IDisposable } from 'vs/base/common/lifecycle'; interface IExtHostReadyEntry { promise: Promise; @@ -411,6 +412,51 @@ export class TerminalService implements ITerminalService { 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)); + instance.addDisposable(instance.onBeforeHandleLink(async e => { + // No link handlers have been registered + const keys = Object.keys(this._linkHandlers); + if (keys.length === 0) { + e.resolve(false); + return; + } + + // Fire each link interceptor and wait for either a true, all false or the cancel time + let resolved = false; + const promises: Promise[] = []; + const timeout = setTimeout(() => { + resolved = true; + e.resolve(false); + }, LINK_INTERCEPT_THRESHOLD); + for (let i = 0; i < keys.length; i++) { + const p = this._linkHandlers[keys[i]](e); + p.then(handled => { + if (!resolved && handled) { + resolved = true; + clearTimeout(timeout); + e.resolve(true); + } + }); + promises.push(p); + } + await Promise.all(promises); + if (!resolved) { + resolved = true; + clearTimeout(timeout); + e.resolve(false); + } + })); + } + + private _linkHandlers: { [key: string]: TerminalLinkHandlerCallback } = {}; + public addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable { + this._linkHandlers[key] = callback; + return { + dispose: () => { + if (this._linkHandlers[key] === callback) { + delete this._linkHandlers[key]; + } + } + }; } private _getTabForInstance(instance: ITerminalInstance): ITerminalTab | undefined { diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts index dcfcf378d02..5e114e9d60a 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalLinkHandler.test.ts @@ -81,7 +81,7 @@ suite('Workbench - TerminalLinkHandler', () => { const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { os: OperatingSystem.Windows, userHome: '' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -157,7 +157,7 @@ suite('Workbench - TerminalLinkHandler', () => { const terminalLinkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!); function testLink(link: string, linkUrl: string, lineNo?: string, columnNo?: string) { assert.equal(terminalLinkHandler.extractLinkUrl(link), linkUrl); assert.equal(terminalLinkHandler.extractLinkUrl(`:${link}:`), linkUrl); @@ -225,7 +225,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { os: OperatingSystem.Windows, userHome: 'C:\\Users\\Me' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!); linkHandler.processCwd = 'C:\\base'; assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\src\\file1'); @@ -238,7 +238,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { os: OperatingSystem.Windows, userHome: 'C:\\Users\\M e' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!); linkHandler.processCwd = 'C:\\base dir'; assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base dir\\src\\file1'); @@ -252,7 +252,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '/home/me' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!); linkHandler.processCwd = '/base'; assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/src/file1'); @@ -265,7 +265,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '/home/me' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!); assert.equal(linkHandler.preprocessPath('./src/file1'), null); assert.equal(linkHandler.preprocessPath('src/file2'), null); @@ -279,7 +279,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkHandler(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!); function assertAreGoodMatches(matches: RegExpMatchArray | null) { if (matches) {