From 0a3ee299cef41da9eb44fa3fef6db0e925124c64 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 21 Jul 2022 15:17:22 -0700 Subject: [PATCH] Ensure folders with spaces don't impact link line/col Fixes #155532 --- .../browser/links/terminalLinkOpeners.ts | 15 ++++++++-- .../browser/links/terminalLinkOpeners.test.ts | 29 ++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts index 6fc5f4eb043..0e6e5347e90 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts @@ -23,6 +23,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; import { ISearchService } from 'vs/workbench/services/search/common/search'; +import { basename } from 'vs/base/common/path'; export class TerminalLocalFileLinkOpener implements ITerminalLinkOpener { constructor( @@ -35,7 +36,7 @@ export class TerminalLocalFileLinkOpener implements ITerminalLinkOpener { if (!link.uri) { throw new Error('Tried to open file link without a resolved URI'); } - const lineColumnInfo: ILineColumnInfo = this.extractLineColumnInfo(link.text); + const lineColumnInfo: ILineColumnInfo = this.extractLineColumnInfo(link.text, link.uri); const selection: ITextEditorSelection = { startLineNumber: lineColumnInfo.lineNumber, startColumn: lineColumnInfo.columnNumber @@ -51,12 +52,22 @@ export class TerminalLocalFileLinkOpener implements ITerminalLinkOpener { * * @param link Url link which may contain line and column number. */ - extractLineColumnInfo(link: string): ILineColumnInfo { + extractLineColumnInfo(link: string, uri?: URI): ILineColumnInfo { const lineColumnInfo: ILineColumnInfo = { lineNumber: 1, columnNumber: 1 }; + // If a URI was passed in the exact file is known, sanitize the link text such that the + // folders and file name do not contain whitespace. The actual path isn't important in + // extracting the line and column from the regex so this is safe + if (uri) { + const fileName = basename(uri.path); + const index = link.indexOf(fileName); + const endIndex = index + fileName.length; + link = link.slice(0, endIndex).replace(/\s/g, '_') + link.slice(endIndex); + } + // The local link regex only works for non file:// links, check these for a simple // `:line:col` suffix if (link.startsWith('file://')) { diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts index 225dd72bbdc..850f1ed8cec 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts @@ -7,7 +7,7 @@ import { deepStrictEqual } from 'assert'; import { Schemas } from 'vs/base/common/network'; import { OperatingSystem } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { ITextEditorSelection, ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IFileService, IFileStatWithPartialMetadata } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -27,6 +27,7 @@ import { Terminal } from 'xterm'; export interface ITerminalLinkActivationResult { source: 'editor' | 'search'; link: string; + selection?: ITextEditorSelection; } class TestCommandDetectionCapability extends CommandDetectionCapability { @@ -79,6 +80,10 @@ suite('Workbench - TerminalLinkOpeners', () => { source: 'editor', link: editor.resource?.toString() }; + // Only assert on selection if it's not the default value + if (editor.options?.selection && (editor.options.selection.startColumn !== 1 || editor.options.selection.startLineNumber !== 1)) { + activationResult.selection = editor.options.selection; + } } } as Partial); // /*editorServiceSpy = */instantiationService.spy(IEditorService, 'openEditor'); @@ -165,6 +170,28 @@ suite('Workbench - TerminalLinkOpeners', () => { }); }); + test('should extract line and column from links in a workspace containing spaces', async () => { + localFileOpener = instantiationService.createInstance(TerminalLocalFileLinkOpener, OperatingSystem.Linux); + const localFolderOpener = instantiationService.createInstance(TerminalLocalFolderInWorkspaceLinkOpener); + opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, Promise.resolve('/space folder'), localFileOpener, localFolderOpener, OperatingSystem.Linux); + fileService.setFiles([ + URI.from({ scheme: Schemas.file, path: '/space folder/foo/bar.txt' }) + ]); + await opener.open({ + text: './foo/bar.txt:10:5', + bufferRange: { start: { x: 1, y: 1 }, end: { x: 8, y: 1 } }, + type: TerminalBuiltinLinkType.Search + }); + deepStrictEqual(activationResult, { + link: 'file:///space%20folder/foo/bar.txt', + source: 'editor', + selection: { + startColumn: 5, + startLineNumber: 10 + }, + }); + }); + suite('macOS/Linux', () => { setup(() => { localFileOpener = instantiationService.createInstance(TerminalLocalFileLinkOpener, OperatingSystem.Linux);