4.1: a text node carrying attributes never merges
This commit is contained in:
@@ -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.
|
`corpus/commonmark-spec/exceptions.json` names those.
|
||||||
|
|
||||||
"Equals" is structural equality over editor-normal ADF — adjacent text nodes with identical marks
|
"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
|
and no attributes merged, JSON number semantics, an empty attrs object, marks array or content
|
||||||
key — the only domain markdown can restore.
|
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.
|
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
|
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
|
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
|
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 —
|
parsing to anything but one unmarked text node — adjacent text nodes with identical marks and no
|
||||||
is a named error, and so is a `text` key in `{attrs}`. An enclosing mark spelling does not reach
|
attributes merged first — is a named error, and so is a `text` key in `{attrs}`. An enclosing mark
|
||||||
into the slot. The rest take no content, `:text` included; content on a node that takes none is a
|
spelling does not reach into the slot. The rest take no content, `:text` included; content on a
|
||||||
named error.
|
node that takes none is a named error.
|
||||||
|
|
||||||
- `date` — Attributes: `localId` (string), `timestamp` (string, epoch milliseconds).
|
- `date` — Attributes: `localId` (string), `timestamp` (string, epoch milliseconds).
|
||||||
- `emoji` — Attributes: `id` (string), `localId` (string), `shortName` (string, `:name:`), `text`
|
- `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
|
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
|
`: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
|
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
|
the rest plain text; `markdownToAdf` merges adjacent text nodes carrying identical marks and no
|
||||||
(AGENTS.md §2). Input reads that spelling alone: the value is one run of spaces and tabs, or one
|
attributes (AGENTS.md §2). Input reads that spelling alone: the value is one run of spaces and
|
||||||
run of newlines, and anything else — a mixed run, or text CommonMark carries plainly — is a named
|
tabs, or one run of newlines, and anything else — a mixed run, or text CommonMark carries plainly —
|
||||||
error.
|
is a named error.
|
||||||
|
|
||||||
```
|
```
|
||||||
:text{text=" "}Two leading spaces held, and one text node split:text{text="\n"}over two lines.
|
: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)
|
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[] {
|
export function mergeAdjacentText(nodes: readonly AdfNode[]): AdfNode[] {
|
||||||
const merged: AdfNode[] = []
|
const merged: AdfNode[] = []
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
const previous = merged[merged.length - 1]
|
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 ?? ''}` }
|
merged[merged.length - 1] = { ...previous, text: `${previous.text ?? ''}${node.text ?? ''}` }
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,10 @@ export function mergeAdjacentText(nodes: readonly AdfNode[]): AdfNode[] {
|
|||||||
return merged
|
return merged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mergesText(node: AdfNode): boolean {
|
||||||
|
return node.type === 'text' && Object.keys(node.attrs ?? {}).length === 0
|
||||||
|
}
|
||||||
|
|
||||||
function sameMarks(previous: AdfNode, node: AdfNode): boolean {
|
function sameMarks(previous: AdfNode, node: AdfNode): boolean {
|
||||||
return marksKey(previous.marks ?? []) === marksKey(node.marks ?? [])
|
return marksKey(previous.marks ?? []) === marksKey(node.marks ?? [])
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user