Change:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.71.0-insider OS Version: macOS Big Sur 11.6 Steps to Reproduce: a link in terminal containing "[" or "]" characters (ex. ) to cmd+click the link, observe that it is split on those characters. For example, links to , , , , and . In reality this is one valid path (nextjs uses "[ ]" to identify parameters in URLs) <img width="526" alt="Screen Shot 2022-09-22 at 5 12 00 PM" src="https://user-"
This works for urls: ! Not local paths: !
reported that it does (kind of) work: ! We would need to review all link detectors for this.
This happens with local links because [] are currently excluded chars:

Discussion:
PathSeparatorClause = '/', // '":; are allowed in paths but they are often separators so ignore them // Also disallow  to prevent a catastropic backtracking case #24795 <del> ExcludedPathCharactersClause = '[^0s!`&*()[]'":;]', </del> <ins> ExcludedPathCharactersClause = '[^0s!`&*()'":;]', ExcludedStartPathCharactersClause = '[^0s!`&*()[]'":;]', </ins> WinOtherPathPrefix = '..?|~', WinPathSeparatorClause = '(|/)', <del> WinExcludedPathCharactersClause = '[^0<>?|/s!`&*()[]'":;]', </del> <ins> WinExcludedPathCharactersClause = '[^0<>?|/s!`&*()'":;]', WinExcludedStartPathCharactersClause = '[^0<>?|/s!`&*()[]'":;]', </ins> } /** A regex that matches paths in the form /foo, ~/foo, ./foo, ../foo, foo/bar */ <del> export const unixLocalLinkClause = '((' + RegexPathConstants.PathPrefix + '|(' + RegexPathConstants.ExcludedPathCharactersClause + ')+)?(' + RegexPathConstants.PathSeparatorClause + '(' + RegexPathConstants.ExcludedPathCharactersClause + ')+)+)'; </del> <ins> export const unixLocalLinkClause = '((' + RegexPathConstants.PathPrefix + '|(' + RegexPathConstants.ExcludedStartPathCharactersClause + RegexPathConstants.ExcludedPathCharactersClause + '*))?(' + RegexPathConstants.PathSeparatorClause + '(' + RegexPathConstants.ExcludedPathCharactersClause + ')+)+)'; </ins> export const winDrivePrefix = '(?:?)?[a-zA-Z]:'; /** A regex that matches paths in the form ?c:foo c:foo, ~foo, .foo, ..foo, foobar */ <del> export const winLocalLinkClause = '((' + `(${winDrivePrefix}|${RegexPathConstants.WinOtherPathPrefix})` + '|(' + RegexPathConstants.WinExcludedPathCharactersClause + ')+)?(' + RegexPathConstants.WinPathSeparatorClause + '(' + RegexPathConstants.WinExcludedPathCharactersClause + ')+)+)'; </del> <ins> export const winLocalLinkClause = '((' + `(${winDrivePrefix}|${RegexPathConstants.WinOtherPathPrefix})` + '|(' + RegexPathConstants.WinExcludedStartPathCharactersClause + RegexPathConstants.WinExcludedPathCharactersClause + '*))?(' + RegexPathConstants.WinPathSeparatorClause + '(' + RegexPathConstants.WinExcludedPathCharactersClause + ')+)+)'; </ins> // TODO: This should eventually move to the more structured terminalLinkParsing /** As xterm reads from DOM, space in that case is nonbreaking char ASCII code - 160,

break; } <ins> // HACK: In order to support both links containing [ and ] characters as well as the // `<file>[<line>, <col>]` pattern, we need to detect the part after the comma and fix // up the link here as before that will be matched as a regular file path. if (link.match(/[d+,$/)) { const partialText = text.slice(rex.lastIndex); const suffixMatch = partialText.match(/^ d+]/); if (suffixMatch) { link += suffixMatch[0]; } } </ins> // Adjust the link range to exclude a/ and b/ if it looks like a git diff if ( // --- a/foo/bar

} } <ins> // If any candidates end with special characters that are likely to not be part of the // link, add a candidate excluding them. const specialEndCharRegex = /[[]]$/; const trimRangeMap: Map<string, number> = new Map(); const specialEndLinkCandidates: string[] = []; for (const candidate of linkCandidates) { let previous = candidate; let removed = previous.replace(specialEndCharRegex, ''); let trimRange = 0; while (removed !== previous) { trimRange++; specialEndLinkCandidates.push(removed); trimRangeMap.set(removed, trimRange); previous = removed; removed = removed.replace(specialEndCharRegex, ''); } } linkCandidates.push(...specialEndLinkCandidates); </ins> const linkStat = await this._validateLinkCandidates(linkCandidates); // Create the link if validated

} else { type = TerminalBuiltinLinkType.LocalFile; } <ins> // Offset the buffer range if the link range was trimmed const trimRange = trimRangeMap.get(linkStat.link); if (trimRange) { bufferRange.end.x -= trimRange; if (bufferRange.end.x < 0) { bufferRange.end.y--; bufferRange.end.x += this.xterm.cols; } } </ins> links.push({ text: linkStat.link, uri: linkStat.uri,

deepStrictEqual(actualLinks, expectedLinks); } <ins> function countOccurrences(source: string, pattern: string): number { return source.length - (source.replaceAll(pattern, '').length / pattern.length); } </ins> export async function resolveLinkForTest(link: string, uri?: URI): Promise<ResolvedLink> { <ins> // A set of conditions to help with square brackets in testing, this is only needed because the // resolve path function must be injected if (link.startsWith('[')) { return null; } if (countOccurrences(link, '[') !== countOccurrences(link, ']')) { return null; } if (link.includes('[') !== link.includes(']')) { return null; } </ins> return { link, uri: URI.from({ scheme: Schemas.file, path: link }),

'./$foo', '../foo', '/foo/bar', <ins> '/foo/[bar]', '/foo/[bar].baz', '/foo/[bar]/baz', </ins> '/foo/bar+more', 'foo/bar', 'foo/bar+more',

'c:foo/barbaz', 'foo/bar', 'foo/bar', <ins> 'foo/[bar]', 'foo/[bar].baz', 'foo/[bar]/baz', </ins> 'foobar', <ins> 'foo[bar].baz', 'foo[bar]baz', </ins> 'foobar+more', ];