From f9c2aa2195b99410cb88b69cf214fbb6a76e5653 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sun, 27 Apr 2025 19:28:42 -0700 Subject: [PATCH 1/9] rewrite smart quotes logic --- packages/site-kit/src/lib/markdown/utils.ts | 40 +++++++++++++-------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 559d507456..3c9356fad5 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -50,23 +50,33 @@ export function smart_quotes( // wouldn't correctly handle `That '70s show` or `My country 'tis of thee` // but a) it's very unlikely they'll occur in our docs, and // b) they can be dealt with manually - return str.replace( - html ? /(.|^)('|")(.|$)/g : /(.|^)('|")(.|$)/g, - (m, before, quote, after) => { - const left = (first && before === '') || [' ', '\n', '('].includes(before); - let replacement = ''; - - if (html) { - const double = quote === '"'; - replacement = `&${left ? 'l' : 'r'}${double ? 'd' : 's'}quo;`; - } else { - const double = quote === '"'; - replacement = double ? (left ? '“' : '”') : left ? '‘' : '’'; + let open_quote = false; + let res = ''; + const len = str.length; + for (let index = 0; index < len; index++) { + let char = str.charAt(index); + if (html && char === '&') { + if (str.slice(index, index + 5) === ''') { + let left: boolean = first && !open_quote; + open_quote = left; + res += `&${left ? 'l' : 'r'}squo;`; + index += 4; + } else if (str.slice(index, index + 6) === '"') { + let left: boolean = first && !open_quote; + open_quote = left; + res += `&${left ? 'l' : 'r'}dquo`; + index += 5; } - - return (before ?? '') + replacement + (after ?? ''); + } else if (!html && (char === '"' || char === "'")) { + let left: boolean = first && !open_quote; + open_quote = left; + let double = char === '"'; + res += double ? (left ? '“' : '”') : left ? '‘' : '’'; + } else { + res += char; } - ); + } + return res; } const tokenizer: TokenizerObject = { From c62147a3d717e78c28c9e270d93a3c254a029a5c Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:05:34 -0700 Subject: [PATCH 2/9] fix --- packages/site-kit/src/lib/markdown/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 3c9356fad5..33632e3bc3 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -66,6 +66,8 @@ export function smart_quotes( open_quote = left; res += `&${left ? 'l' : 'r'}dquo`; index += 5; + } else { + res += '&'; } } else if (!html && (char === '"' || char === "'")) { let left: boolean = first && !open_quote; From c735af75bec91bf65a011a9c9e5b6cdf35b51844 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:23:20 -0700 Subject: [PATCH 3/9] apply suggestion from review --- packages/site-kit/src/lib/markdown/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 33632e3bc3..41fc0dc42e 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -57,12 +57,12 @@ export function smart_quotes( let char = str.charAt(index); if (html && char === '&') { if (str.slice(index, index + 5) === ''') { - let left: boolean = first && !open_quote; + let left: boolean = (first && !open_quote) || (index > 1 && str.charAt(index - 1) === '='); open_quote = left; res += `&${left ? 'l' : 'r'}squo;`; index += 4; } else if (str.slice(index, index + 6) === '"') { - let left: boolean = first && !open_quote; + let left: boolean = (first && !open_quote) || (index > 1 && str.charAt(index - 1) === '='); open_quote = left; res += `&${left ? 'l' : 'r'}dquo`; index += 5; @@ -70,7 +70,7 @@ export function smart_quotes( res += '&'; } } else if (!html && (char === '"' || char === "'")) { - let left: boolean = first && !open_quote; + let left: boolean = (first && !open_quote) || (index > 1 && str.charAt(index - 1) === '='); open_quote = left; let double = char === '"'; res += double ? (left ? '“' : '”') : left ? '‘' : '’'; From d3536fe094aa9ccd77be40d0d1e6a8013144c79e Mon Sep 17 00:00:00 2001 From: Chew Tee Ming Date: Mon, 4 Aug 2025 11:42:36 +0800 Subject: [PATCH 4/9] document smart quotes function --- packages/site-kit/src/lib/markdown/utils.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 41fc0dc42e..6df21e78a1 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -42,14 +42,24 @@ export const slugify = (str: string) => { .replace(/-$/, ''); }; +/** + * Replace dumb quotes with smart quotes. This isn't a perfect algorithm — it + * wouldn't correctly handle `That '70s show` or `My country 'tis of thee` but + * a) it's very unlikely they'll occur in our docs, and + * b) they can be dealt with manually + */ export function smart_quotes( str: string, - { first = true, html = false }: { first?: boolean; html?: boolean } = {} + { + first = true, + html = false + }: { + /** True if the string is the entire sentence or false if it's a substring. @default true */ + first?: boolean; + /** True if the string has HTML entities. @default false */ + html?: boolean; + } = {} ) { - // replace dumb quotes with smart quotes. This isn't a perfect algorithm — it - // wouldn't correctly handle `That '70s show` or `My country 'tis of thee` - // but a) it's very unlikely they'll occur in our docs, and - // b) they can be dealt with manually let open_quote = false; let res = ''; const len = str.length; From 7e81f5b57071b0975979a4f160a4db799f650422 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:20:50 -0700 Subject: [PATCH 5/9] try this --- packages/site-kit/src/lib/markdown/utils.ts | 31 +++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 6df21e78a1..84b1fa9283 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -60,30 +60,45 @@ export function smart_quotes( html?: boolean; } = {} ) { - let open_quote = false; + const stack: Array<"'" | '"'> = []; let res = ''; const len = str.length; for (let index = 0; index < len; index++) { - let char = str.charAt(index); + const char = str.charAt(index); if (html && char === '&') { if (str.slice(index, index + 5) === ''') { - let left: boolean = (first && !open_quote) || (index > 1 && str.charAt(index - 1) === '='); - open_quote = left; + const left: boolean = + (first && stack.at(-1) !== "'") || (index > 1 && str.charAt(index - 1) === '='); res += `&${left ? 'l' : 'r'}squo;`; index += 4; + if (!left) { + stack.pop(); + } else { + stack.push("'"); + } } else if (str.slice(index, index + 6) === '"') { - let left: boolean = (first && !open_quote) || (index > 1 && str.charAt(index - 1) === '='); - open_quote = left; + const left: boolean = + (first && stack.at(-1) !== '"') || (index > 1 && str.charAt(index - 1) === '='); res += `&${left ? 'l' : 'r'}dquo`; index += 5; + if (!left) { + stack.pop(); + } else { + stack.push('"'); + } } else { res += '&'; } } else if (!html && (char === '"' || char === "'")) { - let left: boolean = (first && !open_quote) || (index > 1 && str.charAt(index - 1) === '='); - open_quote = left; + let left: boolean = + (first && stack.at(-1) !== char) || (index > 1 && str.charAt(index - 1) === '='); let double = char === '"'; res += double ? (left ? '“' : '”') : left ? '‘' : '’'; + if (!left) { + stack.pop(); + } else { + stack.push(char); + } } else { res += char; } From 1e234d35d078215f56673abc6f618e8a201d2627 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:51:45 -0700 Subject: [PATCH 6/9] more --- packages/site-kit/src/lib/markdown/utils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 84b1fa9283..00c684ede8 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -63,12 +63,15 @@ export function smart_quotes( const stack: Array<"'" | '"'> = []; let res = ''; const len = str.length; + const opening_squo_chars = /[\s\n\(]/; for (let index = 0; index < len; index++) { const char = str.charAt(index); + const before = str.charAt(index - 1); if (html && char === '&') { if (str.slice(index, index + 5) === ''') { const left: boolean = - (first && stack.at(-1) !== "'") || (index > 1 && str.charAt(index - 1) === '='); + ((first && stack.at(-1) !== "'") || (index > 1 && before === '=')) && + !opening_squo_chars.test(before); res += `&${left ? 'l' : 'r'}squo;`; index += 4; if (!left) { @@ -91,7 +94,8 @@ export function smart_quotes( } } else if (!html && (char === '"' || char === "'")) { let left: boolean = - (first && stack.at(-1) !== char) || (index > 1 && str.charAt(index - 1) === '='); + (first && stack.at(-1) !== char) || + (index > 1 && before === '=' && !(char === "'" && opening_squo_chars.test(before))); let double = char === '"'; res += double ? (left ? '“' : '”') : left ? '‘' : '’'; if (!left) { From 4f9b2b08bd284be7e83e1dd39ddb63b94bc6d855 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sun, 7 Sep 2025 22:09:46 -0700 Subject: [PATCH 7/9] fix --- packages/site-kit/src/lib/markdown/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 00c684ede8..bbd89fcfb2 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -94,8 +94,8 @@ export function smart_quotes( } } else if (!html && (char === '"' || char === "'")) { let left: boolean = - (first && stack.at(-1) !== char) || - (index > 1 && before === '=' && !(char === "'" && opening_squo_chars.test(before))); + ((first && stack.at(-1) !== char) || (index > 1 && before === '=')) && + !(char === "'" && !opening_squo_chars.test(before)); let double = char === '"'; res += double ? (left ? '“' : '”') : left ? '‘' : '’'; if (!left) { From b02cea6c31e1d984fb73b0f722e86761df48148c Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Sun, 7 Sep 2025 22:21:48 -0700 Subject: [PATCH 8/9] try this --- packages/site-kit/src/lib/markdown/utils.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index bbd89fcfb2..3b4c63cf7d 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -70,7 +70,7 @@ export function smart_quotes( if (html && char === '&') { if (str.slice(index, index + 5) === ''') { const left: boolean = - ((first && stack.at(-1) !== "'") || (index > 1 && before === '=')) && + ((first && before === '') || stack.at(-1) !== "'" || (index > 1 && before === '=')) && !opening_squo_chars.test(before); res += `&${left ? 'l' : 'r'}squo;`; index += 4; @@ -81,7 +81,9 @@ export function smart_quotes( } } else if (str.slice(index, index + 6) === '"') { const left: boolean = - (first && stack.at(-1) !== '"') || (index > 1 && str.charAt(index - 1) === '='); + (first && before === '') || + stack.at(-1) !== '"' || + (index > 1 && str.charAt(index - 1) === '='); res += `&${left ? 'l' : 'r'}dquo`; index += 5; if (!left) { @@ -94,7 +96,7 @@ export function smart_quotes( } } else if (!html && (char === '"' || char === "'")) { let left: boolean = - ((first && stack.at(-1) !== char) || (index > 1 && before === '=')) && + ((first && before === '') || stack.at(-1) !== char || (index > 1 && before === '=')) && !(char === "'" && !opening_squo_chars.test(before)); let double = char === '"'; res += double ? (left ? '“' : '”') : left ? '‘' : '’'; From 84004e250effb13ec1da74e9eeb782bd28330343 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Mon, 8 Sep 2025 19:27:06 -0700 Subject: [PATCH 9/9] simplify --- packages/site-kit/src/lib/markdown/utils.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/site-kit/src/lib/markdown/utils.ts b/packages/site-kit/src/lib/markdown/utils.ts index 3b4c63cf7d..e73e748639 100644 --- a/packages/site-kit/src/lib/markdown/utils.ts +++ b/packages/site-kit/src/lib/markdown/utils.ts @@ -70,8 +70,7 @@ export function smart_quotes( if (html && char === '&') { if (str.slice(index, index + 5) === ''') { const left: boolean = - ((first && before === '') || stack.at(-1) !== "'" || (index > 1 && before === '=')) && - !opening_squo_chars.test(before); + stack.at(-1) !== "'" && (opening_squo_chars.test(before) || (first && before === '')); res += `&${left ? 'l' : 'r'}squo;`; index += 4; if (!left) { @@ -80,10 +79,7 @@ export function smart_quotes( stack.push("'"); } } else if (str.slice(index, index + 6) === '"') { - const left: boolean = - (first && before === '') || - stack.at(-1) !== '"' || - (index > 1 && str.charAt(index - 1) === '='); + const left: boolean = stack.at(-1) !== '"'; res += `&${left ? 'l' : 'r'}dquo`; index += 5; if (!left) { @@ -95,10 +91,10 @@ export function smart_quotes( res += '&'; } } else if (!html && (char === '"' || char === "'")) { - let left: boolean = - ((first && before === '') || stack.at(-1) !== char || (index > 1 && before === '=')) && - !(char === "'" && !opening_squo_chars.test(before)); - let double = char === '"'; + const left: boolean = + stack.at(-1) !== char && + (opening_squo_chars.test(char) || (first && before === '') || char === '"'); + const double = char === '"'; res += double ? (left ? '“' : '”') : left ? '‘' : '’'; if (!left) { stack.pop();