Compare commits

...

3 Commits

Author SHA1 Message Date
lilleman 2f03e20549 10b - 34 files the flanking read beside an astral symbol
CI / gate (push) Successful in 34s
CI / publish (push) Successful in 7s
2026-09-25 21:10:23 +02:00
lilleman ae2dc6054b 10b - a == pair is bounded by the code point outside it, CommonMark's word character test 2026-09-25 21:10:05 +02:00
lilleman 7a53b6f7a0 10b - tests: a symbol outside the BMP bounds a == pair 2026-09-25 21:09:12 +02:00
3 changed files with 13 additions and 10 deletions
+1
View File
@@ -143,6 +143,7 @@ test('lifts a == pair to the editor default highlight, Yellow200 #f8e6a0 in @atl
assert.deepEqual(lifted('==**a**_b_ `c`==\n'), [paragraph(text('a', highlight, strong), text('b', highlight, em), text(' ', highlight), text('c', code))]) assert.deepEqual(lifted('==**a**_b_ `c`==\n'), [paragraph(text('a', highlight, strong), text('b', highlight, em), text(' ', highlight), text('c', code))])
assert.deepEqual(lifted('==`a`==\n'), [paragraph(text('a', code))]) assert.deepEqual(lifted('==`a`==\n'), [paragraph(text('a', code))])
assert.deepEqual(lifted('x==y==z ==a == b==, (==c==) _d_==e==\n'), [paragraph(text('x==y==z '), text('a == b', highlight), text(', ('), text('c', highlight), text(') '), text('d', em), text('e', highlight))]) assert.deepEqual(lifted('x==y==z ==a == b==, (==c==) _d_==e==\n'), [paragraph(text('x==y==z '), text('a == b', highlight), text(', ('), text('c', highlight), text(') '), text('d', em), text('e', highlight))])
assert.deepEqual(lifted('😀==b== ==c==😀 é==d==\n'), [paragraph(text('😀'), text('b', highlight), text(' '), text('c', highlight), text('😀 é==d=='))])
assert.deepEqual(lifted('# ==h==\n\n| ==c== |\n| --- |\n'), [ assert.deepEqual(lifted('# ==h==\n\n| ==c== |\n| --- |\n'), [
node('heading', { level: 1 }, text('h', highlight)), node('heading', { level: 1 }, text('h', highlight)),
bare('table', bare('tableRow', bare('tableHeader', paragraph(text('c', highlight))))), bare('table', bare('tableRow', bare('tableHeader', paragraph(text('c', highlight))))),
+6 -7
View File
@@ -1,5 +1,6 @@
import type { AdfDocument, AdfMark, AdfNode } from '../../adf/document.ts' import type { AdfDocument, AdfMark, AdfNode } from '../../adf/document.ts'
import { blockNodeModel } from '../../adf/block-nodes.ts' import { blockNodeModel } from '../../adf/block-nodes.ts'
import { isWordCharacter } from '../commonmark/emphasis-matching.ts'
import { highlightDelimiter, readAlertMarker, readTaskMarker } from '../plain-conventions.ts' import { highlightDelimiter, readAlertMarker, readTaskMarker } from '../plain-conventions.ts'
import { mergeAdjacentText, sameMarks } from '../../adf/editor-normal.ts' import { mergeAdjacentText, sameMarks } from '../../adf/editor-normal.ts'
import { nodeAttrs, nodeContent, nodeMarks } from '../../adf/document.ts' import { nodeAttrs, nodeContent, nodeMarks } from '../../adf/document.ts'
@@ -114,8 +115,11 @@ function delimiters(inline: readonly AdfNode[]): Delimiter[] {
const end = offset + highlightDelimiter.length const end = offset + highlightDelimiter.length
const before = offset > 0 ? text[offset - 1] : textOf(inline[index - 1])?.at(-1) const before = offset > 0 ? text[offset - 1] : textOf(inline[index - 1])?.at(-1)
const after = text[end] ?? textOf(inline[index + 1])?.[0] const after = text[end] ?? textOf(inline[index + 1])?.[0]
const closes = flanks(before) && bounds(text[end]) // Outside a delimiter a node's edge bounds it, standing where the source spelled a mark's punctuation.
found.push({ closes, holder: node, line, node: index, offset, opens: flanks(after) && bounds(text[offset - 1]), position: position + offset }) const outsideBefore = Array.from(text.slice(Math.max(0, offset - 2), offset)).at(-1) ?? ''
const outsideAfter = Array.from(text.slice(end, end + 2))[0] ?? ''
const closes = flanks(before) && !isWordCharacter(outsideAfter)
found.push({ closes, holder: node, line, node: index, offset, opens: flanks(after) && !isWordCharacter(outsideBefore), position: position + offset })
} }
position += text.length position += text.length
} }
@@ -126,11 +130,6 @@ function flanks(character: string | undefined): boolean {
return character !== undefined && !/\s/.test(character) return character !== undefined && !/\s/.test(character)
} }
// Outside a delimiter, a node's edge stands where the source spelled a mark's punctuation.
function bounds(character: string | undefined): boolean {
return character === undefined || /[\s\p{P}\p{S}]/u.test(character)
}
// Each opener takes the next closer holding at least one character after it, both in one line and under the same marks. // Each opener takes the next closer holding at least one character after it, both in one line and under the same marks.
function pairedDelimiters(inline: readonly AdfNode[]): Delimiter[] { function pairedDelimiters(inline: readonly AdfNode[]): Delimiter[] {
const found = delimiters(inline) const found = delimiters(inline)
+6 -3
View File
@@ -22,7 +22,7 @@ Start a session with: `Read AGENTS.md and todo.md, then do what todo.md's "Next
## Milestones ## Milestones
Shipping order: 3h, 3i, 3j, 5a, 5b, 5c, 5d, 5 → `0.1.0` (shipped 2026-09-05); 3k, 11, 4, 12, 13, 4b, Shipping order: 3h, 3i, 3j, 5a, 5b, 5c, 5d, 5 → `0.1.0` (shipped 2026-09-05); 3k, 11, 4, 12, 13, 4b,
4c, 14, 15, 16, 18, 4d, 28, 17, 29, 19, 20, 21, 22, 32, 23, 24, 25, 30, 26, 27, 10, 6, 7, 31, 33, 5f, 5g → 4c, 14, 15, 16, 18, 4d, 28, 17, 29, 19, 20, 21, 22, 32, 23, 24, 25, 30, 26, 27, 10, 6, 7, 31, 33, 34, 5f, 5g →
`0.2.0`; `0.2.0`;
8, 9 → TBD; 5e last. 8, 9 → TBD; 5e last.
The numbering is the order the work was planned in, not the order it ships. Everything known and The numbering is the order the work was planned in, not the order it ships. Everything known and
@@ -43,8 +43,7 @@ HTML doubles the importers and the file count they touch, and 25 to 27 because t
panel says the next reader pays for. panel says the next reader pays for.
29 and 30 come from 17's prose pass (2026-09-20). 29 reads first because every goal is what a later 29 and 30 come from 17's prose pass (2026-09-20). 29 reads first because every goal is what a later
ask is settled against, 19's included; 30 sits beside 25, the other chunk rereading AGENTS.md. ask is settled against, 19's included; 30 sits beside 25, the other chunk rereading AGENTS.md.
33 comes from 10a (2026-09-25) and reads beside 31, the other chunk about what the pipeline 33 comes from 10a and 34 from 10b (2026-09-25); both read beside 31.
measures.
31 comes from 20's gate runs (2026-09-21) and reads beside 5f, the other chunk putting a measured 31 comes from 20's gate runs (2026-09-21) and reads beside 5f, the other chunk putting a measured
number under the pipeline. 32 comes from 21's review (2026-09-21) and reads beside 22, the other number under the pipeline. 32 comes from 21's review (2026-09-21) and reads beside 22, the other
chunk clearing a §11 seam. chunk clearing a §11 seam.
@@ -62,6 +61,10 @@ chunk clearing a §11 seam.
on one paragraph of 2000 × `un` plus `**-r**`: each run its flanking cannot spell re-emits the on one paragraph of 2000 × `un` plus `**-r**`: each run its flanking cannot spell re-emits the
whole line before riding the carry, quadratic in the runs (§11 Bounds), and the plain whole line before riding the carry, quadratic in the runs (§11 Bounds), and the plain
reduction's `spellableLine` drops one mark per re-emit the same way. Make both linear. reduction's `spellableLine` drops one mark per re-emit the same way. Make both linear.
- [ ] **34 — Emphasis flanking reads a whole character (`0.2.0`).** Check whether
`line-escaping.ts`'s `charAt` and the parser's flanking read one UTF-16 unit beside an astral
symbol — a lone surrogate is neither punctuation nor symbol, where CommonMark reads `😀` as
punctuation — and, where they do, read the code point, with a fixture per direction.
- [x] **24 — The conformance gates have a directory (`0.2.0`).** - [x] **24 — The conformance gates have a directory (`0.2.0`).**
- [x] **25 — AGENTS.md §8 and §11 are findable (`0.2.0`).** - [x] **25 — AGENTS.md §8 and §11 are findable (`0.2.0`).**
- [x] **26 — The two mutable structures say what they guarantee (`0.2.0`).** - [x] **26 — The two mutable structures say what they guarantee (`0.2.0`).**