diff --git a/CHANGELOG.md b/CHANGELOG.md index ec3350d..8873feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,6 @@ CommonMark reads it: `- a` then `* b`, `1. a` then `1) b`. `adfToLosslessMarkdown` gives a list right after one of its kind the other marker, `* ` or `1)`, in place of the `::listBreak` separator. Convert stored markdown per `MIGRATION.md`. -- **Breaking:** two lists a link reference definition parts read as two lists, as CommonMark - reads them, where they read as one; see `MIGRATION.md`. - **Breaking:** each call names the markdown it reads or writes. `adfToMarkdown` and `markdownToAdf` are now `adfToLosslessMarkdown` and `losslessMarkdownToAdf`. A bot's or a model's markdown reads in `portableMarkdownToAdf`, and markdown known to be strict CommonMark in the new @@ -58,6 +56,8 @@ `losslessMarkdownToAdf` refuses a directive mark wrapping only hard breaks as `unsupported-node-shape`. `adfToLosslessMarkdown` writes a hard break holding marks as `!adf:carry{json="…"}`. See `MIGRATION.md`. +- **Breaking:** two lists a link reference definition parts read as two lists, as CommonMark + reads them, where `0.1.0` read one; see `MIGRATION.md`. - `adfToLosslessMarkdown` and `adfToPortableMarkdown` write text holding a long run of `*`, `_`, `~` or backticks, or a heading ending in a long run of `#`, in time linear in the run: a run of 20000 `*` took seconds before, and now takes milliseconds. @@ -79,16 +79,16 @@ - Fix a cyclic document, in which an object holds itself: `adfToLosslessMarkdown` and `adfToPortableMarkdown` refuse it as `not-an-adf-document` and `isAdfDocument` returns `false`, where all three hung. -- The README opens with what the package does and for whom, and `package.json` carries search - keywords. -- The source moves to [github.com/larvit/adf-codec](https://github.com/larvit/adf-codec), where - issues and pull requests go. - Add `adfToPortableMarkdown` and `portableMarkdownToAdf`, a lossy pair converting ADF to and from markdown GitHub, GitLab and Obsidian render: alerts, callouts, task lists, `==highlights==` and pipe tables. A task item keeps its paragraphs, then its nested task lists; the rest of the item, such as an image or a code block, stands after the task list. - Spell `rule`'s `color`, `style` and `weight`, `layoutSection`'s `columnRuleStyle` and a link's `collection`, `id` and `occurrenceKey` directly where they rode the opaque carry. +- The README opens with what the package does and for whom, and `package.json` carries search + keywords. +- The source moves to [github.com/larvit/adf-codec](https://github.com/larvit/adf-codec), where + issues and pull requests go. ## 0.1.0 diff --git a/README.md b/README.md index d6a73cc..a847a8b 100644 --- a/README.md +++ b/README.md @@ -239,8 +239,8 @@ Serves Goals 1, 4 and 5. without a delimiter row, is an error result; each can be kept literal (`spec/flavour.md`). - A document any reader built, written back with `adfToLosslessMarkdown`, takes the library's canonical spelling, which round-trips byte-identically. -- Six CommonMark spellings parse without an error and build a document the reference - implementation renders differently. Four because ADF holds no node for what it renders: +- These CommonMark spellings parse without an error and build a document the reference + implementation renders differently, where ADF holds no node for what it renders: - An empty link text reads as its destination, `[](/url)` as `/url` linked to `/url`, and `[]()` as nothing. A link text holding only hard breaks reads the same, followed by its hard breaks. - An image reads as an image only alone in its paragraph. Anywhere else — beside other text, in @@ -253,11 +253,12 @@ Serves Goals 1, 4 and 5. - An image's title becomes its caption when the image stays an image, and its link's title when it becomes linked alt text. Inside another link, the title is dropped. - And two more: a shortcut reference matching its definition only under Unicode case folding - stays unresolved, and a link whose text holds an autolink keeps the inner link and leaves the - outer brackets literal text, which the spec requires and the reference itself breaks, nesting one - `` in the other. `corpus/commonmark-spec/exceptions.json` pins the first four `node-model` - where the suite reaches them and the fifth `pending`; the suite holds no example of the last. + And these: a shortcut reference matching its definition only under Unicode case folding stays + unresolved, and a link whose text holds an autolink keeps the inner link and leaves the outer + brackets literal text, which the spec requires and the reference itself breaks, nesting one `` + in the other. `corpus/commonmark-spec/exceptions.json` pins the spellings ADF holds no node for + `node-model` where the suite reaches them, and the case-folded reference `pending`; the suite + holds no example of a link holding an autolink. - Not every document converts back: `adfToLosslessMarkdown` refuses a document nesting past 500 levels, and the node shapes the `unsupported-node-shape` row lists. Show the refusal and keep the document read-only; saving markdown you could not produce is the loss the round-trip exists to diff --git a/src/markdown/parse/blocks.ts b/src/markdown/parse/blocks.ts index 8301004..a5abe84 100644 --- a/src/markdown/parse/blocks.ts +++ b/src/markdown/parse/blocks.ts @@ -75,8 +75,8 @@ type ContainerStack = { type Walk = ParsedBlocks & { claims: Claims - // The block a paragraph of link reference definitions alone last closed after, which ends a list as any block does. - definedAfter: Block | undefined + // The last block before a paragraph of link reference definitions alone, which ends a list as any block does. + endedByDefinitions: Block | undefined leaf: OpenLeaf | undefined leafOpeners: Map> position: SourcePosition @@ -91,8 +91,8 @@ export function parseBlocks(markdown: string, claims: Claims): ParsedBlocks { const walk: Walk = { blocks: [], claims, - definedAfter: undefined, definitions: new Map(), + endedByDefinitions: undefined, leaf: undefined, leafOpeners: new Map(), position: { line: 1, offset: 0 }, @@ -249,12 +249,12 @@ function openContainer(walk: Walk, start: ContainerStart): void { walk.stack.push({ blocks, indentation: start.indentation, kind: 'item', list }) } -// An item opening after its list's last item closed, as an empty item a blank line ends does, continues that list. +// An item opening after its list's last item closed, as an empty item a blank line ends does, continues that list, unless definitions ended it. function openedList(walk: Walk, start: Extract): ListBlock { if (!start.fresh) return start.list const blocks = currentBlocks(walk) const previous = blocks.at(-1) - if ((previous?.kind === 'bulletList' || previous?.kind === 'orderedList') && previous !== walk.definedAfter && continuesList(previous, start.list)) return previous + if ((previous?.kind === 'bulletList' || previous?.kind === 'orderedList') && previous !== walk.endedByDefinitions && continuesList(previous, start.list)) return previous blocks.push(start.list) return start.list } @@ -466,7 +466,8 @@ function closeLeaf(walk: Walk): void { if (leaf === undefined) return if (leaf.kind === 'paragraph') { const paragraph = takeParagraph(walk) - if (paragraph !== undefined) currentBlocks(walk).push(bareTableFault(paragraph, walk.claims) ?? paragraph) + if (paragraph === undefined) walk.endedByDefinitions = currentBlocks(walk).at(-1) + else currentBlocks(walk).push(bareTableFault(paragraph, walk.claims) ?? paragraph) return } walk.leaf = undefined @@ -517,10 +518,7 @@ function takeParagraph(walk: Walk): Extract | unde if (leaf?.kind !== 'paragraph') return undefined walk.leaf = undefined const text = readLinkDefinitions(walk.definitions, leaf.lines.join('\n')) - if (text === '') { - walk.definedAfter = currentBlocks(walk).at(-1) - return undefined - } + if (text === '') return undefined const kept = leaf.positions[leaf.lines.length - text.split('\n').length] return { kind: 'paragraph', position: kept ?? leaf.position, text } } diff --git a/todo.md b/todo.md index b21ac48..a59c61f 100644 --- a/todo.md +++ b/todo.md @@ -6,7 +6,7 @@ `Bar = 9` -`Next ID = 98` +`Next ID = 99` | Goal | W | |---|---| @@ -35,6 +35,7 @@ | 96 | 0.2.1 | defect | **Read and write a text holding many unclosed backtick runs, or many `