4.1: editor-normal and the node accessors #69
@@ -23,8 +23,8 @@ backticks read back as a fence — so a parse succeeding does not imply a spella
|
||||
`corpus/commonmark-spec/exceptions.json` names those.
|
||||
|
||||
"Equals" is structural equality over editor-normal ADF — adjacent text nodes with identical marks
|
||||
merged, JSON number semantics, an empty attrs object, marks array or content array the absent
|
||||
key — the only domain markdown can restore.
|
||||
and no attributes merged, JSON number semantics, an empty attrs object, marks array or content
|
||||
array the absent key — the only domain markdown can restore.
|
||||
|
||||
Round-trip equality is a property tested over a corpus, not a claim made in prose.
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68cf-78af-abd6-c660ec0d189b"
|
||||
},
|
||||
"text": "Owner",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " signs off.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "Signed by ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68d2-787e-afc5-2459776aa029"
|
||||
},
|
||||
"text": "the owner",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68d5-7372-9962-29a039654056"
|
||||
},
|
||||
"text": "One ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68d5-7372-9962-29a039654056"
|
||||
},
|
||||
"text": "anchor",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68cf-78af-abd6-c660ec0d189b"
|
||||
},
|
||||
"text": "Two ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68d9-73fa-9b9a-caa42061993c"
|
||||
},
|
||||
"text": "anchors",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
:adf{json="{\"attrs\":{\"localId\":\"01a0a067-68cf-78af-abd6-c660ec0d189b\"},\"text\":\"Owner\",\"type\":\"text\"}"} signs off.
|
||||
|
||||
Signed by :adf{json="{\"attrs\":{\"localId\":\"01a0a067-68d2-787e-afc5-2459776aa029\"},\"text\":\"the owner\",\"type\":\"text\"}"}
|
||||
|
||||
:adf{json="{\"attrs\":{\"localId\":\"01a0a067-68d5-7372-9962-29a039654056\"},\"text\":\"One \",\"type\":\"text\"}"}:adf{json="{\"attrs\":{\"localId\":\"01a0a067-68d5-7372-9962-29a039654056\"},\"text\":\"anchor\",\"type\":\"text\"}"}
|
||||
|
||||
:adf{json="{\"attrs\":{\"localId\":\"01a0a067-68cf-78af-abd6-c660ec0d189b\"},\"text\":\"Two \",\"type\":\"text\"}"}:adf{json="{\"attrs\":{\"localId\":\"01a0a067-68d9-73fa-9b9a-caa42061993c\"},\"text\":\"anchors\",\"type\":\"text\"}"}
|
||||
+8
-8
@@ -404,10 +404,10 @@ Right.
|
||||
Attributes and the carry fallback read as in the block sections, the carry in its inline form. Of
|
||||
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 unmarked text node — adjacent identical-mark text nodes merged first —
|
||||
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, `:text` included; content on a node that takes none is a
|
||||
named error.
|
||||
parsing to anything but one unmarked text node — adjacent text nodes with identical marks and no
|
||||
attributes merged first — 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, `: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`
|
||||
@@ -434,10 +434,10 @@ CommonMark strips or refuses one — a block's inline content edges, either side
|
||||
an em, strong or strike spelling's inner edges, a pipe cell's edges — is spelled
|
||||
`:text{text="…"}`, the reserved key carrying the node's text, escaped by the attribute grammar
|
||||
and never literal: pipe cells trim and pad. The emitter wraps the whitespace run alone and leaves
|
||||
the rest plain text; `markdownToAdf` merges adjacent text nodes carrying identical marks
|
||||
(AGENTS.md §2). Input reads that spelling alone: the value is one run of spaces and tabs, or one
|
||||
run of newlines, and anything else — a mixed run, or text CommonMark carries plainly — is a named
|
||||
error.
|
||||
the rest plain text; `markdownToAdf` merges adjacent text nodes carrying identical marks and no
|
||||
attributes (AGENTS.md §2). Input reads that spelling alone: the value is one run of spaces and
|
||||
tabs, or one run of newlines, and anything else — a mixed run, or text CommonMark carries plainly —
|
||||
is a named error.
|
||||
|
||||
```
|
||||
:text{text=" "}Two leading spaces held, and one text node split:text{text="\n"}over two lines.
|
||||
|
||||
@@ -5,12 +5,12 @@ export function sameMark(candidate: AdfMark, mark: AdfMark): boolean {
|
||||
return markKey(candidate) === markKey(mark)
|
||||
}
|
||||
|
||||
// AGENTS.md §2: adjacent text nodes carrying identical marks are one node.
|
||||
// AGENTS.md §2: adjacent text nodes carrying identical marks and no attributes are one node.
|
||||
export function mergeAdjacentText(nodes: readonly AdfNode[]): AdfNode[] {
|
||||
const merged: AdfNode[] = []
|
||||
for (const node of nodes) {
|
||||
const previous = merged[merged.length - 1]
|
||||
if (previous !== undefined && previous.type === 'text' && node.type === 'text' && sameMarks(previous, node)) {
|
||||
if (previous !== undefined && mergesText(previous) && mergesText(node) && sameMarks(previous, node)) {
|
||||
merged[merged.length - 1] = { ...previous, text: `${previous.text ?? ''}${node.text ?? ''}` }
|
||||
continue
|
||||
}
|
||||
@@ -19,6 +19,10 @@ export function mergeAdjacentText(nodes: readonly AdfNode[]): AdfNode[] {
|
||||
return merged
|
||||
}
|
||||
|
||||
function mergesText(node: AdfNode): boolean {
|
||||
return node.type === 'text' && Object.keys(node.attrs ?? {}).length === 0
|
||||
}
|
||||
|
||||
function sameMarks(previous: AdfNode, node: AdfNode): boolean {
|
||||
return marksKey(previous.marks ?? []) === marksKey(node.marks ?? [])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user