From 2969442af9b234940984eab42e3834c19a3be09d Mon Sep 17 00:00:00 2001 From: Alexandra Buzila Date: Tue, 26 Mar 2024 11:06:00 +0100 Subject: [PATCH] Fix review comments - match files without a starting separator, e.g. 'yarn.lock:25:2' --- .../browser/terminal-file-link-provider.ts | 72 ++++++++++++------- .../src/browser/terminal-frontend-module.ts | 4 +- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/packages/terminal/src/browser/terminal-file-link-provider.ts b/packages/terminal/src/browser/terminal-file-link-provider.ts index 8bc0d46a769b7..f7fc082ca43ad 100644 --- a/packages/terminal/src/browser/terminal-file-link-provider.ts +++ b/packages/terminal/src/browser/terminal-file-link-provider.ts @@ -24,12 +24,14 @@ import { TerminalWidget } from './base/terminal-widget'; import { TerminalLink, TerminalLinkProvider } from './terminal-link-provider'; import { TerminalWidgetImpl } from './terminal-widget-impl'; import { FileSearchService } from '@theia/file-search/lib/common/file-search-service'; +import { WorkspaceService } from '@theia/workspace/lib/browser'; @injectable() export class FileLinkProvider implements TerminalLinkProvider { @inject(OpenerService) protected readonly openerService: OpenerService; @inject(FileService) protected fileService: FileService; - @inject(FileSearchService) private searchService: FileSearchService; + @inject(FileSearchService) protected searchService: FileSearchService; + @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; async provideLinks(line: string, terminal: TerminalWidget): Promise { const links: TerminalLink[] = []; @@ -44,36 +46,47 @@ export class FileLinkProvider implements TerminalLinkProvider { handle: () => this.open(match, terminal) }); } else { - const cwd = await this.getCwd(terminal); let searchTerm = await this.extractPath(match); - if (searchTerm) { - // remove any leading ./, ../ etc. as they can't be searched - searchTerm = searchTerm.replace(/^(\.+[\\/])+/, ''); - // try and find a matching file in the workspace - const files = (await this.searchService.find(searchTerm, { - rootUris: [cwd.toString()], - fuzzyMatch: true, - limit: 1 - })); - if (files.length) { - const fileUri = new URI(files[0]); - const valid = await this.isValidFileURI(fileUri); - if (valid) { - const position = await this.extractPosition(match); - links.push({ - startIndex: regExp.lastIndex - match.length, - length: match.length, - handle: () => this.openURI(fileUri, position) - }); - } - - } + const fileUri = await this.isValidWorkspaceFile(searchTerm, terminal); + if (fileUri) { + const position = await this.extractPosition(match); + links.push({ + startIndex: regExp.lastIndex - match.length, + length: match.length, + handle: () => this.openURI(fileUri, position) + }); } } } return links; } + protected async isValidWorkspaceFile(searchTerm: string | undefined, terminal: TerminalWidget): Promise { + if (!searchTerm) { + return undefined; + } + const cwd = await this.getCwd(terminal); + // remove any leading ./, ../ etc. as they can't be searched + searchTerm = searchTerm.replace(/^(\.+[\\/])+/, ''); + const workspaceRoots = this.workspaceService.tryGetRoots().map(root => root.resource.toString()); + // try and find a matching file in the workspace + const files = (await this.searchService.find(searchTerm, { + rootUris: [cwd.toString(), ...workspaceRoots], + fuzzyMatch: true, + limit: 1 + })); + // checks if the string end in a separator + searchTerm + const regex = new RegExp(`[\\\\|\\/]${searchTerm}$`); + if (files.length && regex.test(files[0])) { + const fileUri = new URI(files[0]); + const valid = await this.isValidFileURI(fileUri); + if (valid) { + return fileUri + } + + } + } + protected async createRegExp(): Promise { const baseLocalLinkClause = OS.backend.isWindows ? winLocalLinkClause : unixLocalLinkClause; return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`, 'g'); @@ -188,6 +201,17 @@ export class FileDiffPostLinkProvider extends FileLinkProvider { } } +@injectable() +export class LocalFileLinkProvider extends FileLinkProvider { + override async createRegExp(): Promise { + //match links that might not start with a separator, e.g. 'foo.bar' + const baseLocalLinkClause = OS.backend.isWindows ? + '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)*)' + : '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)*)'; + return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`, 'g'); + } +} + // The following regular expressions are taken from: // https://github.com/microsoft/vscode/blob/b118105bf28d773fbbce683f7230d058be2f89a7/src/vs/workbench/contrib/terminal/browser/links/terminalLocalLinkDetector.ts#L34-L58 diff --git a/packages/terminal/src/browser/terminal-frontend-module.ts b/packages/terminal/src/browser/terminal-frontend-module.ts index e5547eb1d64ad..5b105f527dbd9 100644 --- a/packages/terminal/src/browser/terminal-frontend-module.ts +++ b/packages/terminal/src/browser/terminal-frontend-module.ts @@ -41,7 +41,7 @@ import { TerminalThemeService } from './terminal-theme-service'; import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; import { createXtermLinkFactory, TerminalLinkProvider, TerminalLinkProviderContribution, XtermLinkFactory } from './terminal-link-provider'; import { UrlLinkProvider } from './terminal-url-link-provider'; -import { FileDiffPostLinkProvider, FileDiffPreLinkProvider, FileLinkProvider } from './terminal-file-link-provider'; +import { FileDiffPostLinkProvider, FileDiffPreLinkProvider, FileLinkProvider, LocalFileLinkProvider } from './terminal-file-link-provider'; import { ContributedTerminalProfileStore, DefaultProfileStore, DefaultTerminalProfileService, TerminalProfileService, TerminalProfileStore, UserTerminalProfileStore @@ -123,6 +123,8 @@ export default new ContainerModule(bind => { bind(TerminalLinkProvider).toService(FileDiffPreLinkProvider); bind(FileDiffPostLinkProvider).toSelf().inSingletonScope(); bind(TerminalLinkProvider).toService(FileDiffPostLinkProvider); + bind(LocalFileLinkProvider).toSelf().inSingletonScope(); + bind(TerminalLinkProvider).toService(LocalFileLinkProvider); bind(ContributedTerminalProfileStore).to(DefaultProfileStore).inSingletonScope(); bind(UserTerminalProfileStore).to(DefaultProfileStore).inSingletonScope();