diff --git a/site/lib/lib.test.mjs b/site/lib/lib.test.mjs index 59e6331..c53c4e0 100644 --- a/site/lib/lib.test.mjs +++ b/site/lib/lib.test.mjs @@ -513,6 +513,8 @@ eq("bare url in bold", renderInline("**https://a.test/x**", passLink), ' eq("bare url keeps a balanced paren", renderInline("see https://a.test/wiki/Foo_(bar).", passLink), 'see https://a.test/wiki/Foo_(bar).'); eq("angle-bracket autolink", renderInline("", passLink), 'https://a.test/x'); eq("bare url stops at a code span", renderInline("https://a.test/x`y`", passLink), 'https://a.test/xy'); +eq("bare url stops at curly double quotes", renderInline("on “https://a.test/x”", passLink), 'on “https://a.test/x”'); +eq("bare url stops at curly single quotes", renderInline("see ‘https://a.test/y’", passLink), 'see ‘https://a.test/y’'); throws("malformed escape is a link error", () => resolve("bad%E0.md"), 'link target "bad%E0.md" is malformed'); eq("query on a relative link is kept", resolve("../../docs/provenance.md?plain=1").href, "https://github.com/o/r/blob/main/docs/provenance.md?plain=1"); diff --git a/site/lib/markdown.mjs b/site/lib/markdown.mjs index 99d4f06..9c856ae 100644 --- a/site/lib/markdown.mjs +++ b/site/lib/markdown.mjs @@ -105,8 +105,10 @@ export function renderInline(text, resolveLink) { s = s.replace(/<(https?:\/\/[^\s<>]+)>/g, (_, url) => keep(`${esc(url)}`)); // Bare URLs, leaving trailing sentence punctuation outside the link. A URL // stops at `*` (bold around it) and at a slot (a code span right after it), - // and keeps a closing paren that balances one inside it (Foo_(bar)). - s = s.replace(/https?:\/\/[^\s<>"*\u0000]+/g, (url) => { + // and keeps a closing paren that balances one inside it (Foo_(bar)). Curly + // quotes end it too, as they do in the link check: quoted licence text wraps + // a URL in them (#274, #285). + s = s.replace(/https?:\/\/[^\s<>"*‘’“”\u0000]+/g, (url) => { let [, core, tail] = url.match(/^(.*?)([.,;:!?)\]]*)$/); while (tail.startsWith(")") && count(core, "(") > count(core, ")")) { core += ")";