4.2: the ADF property #75
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": " \na",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"order": 1
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "\t",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "orderedList"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
::::bulletList
|
||||
:::listItem
|
||||
```
|
||||
|
||||
a
|
||||
```
|
||||
:::
|
||||
::::
|
||||
|
||||
::::::orderedList {order=1}
|
||||
:::::listItem
|
||||
::::bulletList
|
||||
:::listItem
|
||||
```
|
||||
|
||||
```
|
||||
:::
|
||||
::::
|
||||
:::::
|
||||
::::::
|
||||
+2
-1
@@ -28,7 +28,8 @@ normalizes to it through the round-trip.
|
||||
adjacent lists of a kind back as one. The leaf `::listBreak` parts them, taking the separation
|
||||
any directive block takes where it sits. It builds no node, and it reads only between two
|
||||
adjacent lists of one type: elsewhere, or carrying an argument, `{attrs}` or a body, it is a
|
||||
named error.
|
||||
named error. A list whose item holds a line of spaces or tabs alone, which a list item reads
|
||||
back empty, takes the directive form.
|
||||
- Blockquotes prefix lines with `> `; a blank line inside a blockquote is a bare `>`.
|
||||
- ATX headings (`#` … `######`); setext input normalizes to ATX.
|
||||
- Code fences ``` with the node's language as info string, the fence lengthened past any backtick
|
||||
|
||||
@@ -167,6 +167,10 @@ export function isAutolink(text: string): boolean {
|
||||
return autolink.test(text)
|
||||
}
|
||||
|
||||
export function isBlankLine(line: string): boolean {
|
||||
return /^[ \t]*$/.test(line)
|
||||
}
|
||||
|
||||
export function isThematicBreak(line: string): boolean {
|
||||
return thematicBreak.test(line)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { carriedBlock } from '../opaque-carry.ts'
|
||||
import { emitInlineLine } from './inline-line.ts'
|
||||
import { failure, faulted, success, type ConvertErrorPath, type Result } from '../../result.ts'
|
||||
import { fencedCodeBlock } from '../backtick-runs.ts'
|
||||
import { holdsNullCharacter, isThematicBreak, markerInterruptsParagraph } from '../commonmark-grammar.ts'
|
||||
import { holdsNullCharacter, isBlankLine, isThematicBreak, markerInterruptsParagraph } from '../commonmark-grammar.ts'
|
||||
import { languageSlot } from '../code-language.ts'
|
||||
import { largestNesting } from '../../nesting.ts'
|
||||
import { listBreakSpelling } from '../list-break.ts'
|
||||
@@ -225,8 +225,10 @@ function emitListItem(item: AdfNode, marker: string, path: ConvertErrorPath, dep
|
||||
const inner = emitBlocks(nodeContent(item), 'list-item', path, depth + 1)
|
||||
if (!inner.ok) return inner
|
||||
if (inner.value.text === '') return success({ fenceColons: 0, text: marker.trimEnd() })
|
||||
const body = inner.value.text.split('\n')
|
||||
if (body.some((line) => line !== '' && isBlankLine(line))) return undefined
|
||||
const indent = ' '.repeat(marker.length)
|
||||
const lines = inner.value.text.split('\n').map((line, index) => (index === 0 ? `${marker}${line}` : line === '' ? '' : `${indent}${line}`))
|
||||
const lines = body.map((line, index) => (index === 0 ? `${marker}${line}` : line === '' ? '' : `${indent}${line}`))
|
||||
if (isThematicBreak(lines[0] ?? '')) return undefined
|
||||
return success({ fenceColons: inner.value.fenceColons, text: lines.join('\n') })
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
claimsPipeLine,
|
||||
closingCodeFence,
|
||||
decodeTextEscapes,
|
||||
isBlankLine,
|
||||
isThematicBreak,
|
||||
listMarker,
|
||||
markerInterruptsParagraph,
|
||||
@@ -61,7 +62,6 @@ type Line = { column: number; text: string }
|
||||
|
||||
type Walk = ParsedBlocks & { leaf: OpenLeaf | undefined; position: SourcePosition; stack: OpenContainer[] }
|
||||
|
||||
const blankLine = /^[ \t]*$/
|
||||
const indentedCodeColumns = 4
|
||||
const largestOpenerIndentation = 3
|
||||
const leafColons = 2
|
||||
@@ -117,7 +117,7 @@ function continuesContainer(walk: Walk, container: OpenContainer, line: Line): L
|
||||
// A directive container has no continuation marker: only its own fence closes it.
|
||||
if (container.kind === 'directive') return line
|
||||
// A list item begins with at most one blank line: an empty one gives the second up.
|
||||
if (blankLine.test(line.text)) {
|
||||
if (isBlankLine(line.text)) {
|
||||
return container.blocks.length === 0 && walk.leaf === undefined ? undefined : { column: line.column, text: '' }
|
||||
}
|
||||
return leadingColumns(line) < container.indentation ? undefined : removeColumns(line, container.indentation)
|
||||
@@ -155,7 +155,7 @@ function itemStart(line: Line, opener: Line, paragraphOpen: boolean, enclosing:
|
||||
const marker = listMarker(opener.text)
|
||||
if (marker === undefined) return undefined
|
||||
const after: Line = { column: opener.column + marker.width, text: opener.text.slice(marker.width) }
|
||||
const blank = blankLine.test(after.text)
|
||||
const blank = isBlankLine(after.text)
|
||||
if (paragraphOpen && !markerInterruptsParagraph(marker.start, blank)) return undefined
|
||||
const spaces = leadingColumns(after)
|
||||
const padding = blank || spaces > indentedCodeColumns ? 1 : spaces
|
||||
@@ -268,7 +268,7 @@ function pushFault(walk: Walk, fault: ConvertFault): void {
|
||||
|
||||
// A claimed line ends the lazy continuation CommonMark would fold it into (spec/flavour.md).
|
||||
function continuesLazily(walk: Walk, line: Line): boolean {
|
||||
if (walk.leaf?.kind !== 'paragraph' || blankLine.test(line.text)) return false
|
||||
if (walk.leaf?.kind !== 'paragraph' || isBlankLine(line.text)) return false
|
||||
if (leadingColumns(line) >= indentedCodeColumns) return true
|
||||
const opener = removeColumns(line, largestOpenerIndentation).text
|
||||
if (claimsDirectiveLine(opener) || claimsPipeLine(opener) || isThematicBreak(opener)) return false
|
||||
@@ -283,7 +283,7 @@ function readBlockLine(walk: Walk, line: Line): void {
|
||||
return
|
||||
}
|
||||
if (leaf?.kind === 'html') {
|
||||
if (leaf.closer === undefined ? blankLine.test(line.text) : leaf.closer.test(line.text)) closeLeaf(walk)
|
||||
if (leaf.closer === undefined ? isBlankLine(line.text) : leaf.closer.test(line.text)) closeLeaf(walk)
|
||||
return
|
||||
}
|
||||
if (leaf?.kind === 'pipe-table') {
|
||||
@@ -298,7 +298,7 @@ function readBlockLine(walk: Walk, line: Line): void {
|
||||
if (readIndentedCodeLine(leaf, line)) return
|
||||
closeLeaf(walk)
|
||||
}
|
||||
if (blankLine.test(line.text)) {
|
||||
if (isBlankLine(line.text)) {
|
||||
closeLeaf(walk)
|
||||
return
|
||||
}
|
||||
@@ -310,7 +310,7 @@ function readBlockLine(walk: Walk, line: Line): void {
|
||||
}
|
||||
|
||||
function readIndentedCodeLine(leaf: Extract<OpenLeaf, { kind: 'indented-code' }>, line: Line): boolean {
|
||||
if (blankLine.test(line.text)) {
|
||||
if (isBlankLine(line.text)) {
|
||||
leaf.held.push(removeColumns(line, indentedCodeColumns).text)
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user