Join only content-free text nodes in editor-normal ADF, type checked first: adfToPlainMarkdown on 3,000 extension blocks of 200-key parameters 1,752-1,941 ms → 1,034-1,240 ms (1,018-1,283 ms at 608ef26)

This commit is contained in:
2026-10-03 17:52:48 +02:00
parent 317262b240
commit ee9a7e7562
3 changed files with 14 additions and 5 deletions
@@ -344,3 +344,9 @@ test('writes what the editor-normal form of the document writes', () => {
assert.equal(plain({ content: [], text: 'hi', type: 'futureInline' }), 'hi\n')
assert.equal(plain(paragraph({ marks: [{ attrs: {}, type: 'code' }], text: 'a', type: 'text' }, { marks: [{ type: 'code' }], text: '|b', type: 'text' })), '`a|b`\n')
})
test('writes a text node holding content the same in either order beside its neighbour', () => {
const holding: AdfNode = { content: [text('inner')], text: 'a', type: 'text' }
assert.equal(plain(paragraph(holding, text('b'))), 'ab\n')
assert.equal(plain(paragraph(text('b'), holding)), 'ba\n')
})
+6 -4
View File
@@ -50,7 +50,7 @@ test('reads negative zero as zero, as JSON does', () => {
)
})
test('reads an empty attrs object, marks array or content array as the absent key, but on doc', () => {
test('reads an empty attrs object, marks array or content array as the absent key, except on doc', () => {
const paragraph: AdfNode = { attrs: {}, content: [{ attrs: {}, marks: [], text: 'a', type: 'text' }, { marks: [{ attrs: {}, type: 'em' }], text: 'b', type: 'text' }], marks: [], type: 'paragraph' }
assert.deepEqual(toEditorNormal({ content: [paragraph, { content: [], type: 'rule' }], type: 'doc', version: 1 }), {
content: [{ content: [{ text: 'a', type: 'text' }, { marks: [{ type: 'em' }], text: 'b', type: 'text' }], type: 'paragraph' }, { type: 'rule' }],
@@ -77,7 +77,9 @@ test('normalizes blocks and mark attributes nesting far past the levels a recurs
assert.deepEqual(merged.content?.[0]?.content?.map((text) => text.text), ['ab'])
})
test('joins a text node holding content to its neighbour, as editor-normal forms hold no content to part them', () => {
const paragraph: AdfNode = { content: [{ content: [{ text: 'lost', type: 'text' }], text: 'a', type: 'text' }, { text: 'b', type: 'text' }], type: 'paragraph' }
assert.deepEqual(toEditorNormal({ content: [paragraph], type: 'doc', version: 1 }).content, [{ content: [{ content: [{ text: 'lost', type: 'text' }], text: 'ab', type: 'text' }], type: 'paragraph' }])
test('joins a text node holding content to no neighbour, so its content is kept', () => {
const holding: AdfNode = { content: [{ text: 'kept', type: 'text' }], text: 'a', type: 'text' }
for (const content of [[holding, { text: 'b', type: 'text' }], [{ text: 'b', type: 'text' }, holding]]) {
assert.deepEqual(toEditorNormal({ content: [{ content, type: 'paragraph' }], type: 'doc', version: 1 }).content, [{ content, type: 'paragraph' }])
}
})
+2 -1
View File
@@ -7,8 +7,9 @@ type JsonContainer = JsonValue[] | { [key: string]: JsonValue }
type NodeHolder = { content?: AdfNode[] }
// The editor's rule is the reader's over the pair's editor-normal forms, which hold no content: a text node's content never parts it.
// The editor's rule is the reader's over the pair's editor-normal forms; a text node holding content never joins, so its content is kept.
export function joinsWhenEditorNormal(previous: AdfNode, node: AdfNode): boolean {
if (previous.type !== 'text' || node.type !== 'text' || nodeContent(previous).length > 0 || nodeContent(node).length > 0) return false
return joinsWhenRead(normalNode(previous), normalNode(node))
}