Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0094b361ab | |||
| dbd80b98c3 | |||
| 8572d76ee9 | |||
| 62ece3f9ac | |||
| 8f33f67f72 | |||
| 3a18627389 |
@@ -91,15 +91,16 @@ function noteLeaves(name: string): AdfNode[] {
|
||||
}
|
||||
|
||||
function noteName(value: unknown): string | undefined {
|
||||
return typeof value === 'string' ? nonEmpty(oneLine(value).trim()) : undefined
|
||||
return nonEmpty(value) === undefined ? undefined : oneLine(String(value)).trim()
|
||||
}
|
||||
|
||||
export function oneLine(text: string): string {
|
||||
return text.replace(/[\r\u0000]/g, '').replace(/\n/g, ' ')
|
||||
}
|
||||
|
||||
// Blank once a text leaf's cleaning has run is empty.
|
||||
function nonEmpty(value: unknown): string | undefined {
|
||||
return typeof value === 'string' && value !== '' ? value : undefined
|
||||
return typeof value === 'string' && oneLine(value).trim() !== '' ? value : undefined
|
||||
}
|
||||
|
||||
function isoDate(timestamp: unknown): string | undefined {
|
||||
|
||||
@@ -172,7 +172,7 @@ test('keeps the CommonMark blocks in their spelling and drops their attributes a
|
||||
assert.equal(plain(bulletList(item(said('a'), bulletList(item(said('x'))), long))), '- a\n - x\n - 10000000000. y\n')
|
||||
const givesWay = node('orderedList', { order: 1e10 }, item(node('rule', {})), item(bulletList(item(bulletList(item())))))
|
||||
assert.equal(plain(givesWay, bulletList(item(said('z')))), '- 10000000000.\n- 10000000001.\n - -\n- z\n')
|
||||
assert.equal(plain(node('orderedList', { order: 999999999 }, item(said('a'))), node('orderedList', { order: 5 }, item(said('b')))), '- 999999999\\. a\n- 1000000000. b\n')
|
||||
assert.equal(plain(node('orderedList', { order: 999999999 }, item(said('a'))), node('orderedList', { order: 5 }, item(said('b')))), '- 999999999\\. a\n- 5\\. b\n')
|
||||
assert.equal(plain(node('heading', { level: 7 }, text('h'))), 'h\n')
|
||||
})
|
||||
|
||||
@@ -216,6 +216,8 @@ test('spells an extension as its text attribute, else a note naming it, and a pl
|
||||
assert.equal(plain(node('syncBlock', { resourceId: 'r' })), '_(synced block not included)_\n')
|
||||
assert.equal(plain(node('extension', { extensionKey: 'jira\r\nissues\u0000' })), '_(jira issues not included)_\n')
|
||||
assert.equal(plain(node('extension', { extensionKey: '\r\u0000' }), node('extension', { extensionKey: '\n' })), '_(extension not included)_\n\n_(extension not included)_\n')
|
||||
const blank = paragraph(node('inlineExtension', { extensionKey: 'k', text: '\r' }), text(' '), node('mediaInline', { alt: '\u0000', type: 'file' }), text(' '), node('mention', { id: '5b10a2', text: '\r' }))
|
||||
assert.equal(plain(blank), '_(k not included)_ _(image not included)_ @5b10a2\n')
|
||||
assert.equal(plain(paragraph(text('a '), node('inlineExtension', { text: 'macro' }), text(' '), node('inlineExtension', {}), node('placeholder', { text: 'Type here' }))), 'a macro _(extension not included)_\n')
|
||||
})
|
||||
|
||||
@@ -299,7 +301,9 @@ test('drops an empty paragraph and merges adjacent lists of one type', () => {
|
||||
const ordered = (order: number, value: string): AdfNode => node('orderedList', { order }, item(said(value)))
|
||||
assert.equal(plain(said('a'), paragraph(), paragraph(text(' ')), said('b')), 'a\n\nb\n')
|
||||
assert.equal(plain(bulletList(item(said('a'))), paragraph(), node('decisionList', {}, node('decisionItem', {}, text('b')))), '- a\n- b\n')
|
||||
assert.equal(plain(ordered(2, 'a'), ordered(7, 'b'), bulletList(item(said('c')))), '2. a\n3. b\n\n- c\n')
|
||||
assert.equal(plain(ordered(2, 'a'), ordered(3, 'b'), bulletList(item(said('c')))), '2. a\n3. b\n\n- c\n')
|
||||
assert.equal(plain(bulletList(item(said('x'))), ordered(1, 'a'), ordered(5, 'b'), ordered(6, 'c')), '- x\n- 1\\. a\n- 5\\. b\n\n6. c\n')
|
||||
assert.equal(plain(ordered(1, 'a'), ordered(1e10, 'y')), '- 1\\. a\n- 10000000000. y\n')
|
||||
const column = (list: AdfNode): AdfNode => node('layoutColumn', {}, list)
|
||||
assert.equal(plain(node('layoutSection', {}, column(bulletList(item(said('a')))), column(bulletList(item(said('b')))))), '- a\n- b\n')
|
||||
})
|
||||
|
||||
@@ -131,24 +131,39 @@ function spliced(sequence: readonly AdfNode[], index: number, replacement: reado
|
||||
// A numbered list whose markers run past CommonMark's keeps its numbers as text in a bullet list.
|
||||
function numberedPastMarkers(list: AdfNode): AdfNode {
|
||||
const order = nodeAttrs(list)['order']
|
||||
const items = nodeContent(list)
|
||||
if (list.type !== 'orderedList' || typeof order !== 'number' || order + items.length - 1 <= largestListMarker) return list
|
||||
return { content: items.map((item, offset) => itemOf(marked(nodeContent(item), `${order + offset}.`))), type: 'bulletList' }
|
||||
if (list.type !== 'orderedList' || typeof order !== 'number' || order + nodeContent(list).length - 1 <= largestListMarker) return list
|
||||
return numberedAsText(list)
|
||||
}
|
||||
|
||||
function numberedAsText(list: AdfNode): AdfNode {
|
||||
const order = Number(nodeAttrs(list)['order'])
|
||||
return { content: nodeContent(list).map((item, offset) => itemOf(marked(nodeContent(item), `${order + offset}.`))), type: 'bulletList' }
|
||||
}
|
||||
|
||||
function mergedLists(blocks: readonly AdfNode[]): AdfNode[] {
|
||||
const merged: AdfNode[] = []
|
||||
for (const block of blocks) {
|
||||
const previous = merged[merged.length - 1]
|
||||
if (previous !== undefined && previous.type === block.type && (block.type === 'bulletList' || block.type === 'orderedList')) {
|
||||
merged[merged.length - 1] = { ...previous, content: [...nodeContent(previous), ...nodeContent(block)] }
|
||||
} else {
|
||||
merged.push(block)
|
||||
let next = block
|
||||
for (let previous = merged.at(-1); previous !== undefined && previous.type === next.type && isList(next); previous = merged.at(-1)) {
|
||||
merged.pop()
|
||||
next = joinedLists(previous, next)
|
||||
}
|
||||
merged.push(next)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
function isList(block: AdfNode): boolean {
|
||||
return block.type === 'bulletList' || block.type === 'orderedList'
|
||||
}
|
||||
|
||||
// Two numbered lists whose numbering breaks between them keep their numbers as text in one bullet list.
|
||||
function joinedLists(first: AdfNode, second: AdfNode): AdfNode {
|
||||
const breaks = first.type === 'orderedList' && nodeAttrs(second)['order'] !== Number(nodeAttrs(first)['order']) + nodeContent(first).length
|
||||
const [head, tail] = breaks ? [numberedAsText(first), numberedAsText(second)] : [first, second]
|
||||
return { ...head, content: [...nodeContent(head), ...nodeContent(tail)] }
|
||||
}
|
||||
|
||||
function paragraph(content: readonly AdfNode[]): AdfNode {
|
||||
return { content: [...content], type: 'paragraph' }
|
||||
}
|
||||
|
||||
@@ -236,8 +236,9 @@ chunk clearing a §11 seam.
|
||||
rows it covered, padding at most to the table's cell count.
|
||||
- A list stays a list: where CommonMark cannot hold a block inside an item, what gives way is
|
||||
what a reader does not see — the spaces of a whitespace-only code line — and a rule opening an
|
||||
item drops (a panel, 3 of 3, 2026-09-25); an ordered list running past `999999999` is a bullet
|
||||
list keeping its numbers as text (a panel, 3 of 3, 2026-09-25).
|
||||
item drops (a panel, 3 of 3, 2026-09-25); an ordered list running past `999999999`, or adjacent
|
||||
ordered lists whose numbering does not continue, is one bullet list keeping its numbers as text
|
||||
(panels, 3 of 3 and 5 of 7, 2026-09-25).
|
||||
- `code`, `em`, `link`, `strike` and `strong` stay and every other mark drops, keeping its text —
|
||||
`subsup` too, since `~2~` is a strike on GitHub; a link no CommonMark escape writes has its
|
||||
`href` percent-encoded until one does, and a mark run CommonMark's flanking or matching cannot
|
||||
|
||||
Reference in New Issue
Block a user