Read the container blocks, and part a nested list the tight spelling would swallow #32
@@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"text": "SELECT id\nFROM part\nWHERE qty > 0;\n-- 1. the marker a container start would claim",
|
"text": "SELECT id\nFROM part\nWHERE qty > 0;\n- 1. the markers a container start would claim",
|
||||||
"type": "text"
|
"type": "text"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
SELECT id
|
SELECT id
|
||||||
FROM part
|
FROM part
|
||||||
WHERE qty > 0;
|
WHERE qty > 0;
|
||||||
-- 1. the marker a container start would claim
|
- 1. the markers a container start would claim
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -130,9 +130,12 @@ test('refuses a line whose start block parsing would claim', () => {
|
|||||||
assert.equal(code(adfToMarkdown(document(paragraph({ marks: [{ type: 'code' }], text: '```', type: 'text' })))), 'unspellable-line-start')
|
assert.equal(code(adfToMarkdown(document(paragraph({ marks: [{ type: 'code' }], text: '```', type: 'text' })))), 'unspellable-line-start')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('refuses two adjacent lists of the same kind', () => {
|
test('refuses two adjacent lists of the same kind, the marker spelling being what merges', () => {
|
||||||
const list: AdfNode = { content: [{ content: [paragraph({ text: 'x', type: 'text' })], type: 'listItem' }], type: 'bulletList' }
|
const list: AdfNode = { content: [{ content: [paragraph({ text: 'x', type: 'text' })], type: 'listItem' }], type: 'bulletList' }
|
||||||
assert.equal(code(adfToMarkdown(document(list, list))), 'unspellable-adjacent-lists')
|
assert.equal(code(adfToMarkdown(document(list, list))), 'unspellable-adjacent-lists')
|
||||||
|
const carried: AdfNode = { ...list, attrs: { unknown: 'x' } }
|
||||||
|
assert.ok(markdown(adfToMarkdown(document(carried, carried))).startsWith('```adf\n'))
|
||||||
|
assert.ok(markdown(adfToMarkdown(document(carried, list))).endsWith('```\n\n- x\n'))
|
||||||
})
|
})
|
||||||
|
|
||||||
test('carries a node type no section spells', () => {
|
test('carries a node type no section spells', () => {
|
||||||
|
|||||||
@@ -14,13 +14,12 @@ import { tryImage } from './image.ts'
|
|||||||
import { tryPipeTable } from './pipe-table.ts'
|
import { tryPipeTable } from './pipe-table.ts'
|
||||||
|
|
||||||
type BlockContainer = 'directive' | 'document' | 'list-item'
|
type BlockContainer = 'directive' | 'document' | 'list-item'
|
||||||
type BlockSpelling = 'commonmark' | 'directive'
|
type BlockSpelling = 'commonmark' | 'directive' | 'list'
|
||||||
type EmittedBody = { fenceColons: number; text: string }
|
type EmittedBody = { fenceColons: number; text: string }
|
||||||
type EmittedBlock = EmittedBody & { spelling: BlockSpelling }
|
type EmittedBlock = EmittedBody & { spelling: BlockSpelling }
|
||||||
type PlacedBlock = EmittedBlock & { node: AdfNode; path: ConvertErrorPath }
|
type PlacedBlock = EmittedBlock & { node: AdfNode; path: ConvertErrorPath }
|
||||||
|
|
||||||
const largestListMarker = 999999999
|
const largestListMarker = 999999999
|
||||||
const listTypes = ['bulletList', 'orderedList']
|
|
||||||
|
|
||||||
export function adfToMarkdown(document: AdfDocument): Result<string> {
|
export function adfToMarkdown(document: AdfDocument): Result<string> {
|
||||||
if (!isAdfDocument(document)) return failure('not-an-adf-document', 'the value is not an ADF document', [])
|
if (!isAdfDocument(document)) return failure('not-an-adf-document', 'the value is not an ADF document', [])
|
||||||
@@ -55,9 +54,9 @@ function emitBlocks(nodes: readonly AdfNode[], container: BlockContainer, path:
|
|||||||
}
|
}
|
||||||
|
|
||||||
function separationBetween(previous: PlacedBlock, next: PlacedBlock, container: BlockContainer): Result<string> {
|
function separationBetween(previous: PlacedBlock, next: PlacedBlock, container: BlockContainer): Result<string> {
|
||||||
const plainPair = previous.spelling === 'commonmark' && next.spelling === 'commonmark'
|
const plainPair = previous.spelling !== 'directive' && next.spelling !== 'directive'
|
||||||
if (plainPair && listTypes.includes(next.node.type)) {
|
if (next.spelling === 'list') {
|
||||||
if (previous.node.type === next.node.type) {
|
if (previous.spelling === 'list' && previous.node.type === next.node.type) {
|
||||||
return failure('unspellable-adjacent-lists', `two adjacent ${next.node.type} nodes read back as one list`, next.path)
|
return failure('unspellable-adjacent-lists', `two adjacent ${next.node.type} nodes read back as one list`, next.path)
|
||||||
}
|
}
|
||||||
if (container === 'list-item') return success(interruptsParagraph(next.node) ? '\n' : '\n\n')
|
if (container === 'list-item') return success(interruptsParagraph(next.node) ? '\n' : '\n\n')
|
||||||
@@ -221,7 +220,7 @@ function emitList(node: AdfNode, path: ConvertErrorPath, depth: number): Result<
|
|||||||
fenceColons = Math.max(fenceColons, emitted.value.fenceColons)
|
fenceColons = Math.max(fenceColons, emitted.value.fenceColons)
|
||||||
lines.push(emitted.value.text)
|
lines.push(emitted.value.text)
|
||||||
}
|
}
|
||||||
return success({ fenceColons, spelling: 'commonmark', text: lines.join('\n') })
|
return success({ fenceColons, spelling: 'list', text: lines.join('\n') })
|
||||||
}
|
}
|
||||||
|
|
||||||
function listStart(node: AdfNode, items: number): number | undefined {
|
function listStart(node: AdfNode, items: number): number | undefined {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function parseBlocks(markdown: string): ParsedBlocks {
|
|||||||
|
|
||||||
function readLine(walk: Walk, line: Line): void {
|
function readLine(walk: Walk, line: Line): void {
|
||||||
const matched = matchContainers(walk, line)
|
const matched = matchContainers(walk, line)
|
||||||
// A leaf that swallows whole lines takes the marker too: no container opens inside a code or HTML block.
|
// CommonMark: no container opens inside an open code or HTML block.
|
||||||
if (matched.depth === walk.stack.length && swallowsLines(walk.leaf)) {
|
if (matched.depth === walk.stack.length && swallowsLines(walk.leaf)) {
|
||||||
readBlockLine(walk, matched.rest)
|
readBlockLine(walk, matched.rest)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ test('measures a tab from the column the containers cut it to', () => {
|
|||||||
test('drops the tightness ADF does not record', () => {
|
test('drops the tightness ADF does not record', () => {
|
||||||
assert.deepEqual(content(markdownToAdf('- a\n\n- b\n')), [bulletList(item(paragraph('a')), item(paragraph('b')))])
|
assert.deepEqual(content(markdownToAdf('- a\n\n- b\n')), [bulletList(item(paragraph('a')), item(paragraph('b')))])
|
||||||
assert.deepEqual(content(markdownToAdf('- a\n\n 2. b\n')), [bulletList(item(paragraph('a'), orderedList(2, item(paragraph('b')))))])
|
assert.deepEqual(content(markdownToAdf('- a\n\n 2. b\n')), [bulletList(item(paragraph('a'), orderedList(2, item(paragraph('b')))))])
|
||||||
|
assert.deepEqual(content(markdownToAdf('- a\n\n -\n')), [bulletList(item(paragraph('a'), bulletList(item())))])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('opens a list beside a paragraph only where the marker interrupts it', () => {
|
test('opens a list beside a paragraph only where the marker interrupts it', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user