mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-14 12:30:26 +01:00
Answer architecture round 1: the node table says a hard break takes no marks, and a link text of hard breaks alone reads as its destination
This commit is contained in:
@@ -260,10 +260,10 @@ function directivePiece(scan: Scan, span: DirectiveSpan, index: number): Result<
|
||||
}
|
||||
|
||||
function directiveMarkPiece(scan: Scan, name: string, mark: AdfMark, slot: SlotContent | undefined, index: number): Result<Piece> {
|
||||
if (slot === undefined || slot.nodes.length === 0) {
|
||||
return failure('unsupported-node-shape', `the ${name} mark wraps the [content] it marks: this one wraps none`, scan.path)
|
||||
if (slot?.nodes.some(holdsOwnMarks) === true) return failure('unsupported-node-shape', ownMarksInMark, scan.path)
|
||||
if (slot === undefined || slot.nodes.every(takesNoMarks)) {
|
||||
return failure('unsupported-node-shape', `the ${name} mark wraps the [content] it marks: this one wraps nothing a mark rides`, scan.path)
|
||||
}
|
||||
if (slot.nodes.some(holdsOwnMarks)) return failure('unsupported-node-shape', ownMarksInMark, scan.path)
|
||||
const refused = mark.type === 'link' ? refuseLinkDirective(scan, mark, slot.nodes, index) : undefined
|
||||
if (refused !== undefined) return refused
|
||||
return success({ kind: 'nodes', nodes: applyMark(slot.nodes, mark) })
|
||||
@@ -322,9 +322,9 @@ function linkMark({ destination, title }: LinkDefinition): AdfMark {
|
||||
return { attrs: title === undefined ? { href: destination } : { href: destination, title }, type: 'link' }
|
||||
}
|
||||
|
||||
// ADF holds no empty text node for a link mark to ride, so an empty link text reads as its destination, and an empty destination as nothing.
|
||||
// ADF holds no empty text node for a link mark to ride, so a link text holding nothing a mark rides reads as its destination, and an empty destination as nothing.
|
||||
function linkTo(nodes: readonly Inline[], definition: LinkDefinition): Inline[] {
|
||||
if (nodes.length > 0) return applyMark(nodes, linkMark(definition))
|
||||
if (!nodes.every(takesNoMarks)) return applyMark(nodes, linkMark(definition))
|
||||
return definition.destination === '' ? [] : [{ marks: [linkMark(definition)], text: definition.destination, type: 'text' }]
|
||||
}
|
||||
|
||||
@@ -345,6 +345,10 @@ function partText(items: readonly Inline[], scan: Scan): Result<AdfNode[]> {
|
||||
return success(parted)
|
||||
}
|
||||
|
||||
function takesNoMarks(item: Inline): boolean {
|
||||
return isNode(item) && inlineNodeModel(item.type)?.marks === false
|
||||
}
|
||||
|
||||
function isNode(item: Inline): item is AdfNode {
|
||||
return !('kind' in item)
|
||||
}
|
||||
@@ -669,10 +673,10 @@ function markType(character: string, used: number): string {
|
||||
return used === 2 ? 'strong' : 'em'
|
||||
}
|
||||
|
||||
// `*(*a*)*` builds one `em`: a node holds at most one mark of each type. Atlassian's schema gives a hard break no marks.
|
||||
// `*(*a*)*` builds one `em`: a node holds at most one mark of each type.
|
||||
function applyMark(nodes: readonly Inline[], mark: AdfMark): Inline[] {
|
||||
return nodes.map((node) => {
|
||||
if (!isNode(node) || node.type === 'hardBreak') return node
|
||||
if (!isNode(node) || takesNoMarks(node)) return node
|
||||
const marks = nodeMarks(node)
|
||||
return marks.some((carried) => carried.type === mark.type) ? node : { ...node, marks: [mark, ...marks] }
|
||||
})
|
||||
|
||||
@@ -837,7 +837,11 @@ test('builds the hard break a mark spelling spans with no marks, in every reader
|
||||
}
|
||||
assert.deepEqual(content(losslessMarkdownToAdf('~~a\\\nb~~\n')), broken(strike))
|
||||
assert.deepEqual(content(losslessMarkdownToAdf('!adf:underline[a!adf:hardBreak{}b]\n')), broken(underline))
|
||||
assert.deepEqual(content(losslessMarkdownToAdf('_!adf:hardBreak{localId=x}_\n')), [{ content: [{ attrs: { localId: 'x' }, type: 'hardBreak' }], type: 'paragraph' }])
|
||||
const localId = '01a0431d-201e-7400-a175-9c04d3628f95'
|
||||
assert.deepEqual(content(losslessMarkdownToAdf(`_!adf:hardBreak{localId=${localId}}_\n`)), [{ content: [{ attrs: { localId }, type: 'hardBreak' }], type: 'paragraph' }])
|
||||
for (const read of [commonMarkToAdf, losslessMarkdownToAdf, portableMarkdownToAdf]) {
|
||||
assert.deepEqual(content(read('[\\\n](/u)\n')), [{ content: [marked('/u', link('/u'))], type: 'paragraph' }])
|
||||
}
|
||||
})
|
||||
|
||||
test('decodes the fenced info string the block walk leaves raw', () => {
|
||||
@@ -1177,9 +1181,11 @@ test('names the link a directive link wraps, no link holding another', () => {
|
||||
})
|
||||
|
||||
test('names the directive mark left without the content it wraps', () => {
|
||||
const named = 'unsupported-node-shape: the underline mark wraps the [content] it marks: this one wraps none'
|
||||
const named = 'unsupported-node-shape: the underline mark wraps the [content] it marks: this one wraps nothing a mark rides'
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:underline[]\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:underline{}\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:underline[!adf:hardBreak{}]\n')), named)
|
||||
assert.equal(content(losslessMarkdownToAdf('!adf:link[!adf:hardBreak{}]{href="/u" title=t}\n')), named.replace('underline', 'link'))
|
||||
})
|
||||
|
||||
test('reads the text break only between two text nodes CommonMark joins, building no node', () => {
|
||||
|
||||
Reference in New Issue
Block a user