From 1a932d62aa67cef9fcf6cee89caad3779eb04723 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Thu, 22 Oct 2026 13:19:58 -0700 Subject: [PATCH] perf(markdown): scan wiki-link delimiters without revisiting the document tail (#31934) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../components/editor/markdown-doc-links.ts | 5 +++- .../raw-markdown-html.doc-link-scan.test.ts | 27 +++++++++++++++++++ .../components/editor/raw-markdown-html.ts | 6 ++++- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/renderer/src/components/editor/raw-markdown-html.doc-link-scan.test.ts diff --git a/src/renderer/src/components/editor/markdown-doc-links.ts b/src/renderer/src/components/editor/markdown-doc-links.ts index 3f5e4f7f6af..ca7f472ade1 100644 --- a/src/renderer/src/components/editor/markdown-doc-links.ts +++ b/src/renderer/src/components/editor/markdown-doc-links.ts @@ -152,12 +152,15 @@ export function resolveMarkdownDocLink( } export function parseMarkdownDocLink(rawTarget: string): ParsedMarkdownDocLink | null { + if (/[[\]]/.test(rawTarget)) { + return null + } const separatorIndex = rawTarget.indexOf('|') const target = separatorIndex === -1 ? rawTarget.trim() : rawTarget.slice(0, separatorIndex).trim() const alias = separatorIndex === -1 ? null : rawTarget.slice(separatorIndex + 1).trim() || null - if (!target || /[\r\n[\]]/.test(target) || (alias !== null && /[\r\n[\]]/.test(alias))) { + if (!target || /[\r\n]/.test(target) || (alias !== null && /[\r\n]/.test(alias))) { return null } if (separatorIndex !== -1 && alias === null) { diff --git a/src/renderer/src/components/editor/raw-markdown-html.doc-link-scan.test.ts b/src/renderer/src/components/editor/raw-markdown-html.doc-link-scan.test.ts new file mode 100644 index 00000000000..800e997cc16 --- /dev/null +++ b/src/renderer/src/components/editor/raw-markdown-html.doc-link-scan.test.ts @@ -0,0 +1,27 @@ +import { runInNewContext } from 'node:vm' +import { expect, it } from 'vitest' +import { encodeRawMarkdownHtmlForRichEditor } from './raw-markdown-html' +import { createRichMarkdownEditorCodec } from './rich-markdown-source-transport' + +it.each([false, true])('encodes dense unfinished wiki links with a closing tail: %s', (closed) => { + const codec = createRichMarkdownEditorCodec('0'.repeat(32)) + const prefix = '[[x '.repeat(400_000) + const content = closed ? `${prefix}]]\n[[valid]]` : prefix + const expected = closed + ? `${prefix.slice(0, -4)}${codec.transport.create('document-link', 'x')}\n${codec.transport.create('document-link', 'valid')}` + : prefix + const encoded = runInNewContext( + 'encode(content, codec)', + { encode: encodeRawMarkdownHtmlForRichEditor, content, codec }, + { timeout: 1500 } + ) + expect(encoded).toBe(expected) +}) + +it('advances past literal and code delimiters while retaining trimmed link targets', () => { + const codec = createRichMarkdownEditorCodec('0'.repeat(32)) + const content = ']] `[[code]]` [[bad [target]] [[\nfirst\n]] [[second|Label]] [[unfinished' + expect(encodeRawMarkdownHtmlForRichEditor(content, codec)).toBe( + `]] \`[[code]]\` [[bad [target]] ${codec.transport.create('document-link', 'first')} ${codec.transport.create('document-link', 'second|Label')} [[unfinished` + ) +}) diff --git a/src/renderer/src/components/editor/raw-markdown-html.ts b/src/renderer/src/components/editor/raw-markdown-html.ts index 1c6c334b90b..d63a37b658c 100644 --- a/src/renderer/src/components/editor/raw-markdown-html.ts +++ b/src/renderer/src/components/editor/raw-markdown-html.ts @@ -49,6 +49,7 @@ export function encodeRawMarkdownHtmlForRichEditor( const lastCommentClose = normalizedContent.lastIndexOf('-->') const { transport } = codec let index = 0 + let nextDocLinkClose = 0 let activeFence: '`' | '~' | null = null let activeFenceLength = 0 let result = '' @@ -186,7 +187,10 @@ export function encodeRawMarkdownHtmlForRichEditor( normalizedContent[index + 1] === '[' && !isEscaped(normalizedContent, index) ) { - const closingIndex = normalizedContent.indexOf(']]', index + 2) + if (nextDocLinkClose !== -1 && nextDocLinkClose < index + 2) { + nextDocLinkClose = normalizedContent.indexOf(']]', index + 2) + } + const closingIndex = nextDocLinkClose if (closingIndex !== -1) { const rawTarget = normalizedContent.slice(index + 2, closingIndex) const link = parseMarkdownDocLink(rawTarget)