From a502cfd0d06c299876378350cb68bce99d7f6488 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 16 Oct 2026 09:08:29 +0200 Subject: [PATCH] fix(markdown): bare prose paths need an extension, line suffix or depth Slash-separated English pairs were recognized as file links in prose. Require stronger path intent there while preserving the existing inline-code recognition rule. --- .../src/components/markdown/renderer.test.ts | 26 ++++++++++++++++--- .../app/src/components/markdown/source.ts | 25 +++++++++++++----- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/packages/app/src/components/markdown/renderer.test.ts b/packages/app/src/components/markdown/renderer.test.ts index 10ca97beec..218cdc8884 100644 --- a/packages/app/src/components/markdown/renderer.test.ts +++ b/packages/app/src/components/markdown/renderer.test.ts @@ -444,14 +444,20 @@ it.each([ it.each([ "src/index.ts", "README.md", - "packages/app", "./scripts/run", + "./x/y", + "../x/y", + "/x/y", + "a/b/c", + String.raw`C:\Users\x`, + "src/index.ts:12:3", + "src/run:12", + "src/run:12:3", + "src/run:12-20", "~/dev/paseo", String.raw`C:\Users\x\file.txt`, "file.ts:12", "file.ts:12-20", - "123/file", - "src/123", "123.ts", "FILE.TS", "https://example.com/path", @@ -463,6 +469,20 @@ it.each([ expect(linkRecognition.inlineCode.test(token)).toBe(true); }); +it.each([ + "before/after", + "and/or", + "input/output", + "read/write", + "packages/app", + "123/file", + "src/123", +])("requires stronger path intent in prose while preserving inline code: %s", (token) => { + const { linkRecognition } = prepareMarkdownSource(`Compare ${token}, then continue.`); + expect([...`Compare ${token}, then continue.`.matchAll(linkRecognition.text)]).toEqual([]); + expect(linkRecognition.inlineCode.test(token)).toBe(true); +}); + it("keeps prose and inline-code recognition enabled through the native regex adapter", () => { const { linkRecognition } = prepareMarkdownSource("3/4 src/index.ts"); const native = toNativeLinkRecognition(linkRecognition); diff --git a/packages/app/src/components/markdown/source.ts b/packages/app/src/components/markdown/source.ts index 320864f064..fc6f7463a5 100644 --- a/packages/app/src/components/markdown/source.ts +++ b/packages/app/src/components/markdown/source.ts @@ -16,20 +16,31 @@ const LINE_SUFFIX = String.raw`(?::\d+(?::\d+)?(?:-\d+(?::\d+)?)?)?`; const FILE_EXTENSIONS = [...ASSISTANT_FILE_EXTENSIONS] .map((extension) => extension.replace(/[a-z]/g, (letter) => `[${letter}${letter.toUpperCase()}]`)) .join("|"); -const SEPARATED_PATH = String.raw`(?:[A-Za-z]:[\\/]|\.{0,2}\/|~\/)?(?=${PATH_CHARACTER}*(?:[\\/]${PATH_CHARACTER}*)*${PATH_LETTER})${PATH_SEGMENT}(?:[\\/]${PATH_SEGMENT})+`; +const PATH_PREFIX = String.raw`(?:[A-Za-z]:[\\/]|\.{0,2}\/|~\/)`; +const HAS_PATH_LETTER = String.raw`(?=${PATH_CHARACTER}*(?:[\\/]${PATH_CHARACTER}*)*${PATH_LETTER})`; +const SEPARATED_PATH = String.raw`${PATH_SEGMENT}(?:[\\/]${PATH_SEGMENT})+`; const FILE_NAME = String.raw`${PATH_SEGMENT}\.(?:${FILE_EXTENSIONS})`; -// The native adapter mistakes a literal + in lookbehind for a quantifier; use its hex escape. -const FILE_PATTERN = new RegExp( - String.raw`(?:https?://[^\s<>]+|(?]+|(?