Rewrap the broken lines, and say plainly how an adf: fence stays code

This commit is contained in:
2026-10-03 15:06:11 +02:00
parent fb78063f12
commit 912c5039aa
4 changed files with 29 additions and 28 deletions
+2 -2
View File
@@ -9,8 +9,8 @@
`type`: text holding an unescaped `!adf:` and a code fence whose info string opens `adf:` are
claimed, and `adf` is an ordinary code block language. Convert stored markdown per `MIGRATION.md`.
- **Breaking:** `markdownToAdf` and `plainMarkdownToAdf` read markdown holding no block as a
document whose `content` is empty, as Atlassian's schema requires; `!adf:doc {content=none}` spells a document holding no
`content` key.
document whose `content` is empty, as Atlassian's schema requires; `!adf:doc {content=none}`
spells a document holding no `content` key.
- `markdownToAdf(adfToMarkdown(doc))` deep-equals `doc`: two adjacent text nodes a reader would
join are parted by `!adf:textBreak{}`, an empty `attrs`, `content` or `marks` is spelled
`{attrs=empty}`, `{content=empty}` or `{marks=empty}`, `-0` is spelled `-0`, and a `codeBlock`
+7 -8
View File
@@ -212,14 +212,13 @@ Serves Goals 1, 3 and 4.
`0.2.0`, well-formed HTML means what the HTML standard says, read or written. The bullets below
name every exception.
- Plain CommonMark is valid input to `markdownToAdf` apart from the raw HTML `unmappable-html`
names, with four carve-outs — literal text matching directive, pipe-table or strikethrough
syntax, and a code fence whose info string opens `adf:`, is claimed (escapable — `spec/flavour.md`)
— and one gap: a CommonMark image fits only as
its own title-less paragraph; mid-text and titled images are error results, save an image inside
another's description, which flattens into the alt text. Converting back yields the library's
canonical spelling, which round-trips byte-identically — where it converts back at all: a parse
succeeding is no promise of that, so keep the source until the way back succeeds.
``` ` `` ` ``` reads cleanly and then refuses.
names, with four carve-outs — literal text matching directive, pipe-table or strikethrough syntax,
and a code fence whose info string opens `adf:`, are claimed (escapable — `spec/flavour.md`) — and
one gap: a CommonMark image fits only as its own title-less paragraph; mid-text and titled images
are error results, save an image inside another's description, which flattens into the alt text.
Converting back yields the library's canonical spelling, which round-trips byte-identically —
where it converts back at all: a parse succeeding is no promise of that, so keep the source until
the way back succeeds. ``` ` `` ` ``` reads cleanly and then refuses.
- Four CommonMark spellings parse without an error and build a document the reference
implementation renders differently: `[](/url)` and `[]()` stay literal text against CommonMark's
empty link, a list continuing past a marker change stays one list against CommonMark's two, a
+5 -4
View File
@@ -19,7 +19,8 @@ One directory per contract kind:
model), `unspellable` (parses but the flavour has no spelling) or `pending` (a parser gap).
JSON is two-space indent, keys sorted, and a document read back must deep-equal the fixture's
(`docs/decisions.md` §Equality is deep). `spec.json` is the vendored, upstream machine-readable suite, byte-exact from
[spec.commonmark.org](https://spec.commonmark.org/0.31.2/spec.json) (CommonMark 0.31.2, © John
MacFarlane, [CC-BY-SA-4.0](https://creativecommons.org/licenses/by-sa/4.0/)), and is not
re-serialized by the corpus gate.
(`docs/decisions.md` §Equality is deep). `spec.json` is the vendored, upstream machine-readable
suite, byte-exact from [spec.commonmark.org](https://spec.commonmark.org/0.31.2/spec.json)
(CommonMark 0.31.2, © John MacFarlane,
[CC-BY-SA-4.0](https://creativecommons.org/licenses/by-sa/4.0/)), and is not re-serialized by the
corpus gate.
+15 -14
View File
@@ -4,10 +4,11 @@ The grammar of the extended markdown `adfToMarkdown` emits and `markdownToAdf` p
CommonMark is a subset apart from raw HTML (below), with four carve-outs: literal text that matches
directive syntax below or reads as a pipe table is claimed by the flavour, a matched `~~` pair
spells `strike` (escape the `!adf:`, `|` or `~` to keep it literal), and a code fence whose info
string opens `adf:` is the opaque carry (The opaque carry says how to keep it code) — and one gap: a CommonMark
image fits only as its own title-less paragraph — mid-text and titled images are named errors. The
emitted form is contract (`docs/decisions.md` §The formats are API). Per-node syntaxes build on this
grammar in the sections below.
string opens `adf:` is the opaque carry (wrap it as a bare fence in `!adf:codeBlock
{language="adf:…"}` to keep it code) — and one gap: a CommonMark image fits only as its own
title-less paragraph — mid-text and titled images are named errors. The emitted form is contract
(`docs/decisions.md` §The formats are API). Per-node syntaxes build on this grammar in the sections
below.
## Canonical form
@@ -446,8 +447,8 @@ Attributes and the carry fallback read as in the block sections, the carry in it
the nodes below, `emoji`, `mention` and `status` spell their `text` attribute in the content slot as
plain text: `[]` is the empty string, absent content is the absent attribute, non-empty content
parsing to anything but one text node carrying neither marks, attributes nor content is a named
error, and so is a `text` key in `{attrs}`. An enclosing mark spelling does not reach into the slot. The rest take no content,
`!adf:text` included; content on a node that takes none is a named error.
error, and so is a `text` key in `{attrs}`. An enclosing mark spelling does not reach into the slot.
The rest take no content, `!adf:text` included; content on a node that takes none is a named error.
- `date` — Attributes: `localId` (string), `timestamp` (string, epoch milliseconds).
- `emoji` — Attributes: `id` (string), `localId` (string), `shortName` (string, `:name:`), `text`
@@ -527,14 +528,14 @@ that depth: `attrs: {}` differs from no `attrs`, and a directive spells it `{att
breaks at every node the emitter carries, so no emitted carry sits inside a mark spelling.
An inline node whose marks no nesting spells — a mark type not listed here, an attrs key its
spelling does not list, a value that is not the spelling's type, an attribute the spelling needs
and the mark lacks, an empty `attrs` on a mark CommonMark spells, an order putting a code span outside another mark, `code` over anything but a
text node or over text holding a newline, or a spelling CommonMark's flanking rules cannot open or
close where the run sits (`un**-real**istic`), or one CommonMark's matching pairs elsewhere — the
intra-word `*` runs together with a neighbouring `**`, and the multiple-of-3 rule can leave the
merged run's pairing to another delimiter — rides the inline carry whole. An opaque carry inside a
mark spelling is a named error in input: the carry restores its node exactly, marks included
(`docs/decisions.md` §Unknown nodes ride the carry).
spelling does not list, a value that is not the spelling's type, an attribute the spelling needs and
the mark lacks, an empty `attrs` on a mark CommonMark spells, an order putting a code span outside
another mark, `code` over anything but a text node or over text holding a newline, or a spelling
CommonMark's flanking rules cannot open or close where the run sits (`un**-real**istic`), or one
CommonMark's matching pairs elsewhere — the intra-word `*` runs together with a neighbouring `**`,
and the multiple-of-3 rule can leave the merged run's pairing to another delimiter — rides the
inline carry whole. An opaque carry inside a mark spelling is a named error in input: the carry
restores its node exactly, marks included (`docs/decisions.md` §Unknown nodes ride the carry).
```
!adf:textColor[**Overdue**]{color="#ae2e24"}, H!adf:subsup[2]{type=sub}O, !adf:underline[signed].