diff --git a/packages/shared/src/threadLinks.test.ts b/packages/shared/src/threadLinks.test.ts index 5285214b8656d..3995ff05917df 100644 --- a/packages/shared/src/threadLinks.test.ts +++ b/packages/shared/src/threadLinks.test.ts @@ -150,6 +150,33 @@ describe("thread links", () => { "After [New](t3-thread://v2/local/t1)", ].join("\n"), ); + for (const literal of [ + "`multiline\n\ncode`", + "`multiline\r\n\r\ncode`", + "`multiline\r\rcode`", + "```md\n\n\n\n```", + "~~~md\n\n\n\n~~~", + ]) { + expect(threadLinkRefs(literal, local)).toEqual([]); + expect(relabelThreadLinks(literal, local, () => "New")).toBe(literal); + } + for (const newline of ["\n", "\r\n", "\r"]) { + for (const blank of ["", " \t"]) { + const paragraphs = [ + "An unmatched ` in one paragraph.", + blank, + "< t3-thread://v1/t1 >", + blank, + "Another ` in a later paragraph.", + ].join(newline); + const normalized = paragraphs.replace("< t3-thread://v1/t1 >", ""); + expect(trimThreadAutolinkPadding(paragraphs)).toBe(normalized); + expect(threadLinkRefs(normalized, local)).toEqual([ref("local", "t1")]); + expect(relabelThreadLinks(normalized, local, () => "New")).toBe( + paragraphs.replace("< t3-thread://v1/t1 >", "[New](t3-thread://v1/t1)"), + ); + } + } }); it("trims the whitespace a model writes inside a thread autolink's brackets, and nothing else", () => { diff --git a/packages/shared/src/threadLinks.ts b/packages/shared/src/threadLinks.ts index e3a5c5eb1ac03..ea21ca9a4a911 100644 --- a/packages/shared/src/threadLinks.ts +++ b/packages/shared/src/threadLinks.ts @@ -11,7 +11,7 @@ const HOSTED_PREFIX = `${DEFAULT_HOSTED_APP_URL}/threads/`; const HOSTED_SEGMENT = "[A-Za-z0-9_%-]+"; const HOSTED_LINK = `${HOSTED_PREFIX.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}${HOSTED_SEGMENT}/${HOSTED_SEGMENT}`; // Code comes first in an alternation so a link written inside a code span or fence is skipped. -const CODE = String.raw`(?(\`{3,}|~{3,})[\s\S]*?(?:\2|(?![\s\S])))|(?(\`+)[\s\S]*?\4)`; +const CODE = String.raw`(?(\`{3,}|~{3,})[\s\S]*?(?:\2|(?![\s\S])))|(?(\`+)(?:(?!(?:\r\n|\r(?!\n)|\n)[ \t]*(?:\r\n|\r(?!\n)|\n))[\s\S])*?\4)`; const THREAD_LINK_OUTSIDE_CODE = new RegExp( String.raw`${CODE}|\[(?