Repository: microsoft/vscode (TypeScript)
Date: 2022-08-24 23:51:19+00:00
Task type: Bug Fix

Issue:
can't go to line and column by click terminal link if project path contains space
My project is under `/Users/yutengjing/Library/Application Support/Adobe/CEP/extensions/vscode-fe-helper`：

https://user-images.githubusercontent.com/41773861/179580458-2e804f82-5acc-4e07-b2c3-e1b246deebfa.mov



Reproduce steps:

1. using vscode open a folder which file path contains space, for example: `/Users/yutengjing/Library/Application Support/project-folder`
2. open an integrated terminal
3. echo `file/under/project.ts:line:col`
4. click the path link

Versions:

```
Version: 1.70.0-insider (Universal)
Commit: 1ccfe2bbe804a20a7c657ca42368987fd1adac58
Date: 2022-07-18T08:46:51.862Z
Electron: 18.3.5
Chromium: 100.0.4896.160
Node.js: 16.13.2
V8: 10.0.139.17-electron.0
OS: Darwin x64 21.5.0
```

@Tyriar


Issue discussion:
@tjx666 nice I can repro with the space in the name, thanks for following up 👍 
I can still repro with Windows.
I looked into this, and it will be tricky because we currently use space as one of the characters which indicates the end of a path. 

One idea would be to check if such a link exists (using our original regex) and if not, use regex on the subsequent sequence - then concatenating the two results 

for `/path/to folder/file.txt`

First result would be `/path to` ❌ doesn't exist

process `folder/file.txt` 

`/path to ` + ` folder/file.txt` 👍 exists

@meganrogge 

https://user-images.githubusercontent.com/41773861/186525917-e60d63d3-b63b-4c12-9b60-bea304899928.mov

Look the above video, I have several questions.

1. the path `/a/b/c` is not a existed path in the disk, but also clickable.
2. when I click the `/a/b/c`, it open a file which is not `/a/b/c`
3. 

```
安装包路径：/Users/yutengjing/code/hammer/packages/app/dist
```

why the whole string is recogonized to a path? the real path is:

```
/Users/yutengjing/code/hammer/packages/app/dist
```


@meganrogge this is actually about when the parents of the link have a space, it's expected currently that an absolute path with a space won't work.
@tyriar is it expected that a local link with a space somewhere within the path is not detected? that is the case currently - verified via adding a test to `terminalLocalLinkDetector`
@meganrogge absolute paths (eg. `/home/foo bar/file`) won't work because of https://github.com/microsoft/vscode/issues/97941. Relative paths without spaces with absolute ancestors that have spaces (eg. `./file` inside `/home/foo bar`) should work but don't in some cases.

Solution patch:
diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts
--- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts
+++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts
@@ -66,12 +66,18 @@ export class TerminalLocalFileLinkOpener implements ITerminalLinkOpener {
 		const fileNameEndIndex: number = index !== -1 ? index + fileName.length : link.length;
 
 		// Sanitize the link text such that the folders and file name do not contain whitespace.
-		link = link.slice(0, fileNameEndIndex).replace(/\s/g, '_') + link.slice(fileNameEndIndex);
+		let sanitizedLink = link.slice(0, fileNameEndIndex).replace(/\s/g, '_') + link.slice(fileNameEndIndex);
+
+		// Remove / suffixes from Windows paths such that the windows link regex works
+		// (eg. /c:/file -> c:/file)
+		if (this._os === OperatingSystem.Windows && sanitizedLink.match(/^\/[a-z]:\//i)) {
+			sanitizedLink = sanitizedLink.slice(1);
+		}
 
 		// The local link regex only works for non file:// links, check these for a simple
 		// `:line:col` suffix
-		if (link.startsWith('file://')) {
-			const simpleMatches = link.match(/:(\d+)(:(\d+))?$/);
+		if (sanitizedLink.startsWith('file://')) {
+			const simpleMatches = sanitizedLink.match(/:(\d+)(:(\d+))?$/);
 			if (simpleMatches) {
 				if (simpleMatches[1] !== undefined) {
 					lineColumnInfo.lineNumber = parseInt(simpleMatches[1]);
@@ -83,7 +89,7 @@ export class TerminalLocalFileLinkOpener implements ITerminalLinkOpener {
 			return lineColumnInfo;
 		}
 
-		const matches: string[] | null = getLocalLinkRegex(this._os).exec(link);
+		const matches: string[] | null = getLocalLinkRegex(this._os).exec(sanitizedLink);
 		if (!matches) {
 			return lineColumnInfo;
 		}


Test patch:
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
--- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts
+++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts
@@ -292,9 +292,9 @@ suite('Workbench - TerminalLinkOpeners', () => {
 			test('should extract line and column from links in a workspace containing spaces', async () => {
 				localFileOpener = instantiationService.createInstance(TerminalLocalFileLinkOpener, OperatingSystem.Windows);
 				const localFolderOpener = instantiationService.createInstance(TerminalLocalFolderInWorkspaceLinkOpener);
-				opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, Promise.resolve('/space folder'), localFileOpener, localFolderOpener, OperatingSystem.Windows);
+				opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, Promise.resolve('c:/space folder'), localFileOpener, localFolderOpener, OperatingSystem.Windows);
 				fileService.setFiles([
-					URI.from({ scheme: Schemas.file, path: '/space folder/foo/bar.txt' })
+					URI.from({ scheme: Schemas.file, path: 'c:/space folder/foo/bar.txt' })
 				]);
 				await opener.open({
 					text: './foo/bar.txt:10:5',
@@ -302,7 +302,7 @@ suite('Workbench - TerminalLinkOpeners', () => {
 					type: TerminalBuiltinLinkType.Search
 				});
 				deepStrictEqual(activationResult, {
-					link: 'file:///space%20folder/foo/bar.txt',
+					link: 'file:///c%3A/space%20folder/foo/bar.txt',
 					source: 'editor',
 					selection: {
 						startColumn: 5,
@@ -315,7 +315,7 @@ suite('Workbench - TerminalLinkOpeners', () => {
 					type: TerminalBuiltinLinkType.Search
 				});
 				deepStrictEqual(activationResult, {
-					link: 'file:///space%20folder/foo/bar.txt',
+					link: 'file:///c%3A/space%20folder/foo/bar.txt',
 					source: 'editor',
 					selection: {
 						startColumn: 5,