Repository navigation
fix(shared): file links with parentheses in the path keep their parent folder labels #28644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -356,10 +356,33 @@ function markdownLinkTailScanner(markdown: string) { | |
| }; | ||
| } | ||
|
|
||
| // micromark's own nesting limit. It also keeps link scanning linear: a scan only runs past another | ||
| // link's opening parenthesis while that parenthesis counts toward this depth. | ||
| const MAX_BARE_DESTINATION_PAREN_DEPTH = 32; | ||
|
|
||
| function bareMarkdownLinkDestinationEnd(markdown: string, start: number): number | null { | ||
| const delimiter = /[\s()\\]/g; | ||
| delimiter.lastIndex = start; | ||
| let depth = 0; | ||
| while (true) { | ||
| const index = delimiter.exec(markdown)?.index ?? markdown.length; | ||
| const char = markdown[index]; | ||
| if (char === "\\") { | ||
| if (/[()\\]/.test(markdown[index + 1] ?? "")) delimiter.lastIndex = index + 2; | ||
| } else if (char === "(") { | ||
| if (depth === MAX_BARE_DESTINATION_PAREN_DEPTH) return null; | ||
| depth += 1; | ||
| } else if (char === ")" && depth > 0) { | ||
| depth -= 1; | ||
| } else { | ||
| return depth === 0 ? index : null; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function markdownLinkDestinationScanner(markdown: string) { | ||
| const whitespaceEnd = markdownRunEndFinder(markdown, /\S/g); | ||
| const angleEnd = markdownRunEndFinder(markdown, /[>\n]/g); | ||
| const bareEnd = markdownRunEndFinder(markdown, /[\s)]/g); | ||
| // Separate caches keep scans moving forward when angle destinations fall back to bare ones. | ||
| const angleTail = markdownLinkTailScanner(markdown); | ||
| const bareTail = markdownLinkTailScanner(markdown); | ||
|
|
@@ -373,13 +396,18 @@ function markdownLinkDestinationScanner(markdown: string) { | |
| if (end !== null) return { hrefStart: hrefStart + 1, hrefEnd, end }; | ||
| } | ||
| } | ||
| const hrefEnd = bareEnd(hrefStart); | ||
| if (hrefEnd === hrefStart) return null; | ||
| const hrefEnd = bareMarkdownLinkDestinationEnd(markdown, hrefStart); | ||
| if (hrefEnd === null || hrefEnd === hrefStart) return null; | ||
| const end = bareTail(hrefEnd); | ||
| return end === null ? null : { hrefStart, hrefEnd, end }; | ||
| }; | ||
| } | ||
|
|
||
| export function decodeMarkdownLinkEscapes(href: string): string { | ||
| if (isWindowsAbsolutePath(href) && !href.includes("&")) return href; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return href.replace(/\\([!-/:-@[-`{-~])/g, "$1"); | ||
| } | ||
|
|
||
| export function extractMarkdownLinkHrefs(markdown: string): string[] { | ||
| const hrefs: string[] = []; | ||
| const scanDestination = markdownLinkDestinationScanner(markdown); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.