diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index 4beda10f9eec02715ebcd3b78d915c5b00f15489..f53a7ab7d6ab985fffa277bc96b5be613d139687 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -30,6 +30,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { isEqualOrParent } from 'vs/base/common/resources'; +import { ISearchService } from 'vs/workbench/services/search/common/search'; +import { QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; const pathPrefix = '(\\.\\.?|\\~)'; const pathSeparatorClause = '\\/'; @@ -91,6 +93,7 @@ export class TerminalLinkManager extends DisposableStore { private _webLinksAddon: ITerminalAddon | undefined; private _linkProviders: IDisposable[] = []; private _hasBeforeHandleLinkListeners = false; + private readonly _fileQueryBuilder = this._instantiationService.createInstance(QueryBuilder); protected static _LINK_INTERCEPT_THRESHOLD = LINK_INTERCEPT_THRESHOLD; public static readonly LINK_INTERCEPT_THRESHOLD = TerminalLinkManager._LINK_INTERCEPT_THRESHOLD; @@ -120,7 +123,8 @@ export class TerminalLinkManager extends DisposableStore { @IQuickInputService private readonly _quickInputService: IQuickInputService, @ICommandService private readonly _commandService: ICommandService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, - @IHostService private readonly _hostService: IHostService + @IHostService private readonly _hostService: IHostService, + @ISearchService private readonly _searchService: ISearchService ) { super(); @@ -322,7 +326,24 @@ export class TerminalLinkManager extends DisposableStore { const tooltipWordCallback = (event: MouseEvent, link: string, location: IViewportRange) => { this._tooltipCallback(event, link, location, link => this._quickInputService.quickAccess.show(link)); }; - const wrappedWordActivateCallback = this._wrapLinkHandler(link => this._quickInputService.quickAccess.show(link)); + const wrappedWordActivateCallback = this._wrapLinkHandler(async link => { + const results = await this._searchService.fileSearch( + this._fileQueryBuilder.file(this._workspaceContextService.getWorkspace().folders, { + filePattern: link, + maxResults: 2 + }) + ); + + // If there was exactly one match, open it + if (results.results.length === 1) { + const match = results.results[0]; + await this._editorService.openEditor({ resource: match.resource, options: { pinned: true } }); + return; + } + + // Fallback to searching quick access + this._quickInputService.quickAccess.show(link); + }); this._linkProviders.push(this._xterm.registerLinkProvider( this._instantiationService.createInstance(TerminalWordLinkProvider, this._xterm, wrappedWordActivateCallback, tooltipWordCallback, this._leaveCallback) )); diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts index ee739f8b562fd5921882c2a55d2f1dc546c22b54..332c68184c315238f65d3e2f50279c367fbd1c80 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts @@ -11,6 +11,10 @@ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/ import { Event } from 'vs/base/common/event'; import { ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { TestRemotePathService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; class TestTerminalLinkManager extends TerminalLinkManager { public get localLinkRegex(): RegExp { @@ -84,12 +88,20 @@ const testConfigHelper: ITerminalConfigHelper = { }; suite('Workbench - TerminalLinkHandler', () => { + let instantiationService: TestInstantiationService; + + setup(() => { + instantiationService = new TestInstantiationService(); + instantiationService.stub(IEnvironmentService, TestEnvironmentService); + instantiationService.stub(IRemotePathService, new TestRemotePathService(TestEnvironmentService)); + }); + suite('localLinkRegex', () => { test('Windows', () => { const terminalLinkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Windows, userHome: '' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, instantiationService, 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); @@ -165,7 +177,7 @@ suite('Workbench - TerminalLinkHandler', () => { const terminalLinkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, instantiationService, 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); @@ -233,7 +245,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Windows, userHome: 'C:\\Users\\Me' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, instantiationService, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base'; assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base\\src\\file1'); @@ -246,7 +258,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Windows, userHome: 'C:\\Users\\M e' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, instantiationService, null!, null!, null!, null!, null!); linkHandler.processCwd = 'C:\\base dir'; assert.equal(linkHandler.preprocessPath('./src/file1'), 'C:\\base dir\\src\\file1'); @@ -260,7 +272,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '/home/me' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, instantiationService, null!, null!, null!, null!, null!); linkHandler.processCwd = '/base'; assert.equal(linkHandler.preprocessPath('./src/file1'), '/base/src/file1'); @@ -273,7 +285,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '/home/me' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, instantiationService, null!, null!, null!, null!, null!); assert.equal(linkHandler.preprocessPath('./src/file1'), null); assert.equal(linkHandler.preprocessPath('src/file2'), null); @@ -287,7 +299,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '' - } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, null!, new MockTerminalInstanceService(), null!, null!, instantiationService, null!, null!, null!, null!, null!); function assertAreGoodMatches(matches: RegExpMatchArray | null) { if (matches) { @@ -318,7 +330,7 @@ suite('Workbench - TerminalLinkHandler', () => { const linkHandler = new TestTerminalLinkManager(new TestXterm() as any, { os: OperatingSystem.Linux, userHome: '' - } as any, testConfigHelper, null!, null!, new TestConfigurationService(), new MockTerminalInstanceService(), null!, null!, null!, null!); + } as any, testConfigHelper, null!, null!, new TestConfigurationService(), new MockTerminalInstanceService(), null!, null!, instantiationService, null!, null!, null!, null!, null!); linkHandler.onBeforeHandleLink(e => { if (e.link === 'https://www.microsoft.com') { intercepted = true; diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts index 3c63cde4bab61c28d7a5a96d103186deedb17a19..18aca9b8ccde1dd2519b89690c94cdc7784364ed 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalValidatedLocalLinkProvider.test.ts @@ -8,6 +8,7 @@ import { TerminalValidatedLocalLinkProvider } from 'vs/workbench/contrib/termina import { Terminal, ILink, IBufferRange, IBufferCellPosition } from 'xterm'; import { OperatingSystem } from 'vs/base/common/platform'; import { format } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; const unixLinks = [ '/foo', @@ -66,7 +67,7 @@ const supportedLinkFormats: LinkFormatInfo[] = [ suite('Workbench - TerminalValidatedLocalLinkProvider', () => { async function assertLink(text: string, os: OperatingSystem, expected: { text: string, range: [number, number][] }) { const xterm = new Terminal(); - const provider = new TerminalValidatedLocalLinkProvider(xterm, os, () => { }, () => { }, () => { }, (_, cb) => { cb(true); }); + const provider = new TerminalValidatedLocalLinkProvider(xterm, os, () => { }, () => { }, () => { }, () => { }, (_, cb) => { cb({ uri: URI.file('/'), isDirectory: false }); }); // Write the text and wait for the parser to finish await new Promise(r => xterm.write(text, r));