mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
Answer prose round 1: plainer decision title and spec, the refusal the spec owes, and comments that say why
This commit is contained in:
@@ -25,7 +25,7 @@ In `docs/decisions.md`:
|
|||||||
- CommonMark is a subset
|
- CommonMark is a subset
|
||||||
- Tables
|
- Tables
|
||||||
- Links
|
- Links
|
||||||
- An image reads as an image only alone in its paragraph, else as its alt text linked; a link text holding nothing a mark rides reads as its destination, then the hard breaks it held
|
- An image reads as an image only alone in its paragraph, else as its alt text linked; a link text holding no node that takes marks reads as its destination, then its hard breaks
|
||||||
- Ids stay site-local
|
- Ids stay site-local
|
||||||
- Portable task ids come from position
|
- Portable task ids come from position
|
||||||
- A callout title keeps its link targets
|
- A callout title keeps its link targets
|
||||||
|
|||||||
+2
-2
@@ -75,8 +75,8 @@
|
|||||||
whose text is only hard breaks reads as its destination linked, followed by those breaks, and
|
whose text is only hard breaks reads as its destination linked, followed by those breaks, and
|
||||||
`losslessMarkdownToAdf` refuses a directive mark wrapping only hard breaks as
|
`losslessMarkdownToAdf` refuses a directive mark wrapping only hard breaks as
|
||||||
`unsupported-node-shape`. `adfToLosslessMarkdown` writes a hard break holding marks as
|
`unsupported-node-shape`. `adfToLosslessMarkdown` writes a hard break holding marks as
|
||||||
`!adf:carry{json="…"}`, and both writers spell an emphasis or link across the hard breaks inside
|
`!adf:carry{json="…"}`, and spells an emphasis or link across the hard breaks inside it once,
|
||||||
it once, `_a\` then `b_`, where they closed and reopened it. See `MIGRATION.md`.
|
`_a\` then `b_`, where `adfToMarkdown` closed and reopened it. See `MIGRATION.md`.
|
||||||
- Fix a cyclic document, in which an object holds itself: `adfToLosslessMarkdown` and
|
- Fix a cyclic document, in which an object holds itself: `adfToLosslessMarkdown` and
|
||||||
`adfToPortableMarkdown` refuse it as `not-an-adf-document` and `isAdfDocument` returns `false`,
|
`adfToPortableMarkdown` refuse it as `not-an-adf-document` and `isAdfDocument` returns `false`,
|
||||||
where all three hung.
|
where all three hung.
|
||||||
|
|||||||
+2
-2
@@ -29,7 +29,7 @@ npm install @larvit/adf-codec@0.2.0 adf-codec-0.1@npm:@larvit/adf-codec@0.1.0
|
|||||||
import { adfToLosslessMarkdown, type AdfNode } from '@larvit/adf-codec'
|
import { adfToLosslessMarkdown, type AdfNode } from '@larvit/adf-codec'
|
||||||
import { markdownToAdf as markdownToAdf010 } from 'adf-codec-0.1'
|
import { markdownToAdf as markdownToAdf010 } from 'adf-codec-0.1'
|
||||||
|
|
||||||
// 0.1.0 gave a hard break the marks spelled around it; Atlassian's schema gives it none.
|
// 0.1.0 gave a hard break the marks spelled around it; 0.2.0 writes such a break as !adf:carry{…}.
|
||||||
function unmarkHardBreaks(node: AdfNode): AdfNode {
|
function unmarkHardBreaks(node: AdfNode): AdfNode {
|
||||||
const { marks, ...unmarked } = node
|
const { marks, ...unmarked } = node
|
||||||
if (node.type === 'hardBreak') return unmarked
|
if (node.type === 'hardBreak') return unmarked
|
||||||
@@ -72,7 +72,7 @@ Markdown the spelling table leaves alone, which `0.2.0` reads as a different doc
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| a link whose text already holds one (`[a<https://example.com/>b](/v)`) | marks every node the inner link does not, splitting the outer link around it | leaves the outer brackets literal text; write the pieces as separate links to keep them |
|
| a link whose text already holds one (`[a<https://example.com/>b](/v)`) | marks every node the inner link does not, splitting the outer link around it | leaves the outer brackets literal text; write the pieces as separate links to keep them |
|
||||||
| markdown holding no block (`""`) | `{ type: 'doc', version: 1 }` | `{ content: [], type: 'doc', version: 1 }`; `!adf:doc {content=none}` reads as the former |
|
| markdown holding no block (`""`) | `{ type: 'doc', version: 1 }` | `{ content: [], type: 'doc', version: 1 }`; `!adf:doc {content=none}` reads as the former |
|
||||||
| a hard break inside a mark's spelling (`*a\` then `b*` on the next line) | the hard break holds the mark | the hard break holds no marks, as Atlassian's schema requires, and the recipe above drops them from stored markdown. A link whose text is only hard breaks reads as its destination linked, followed by those breaks |
|
| a hard break inside a mark's spelling (`*a\` then `b*` on the next line) | the hard break holds the mark | the hard break holds no marks, as Atlassian's schema requires, and the recipe above drops the marks `0.1.0` gave it. A link whose text is only hard breaks reads as its destination linked, followed by those breaks |
|
||||||
| a link whose text is empty (`[](/url)`, `[]()`) | literal text | `/url` linked to `/url`, and nothing where the destination is empty too |
|
| a link whose text is empty (`[](/url)`, `[]()`) | literal text | `/url` linked to `/url`, and nothing where the destination is empty too |
|
||||||
| a code fence whose info string opens `adf:` (```` ```adf:x ````) | a `codeBlock` with that language | in `losslessMarkdownToAdf`, the block carry, refusing a body that is not one node's canonical JSON; write `!adf:codeBlock {language="adf:x"}` around a bare fence to keep the code block |
|
| a code fence whose info string opens `adf:` (```` ```adf:x ````) | a `codeBlock` with that language | in `losslessMarkdownToAdf`, the block carry, refusing a body that is not one node's canonical JSON; write `!adf:codeBlock {language="adf:x"}` around a bare fence to keep the code block |
|
||||||
|
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ emit refuses:
|
|||||||
| Code | Fires when | What you can do |
|
| Code | Fires when | What you can do |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `unsupported-nesting-depth` | blocks, marks, an attribute's JSON or a carried node's JSON nest past 500 levels | keep the ADF and pass the document over, or show it read-only; flatten the input where you are the one who wrote it |
|
| `unsupported-nesting-depth` | blocks, marks, an attribute's JSON or a carried node's JSON nest past 500 levels | keep the ADF and pass the document over, or show it read-only; flatten the input where you are the one who wrote it |
|
||||||
| `unsupported-node-shape` | parsing, in `losslessMarkdownToAdf`: markdown spells a node with an attribute, value, argument or body its type does not take, or without one it needs, wraps a directive mark around nothing or only hard breaks, writes as a directive a node or mark the lossless flavour spells as CommonMark, or puts a reserved directive out of place: `!adf:textBreak{}` or `!adf:listBreak` parting nothing, `!adf:doc` anywhere but as the whole document. Emitting: a node carrying one mark type twice, unless it rides the carry, a block or a non-text inline node holding `text`, or a leaf block or a non-text inline node holding `content` | fix what the message names; `spec/flavour.md` lists every type's attributes and body |
|
| `unsupported-node-shape` | parsing, in `losslessMarkdownToAdf`: markdown spells a node with an attribute, value, argument or body its type does not take, or without one it needs, wraps a directive mark around nothing or only hard breaks, writes as a directive a node or mark the lossless flavour spells as CommonMark, or puts a reserved directive out of place: `!adf:textBreak{}` or `!adf:listBreak` parting nothing, `!adf:doc` anywhere but as the whole document. Emitting: a node not riding the carry that carries one mark type twice, a block or a non-text inline node holding `text`, or a leaf block or a non-text inline node holding `content` | fix what the message names; `spec/flavour.md` lists every type's attributes and body |
|
||||||
|
|
||||||
## The guarantees
|
## The guarantees
|
||||||
|
|
||||||
|
|||||||
@@ -466,13 +466,13 @@
|
|||||||
"divergence": "em 1/2",
|
"divergence": "em 1/2",
|
||||||
"example": 638,
|
"example": 638,
|
||||||
"kind": "mark-model",
|
"kind": "mark-model",
|
||||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
"reason": "The mark spans the text nodes either side of the hard break, counted once per node where CommonMark nests one element."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"check": "count",
|
"check": "count",
|
||||||
"divergence": "em 1/2",
|
"divergence": "em 1/2",
|
||||||
"example": 639,
|
"example": 639,
|
||||||
"kind": "mark-model",
|
"kind": "mark-model",
|
||||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
"reason": "The mark spans the text nodes either side of the hard break, counted once per node where CommonMark nests one element."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+6
-7
@@ -202,19 +202,18 @@ link reference definition — and a directive link CommonMark could spell is ref
|
|||||||
link — the bracket form goes literal, the directive form refused — which is CommonMark's prose
|
link — the bracket form goes literal, the directive form refused — which is CommonMark's prose
|
||||||
where its reference implementation nests one `<a>` in another.
|
where its reference implementation nests one `<a>` in another.
|
||||||
|
|
||||||
## An image reads as an image only alone in its paragraph, else as its alt text linked; a link text holding nothing a mark rides reads as its destination, then the hard breaks it held
|
## An image reads as an image only alone in its paragraph, else as its alt text linked; a link text holding no node that takes marks reads as its destination, then its hard breaks
|
||||||
|
|
||||||
2026-10-04, the maintainer and a writer panel, 3 of 3; `[]()` 2026-10-09, by Goals 4 and 7, as the
|
2026-10-04, the maintainer and a writer panel, 3 of 3; `[]()` 2026-10-09, by Goals 4 and 7, as the
|
||||||
maintainer's plan item asked; a link text of hard breaks alone 2026-10-09, the maintainer, after a
|
maintainer's plan item asked; a link text of hard breaks alone 2026-10-09, the maintainer. Goals 4, 5, 6 and 7. Valid while
|
||||||
reader panel split 4 to 3. Goals 4, 5, 6 and 7. Valid while `mediaInline` carries no URL, ADF holds
|
`mediaInline` carries no URL, ADF holds no empty text node, a hard break takes no marks and no
|
||||||
no empty text node and a hard break takes no marks.
|
consumer has reported a link of hard breaks alone.
|
||||||
|
|
||||||
Every reader reads every CommonMark image and empty link, as `spec/flavour.md` §The CommonMark image
|
Every reader reads every CommonMark image and empty link, as `spec/flavour.md` §The CommonMark image
|
||||||
and §Marks spell out. An image alone in its paragraph reads as the image, its title as the caption.
|
and §Marks spell out. An image alone in its paragraph reads as the image, its title as the caption.
|
||||||
Anywhere else it reads as its alt text linked to its URL, titled by its title. An empty alt text
|
Anywhere else it reads as its alt text linked to its URL, titled by its title. An empty alt text
|
||||||
reads as the URL, and an empty link text as its destination; a link text holding only hard breaks,
|
reads as the URL, and an empty link text as its destination. A link text holding only hard breaks
|
||||||
which take no marks, reads as its destination followed by those breaks, which four of seven readers
|
reads as its destination, then those breaks: four of seven readers expected that over dropping them.
|
||||||
expected over dropping them, and the maintainer kept as an edge case until real reports arrive.
|
|
||||||
`[]()` reads as nothing: it renders nothing, so Goal 7 loses no content. A link alone in its
|
`[]()` reads as nothing: it renders nothing, so Goal 7 loses no content. A link alone in its
|
||||||
paragraph whose whole text is one image reads as that image, with the link marking its `media`.
|
paragraph whose whole text is one image reads as that image, with the link marking its `media`.
|
||||||
Inside any other link, the alt text is plain text the link marks, and the image's URL and title
|
Inside any other link, the alt text is plain text the link marks, and the image's URL and title
|
||||||
|
|||||||
+18
-18
@@ -511,20 +511,20 @@ Hello, !adf:textBreak{}world — **Hello, !adf:textBreak{}world** — `a`!adf:te
|
|||||||
|
|
||||||
An inline node's marks ride the spelling wrapped around them, never the block sections' reserved
|
An inline node's marks ride the spelling wrapped around them, never the block sections' reserved
|
||||||
`marks` key. `code`, `em`, `strike` and `strong` keep their markdown spellings, and are not
|
`marks` key. `code`, `em`, `strike` and `strong` keep their markdown spellings, and are not
|
||||||
directive names: `!adf:em[x]` is a named error. `border`, `subsup`, `textColor` and `underline`
|
directive names: `!adf:em[x]` is a named error. `border`, `subsup`, `textColor` and `underline` are
|
||||||
are inline directives, content required non-empty. `link` keeps its markdown spelling wherever
|
inline directives, content required, holding more than hard breaks. `link` keeps its markdown
|
||||||
CommonMark holds it — `<url>` for a bare autolink-shaped text, else `[text](url "title")` — and is
|
spelling wherever CommonMark holds it — `<url>` for a bare autolink-shaped text, else `[text](url
|
||||||
the inline directive `!adf:link[text]{attrs}` only where CommonMark does not: an attribute besides
|
"title")` — and is the inline directive `!adf:link[text]{attrs}` only where CommonMark does not: an
|
||||||
`href` and `title`, an `href` or `title` no canonical escape spells (a control character, a
|
attribute besides `href` and `title`, an `href` or `title` no canonical escape spells (a control
|
||||||
backslash, an entity reference, an angle bracket beside a space or opening a bare destination, a
|
character, a backslash, an entity reference, an angle bracket beside a space or opening a bare
|
||||||
newline in the title), or a link opening a paragraph whose markdown spelling would read as a link
|
destination, a newline in the title), or a link opening a paragraph whose markdown spelling would
|
||||||
reference definition. Every such spelling carries an `href`: a directive link CommonMark could spell
|
read as a link reference definition. Every such spelling carries an `href`: a directive link
|
||||||
is a named error, and so is one spelling none. No link wraps a link at any nesting, which is
|
CommonMark could spell is a named error, and so is one spelling none. No link wraps a link at any
|
||||||
CommonMark's own rule: a `[text]` already holding one leaves the outer brackets literal text, and
|
nesting, which is CommonMark's own rule: a `[text]` already holding one leaves the outer brackets
|
||||||
the directive form, open to no literal reading, is a named error. ADF holds no empty text node, so a
|
literal text, and the directive form, open to no literal reading, is a named error. ADF holds no
|
||||||
link whose text is empty reads as its destination linked, `[](/url)` as `/url`, and one whose
|
empty text node, so a link whose text is empty reads as its destination linked, `[](/url)` as
|
||||||
destination is empty too, `[]()`, as nothing. A link text holding only hard breaks reads the same,
|
`/url`, and one whose destination is empty too, `[]()`, as nothing. A link text holding only hard
|
||||||
followed by its hard breaks.
|
breaks reads the same, followed by its hard breaks.
|
||||||
|
|
||||||
- `border` — Attributes: `color` (string, `#rrggbb` or `#rrggbbaa`), `size` (number, 1–3).
|
- `border` — Attributes: `color` (string, `#rrggbb` or `#rrggbbaa`), `size` (number, 1–3).
|
||||||
- `code`, `em`, `strike`, `strong` — Attributes: none.
|
- `code`, `em`, `strike`, `strong` — Attributes: none.
|
||||||
@@ -539,10 +539,10 @@ gives no marks, and nesting is the marks array in order, outermost first: `_!adf
|
|||||||
gives marks `[em, underline]`, `!adf:underline[_x_]` the reverse. `adfToLosslessMarkdown` nests in
|
gives marks `[em, underline]`, `!adf:underline[_x_]` the reverse. `adfToLosslessMarkdown` nests in
|
||||||
the order the array holds rather than sorting it — `docs/decisions.md` §Equality is deep restores
|
the order the array holds rather than sorting it — `docs/decisions.md` §Equality is deep restores
|
||||||
the array, not a set — and opens each spelling once over the longest run of adjacent inline nodes
|
the array, not a set — and opens each spelling once over the longest run of adjacent inline nodes
|
||||||
carrying an identical mark, attributes included, at that depth, an emphasis or link spelling also
|
carrying an identical mark, attributes included, at that depth: `attrs: {}` differs from no `attrs`,
|
||||||
spanning the hard breaks between two of them: `attrs: {}` differs from no `attrs`, and a directive
|
and a directive spells it `{attrs=empty}`. An emphasis or link spelling also spans the hard breaks
|
||||||
spells it `{attrs=empty}`. A run breaks at every node the emitter carries, so no emitted carry sits
|
between two nodes of its run. A run breaks at every node the emitter carries, so no emitted carry
|
||||||
inside a mark spelling.
|
sits inside a mark spelling.
|
||||||
|
|
||||||
An inline node whose marks no nesting spells — a hard break holding any mark, a mark type not listed
|
An inline node whose marks no nesting spells — a hard break holding any mark, 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
|
here, an attrs key its spelling does not list, a value that is not the spelling's type, an attribute
|
||||||
|
|||||||
@@ -728,7 +728,7 @@ test('spells a mark run across the hard break between two nodes holding the mark
|
|||||||
assert.equal(readsBack(document(paragraph({ text: 'x', type: 'text' }, marked('.', em), hardBreak, marked('b', em)))), 'x!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"em\\"}],\\"text\\":\\".\\",\\"type\\":\\"text\\"}"}\\\n_b_\n')
|
assert.equal(readsBack(document(paragraph({ text: 'x', type: 'text' }, marked('.', em), hardBreak, marked('b', em)))), 'x!adf:carry{json="{\\"marks\\":[{\\"type\\":\\"em\\"}],\\"text\\":\\".\\",\\"type\\":\\"text\\"}"}\\\n_b_\n')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('carries a hard break holding marks, which Atlassian\'s schema gives it none of', () => {
|
test('carries a hard break holding marks, which Atlassian\'s schema withholds', () => {
|
||||||
const link: AdfMark = { attrs: { href: 'https://example.com/' }, type: 'link' }
|
const link: AdfMark = { attrs: { href: 'https://example.com/' }, type: 'link' }
|
||||||
assert.equal(
|
assert.equal(
|
||||||
readsBack(document(paragraph({ marks: [link], text: 'a', type: 'text' }, { marks: [link], type: 'hardBreak' }, { marks: [link], text: 'b', type: 'text' }))),
|
readsBack(document(paragraph({ marks: [link], text: 'a', type: 'text' }, { marks: [link], type: 'hardBreak' }, { marks: [link], text: 'b', type: 'text' }))),
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ function inlineRuns(nodes: readonly AdfNode[], depth: number, firstIndex: number
|
|||||||
return runs
|
return runs
|
||||||
}
|
}
|
||||||
|
|
||||||
// A directive's [content] holds no line ending, so only an emphasis or link spelling spans the hard breaks between two nodes it marks.
|
// A directive's [content] holds no line ending, and `!adf:hardBreak{}` there reads worse than closing and reopening it, so only an emphasis or link spelling spans the hard breaks between two nodes it marks.
|
||||||
function joinsRun(nodes: readonly AdfNode[], offset: number, firstIndex: number, run: MarkedRun, depth: number, context: InlineContext): boolean {
|
function joinsRun(nodes: readonly AdfNode[], offset: number, firstIndex: number, run: MarkedRun, depth: number, context: InlineContext): boolean {
|
||||||
const kind = markSpelling(run.mark.type)?.kind
|
const kind = markSpelling(run.mark.type)?.kind
|
||||||
if (kind !== 'emphasis' && kind !== 'link') return false
|
if (kind !== 'emphasis' && kind !== 'link') return false
|
||||||
@@ -239,7 +239,7 @@ function joinsRun(nodes: readonly AdfNode[], offset: number, firstIndex: number,
|
|||||||
return node?.type === 'hardBreak' && !carries(node, context.carried, firstIndex + at) && !context.parted.has(firstIndex + at)
|
return node?.type === 'hardBreak' && !carries(node, context.carried, firstIndex + at) && !context.parted.has(firstIndex + at)
|
||||||
}
|
}
|
||||||
if (!joinable(offset)) return false
|
if (!joinable(offset)) return false
|
||||||
// A later break in a sequence joins because the first one found the node closing it.
|
// A later break in a sequence joins because the first one found the node closing the run.
|
||||||
if (run.nodes.at(-1)?.type === 'hardBreak') return true
|
if (run.nodes.at(-1)?.type === 'hardBreak') return true
|
||||||
let ahead = offset + 1
|
let ahead = offset + 1
|
||||||
while (joinable(ahead)) ahead += 1
|
while (joinable(ahead)) ahead += 1
|
||||||
@@ -259,7 +259,7 @@ function nodePath(context: InlineContext, index: number): ConvertErrorPath {
|
|||||||
return [...context.path, 'content', index]
|
return [...context.path, 'content', index]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decides the text-node, mark and slot-fault carries up front, so a line holding many of them is emitted once.
|
// Decides the text-node, marked-hard-break and slot-fault carries up front, so a line holding many of them is emitted once.
|
||||||
function carries(node: AdfNode, carried: ReadonlySet<number>, index: number): boolean {
|
function carries(node: AdfNode, carried: ReadonlySet<number>, index: number): boolean {
|
||||||
if (carried.has(index)) return true
|
if (carried.has(index)) return true
|
||||||
if (node.type === 'text') return !isSpellableText(node) || typeof node.text !== 'string' || node.text === ''
|
if (node.type === 'text') return !isSpellableText(node) || typeof node.text !== 'string' || node.text === ''
|
||||||
@@ -327,7 +327,7 @@ function emitMarkedRun(nodes: readonly AdfNode[], mark: AdfMark, depth: number,
|
|||||||
const inner = emitRun(nodes, depth + 1, index, { ...context, bracketed: true })
|
const inner = emitRun(nodes, depth + 1, index, { ...context, bracketed: true })
|
||||||
if (!inner.ok) return inner
|
if (!inner.ok) return inner
|
||||||
if (inner.value.carry !== undefined) return inner
|
if (inner.value.carry !== undefined) return inner
|
||||||
// A link `tryLink` gave back holds the hard breaks `joinsRun` joined; the carry's fallback parts them.
|
// A link `tryLink` declined holds the hard breaks `joinsRun` joined; the carry's fallback parts them.
|
||||||
if (inner.value.segments.some((segment) => segment.text.includes('\n'))) return success({ carry: range })
|
if (inner.value.segments.some((segment) => segment.text.includes('\n'))) return success({ carry: range })
|
||||||
return success({ segments: [syntax(spellInlineDirectiveOpener(mark.type)), ...inner.value.segments, syntax(`]${attributes}`)] })
|
return success({ segments: [syntax(spellInlineDirectiveOpener(mark.type)), ...inner.value.segments, syntax(`]${attributes}`)] })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ function linkMark({ destination, title }: LinkDefinition): AdfMark {
|
|||||||
return { attrs: title === undefined ? { href: destination } : { href: destination, title }, type: 'link' }
|
return { attrs: title === undefined ? { href: destination } : { href: destination, title }, type: 'link' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ADF holds no empty text node for a link mark to ride, so a link text holding nothing a mark rides reads as its destination, then the hard breaks it held, and an empty destination as those breaks alone.
|
// ADF holds no empty text node and a hard break takes no marks, so the destination text carries the link.
|
||||||
function linkTo(nodes: readonly Inline[], definition: LinkDefinition): Inline[] {
|
function linkTo(nodes: readonly Inline[], definition: LinkDefinition): Inline[] {
|
||||||
if (!nodes.every(takesNoMarks)) return applyMark(nodes, linkMark(definition))
|
if (!nodes.every(takesNoMarks)) return applyMark(nodes, linkMark(definition))
|
||||||
return definition.destination === '' ? [...nodes] : [{ marks: [linkMark(definition)], text: definition.destination, type: 'text' }, ...nodes]
|
return definition.destination === '' ? [...nodes] : [{ marks: [linkMark(definition)], text: definition.destination, type: 'text' }, ...nodes]
|
||||||
|
|||||||
@@ -828,7 +828,7 @@ test('reads a hard break from a trailing backslash and from two trailing spaces
|
|||||||
assert.deepEqual(content(losslessMarkdownToAdf('> One\\\n> two.\n')), [quote({ content: [text('One'), hardBreak(), text('two.')], type: 'paragraph' })])
|
assert.deepEqual(content(losslessMarkdownToAdf('> One\\\n> two.\n')), [quote({ content: [text('One'), hardBreak(), text('two.')], type: 'paragraph' })])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('builds the hard break a mark spelling spans with no marks, in every reader', () => {
|
test('gives the hard break inside a mark spelling no marks, and reads a link of hard breaks as its destination, in every reader', () => {
|
||||||
const broken = (...marks: AdfMark[]): AdfNode[] => [{ content: [marked('a', ...marks), hardBreak(), marked('b', ...marks)], type: 'paragraph' }]
|
const broken = (...marks: AdfMark[]): AdfNode[] => [{ content: [marked('a', ...marks), hardBreak(), marked('b', ...marks)], type: 'paragraph' }]
|
||||||
for (const read of [commonMarkToAdf, losslessMarkdownToAdf, portableMarkdownToAdf]) {
|
for (const read of [commonMarkToAdf, losslessMarkdownToAdf, portableMarkdownToAdf]) {
|
||||||
assert.deepEqual(content(read('*a \nb*\n')), broken(em))
|
assert.deepEqual(content(read('*a \nb*\n')), broken(em))
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ three directories.
|
|||||||
|
|
||||||
`*!adf:mention[@a]{id=…}*` and `[!adf:status[Done]{…}](/u)` build a node holding `em` or `link`.
|
`*!adf:mention[@a]{id=…}*` and `[!adf:status[Done]{…}](/u)` build a node holding `em` or `link`.
|
||||||
`full.json` gives these five no `marks`, and `stage-0.json` gives them `annotation` alone, so a site
|
`full.json` gives these five no `marks`, and `stage-0.json` gives them `annotation` alone, so a site
|
||||||
may reject the document. `InlineNodeModel.marks` (`src/adf/inline-nodes.ts`) then names the mark
|
may reject the document. `InlineNodeModel.marks` (`src/adf/inline-nodes.ts`) afterwards names the mark
|
||||||
types a node takes, and the emitter carries a node holding another.
|
types a node takes, and the emitter carries a node holding another.
|
||||||
|
|
||||||
### 33. Emit a line in time linear in its mark runs, in `adfToLosslessMarkdown` and `adfToPortableMarkdown`.
|
### 33. Emit a line in time linear in its mark runs, in `adfToLosslessMarkdown` and `adfToPortableMarkdown`.
|
||||||
|
|||||||
Reference in New Issue
Block a user