mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
Pin each rule of the emphasis sweep with a hand-worked line
This commit is contained in:
@@ -805,3 +805,16 @@ Read most (opens and searches):
|
||||
- `AGENTS.md` 1
|
||||
- `ci.sh` 1
|
||||
- `docs/decisions.md` 1
|
||||
|
||||
## Reading cost, chunk a65075e633b3, finished 2026-11-09T00:57:29Z
|
||||
|
||||
67156 new input tokens before the first commit.
|
||||
|
||||
Read most (opens and searches):
|
||||
|
||||
- `src/markdown/inline-content.ts` 9
|
||||
- `todo.md` 3
|
||||
- `README.md` 2
|
||||
- `CHANGELOG.md` 1
|
||||
- `docs/decisions.md` 1
|
||||
- `src/markdown/emit/line-escaping.ts` 1
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { NodeRange } from './line-escaping.ts'
|
||||
import { isAdfMark, type AdfMark, type AdfNode } from '../../adf/document.ts'
|
||||
import { linePass, withheldMarkFallbacks } from './inline-line.ts'
|
||||
import { portableJudgement, portableLeaves } from '../portable/inline-reduction.ts'
|
||||
|
||||
// Each line is worked by hand from docs/decisions.md §With the first misread emphasis run, `unspellableRuns` returns only runs that, taken back early, leave the line as the one-run-per-pass reference writes it.
|
||||
// A run reads `mark@first-last`, nodes named by index. `carry` is the lossless writer's first read-back pass, `drop` the portable writer's.
|
||||
const lines: { carry?: string[]; drop?: string[]; name: string; nodes: AdfNode[] }[] = [
|
||||
{
|
||||
carry: ['em@0-0', 'strong@0-0'],
|
||||
name: 'a strong whose em cannot close, with the em',
|
||||
nodes: [marked(['em', 'strong', 'strike'], ')'), plain('a')],
|
||||
},
|
||||
{
|
||||
carry: ['strike@1-1'],
|
||||
drop: ['em@1-1', 'strike@1-1'],
|
||||
name: 'the strike that cannot open',
|
||||
nodes: [marked(['strong'], 'r'), marked(['strong', 'strike', 'em'], '_'), plain('a')],
|
||||
},
|
||||
{
|
||||
carry: ['strike@0-0'],
|
||||
drop: ['strike@0-0', 'strong@0-0'],
|
||||
name: 'the strike that cannot close',
|
||||
nodes: [marked(['strike', 'em', 'strong'], '_'), plain('0')],
|
||||
},
|
||||
{
|
||||
carry: ['strike@1-1', 'strike@3-3'],
|
||||
drop: ['strike@1-1', 'strike@3-3'],
|
||||
name: 'two strikes that cannot open, not the strong over them',
|
||||
nodes: [plain('b'), marked(['strike', 'strong'], '_'), marked(['strong'], 'r'), marked(['strong', 'strike', 'em'], '(')],
|
||||
},
|
||||
{
|
||||
carry: ['strong@0-0'],
|
||||
drop: ['strike@0-1', 'strong@0-0'],
|
||||
name: 'the strong that cannot close, and under a drop the strike',
|
||||
nodes: [marked(['strike', 'em', 'strong'], '_'), marked(['strike', 'em'], 'b)'), plain('0')],
|
||||
},
|
||||
{
|
||||
carry: ['strong@0-0'],
|
||||
drop: ['em@2-3', 'strong@0-0', 'strong@2-3'],
|
||||
name: 'the strong that cannot close, the em holding a strike staying under a carry',
|
||||
nodes: [marked(['strong'], '*'), plain('0'), marked(['em', 'strong', 'strike'], '['), marked(['em', 'strong'], '(')],
|
||||
},
|
||||
{
|
||||
carry: ['strike@1-1'],
|
||||
drop: ['em@2-2', 'strike@1-1', 'strong@2-2'],
|
||||
name: 'the strike that cannot open, and under a drop the strong and em after it',
|
||||
nodes: [plain('b'), marked(['strike'], ')'), marked(['em', 'strong'], '_'), plain('n')],
|
||||
},
|
||||
{
|
||||
drop: ['strong@2-2'],
|
||||
name: 'a strong that cannot open, the em beside a strike that merges staying',
|
||||
nodes: [marked(['strong', 'strike'], 'a'), marked(['strike'], 'a'), marked(['strike', 'strong', 'em'], '_')],
|
||||
},
|
||||
{
|
||||
drop: ['strong@1-2'],
|
||||
name: 'a strong that cannot open, the em beside it staying as its end can merge',
|
||||
nodes: [plain('a'), marked(['strong', 'em'], '[ '), marked(['strong'], 'a'), marked(['em', 'strong'], 'a')],
|
||||
},
|
||||
{
|
||||
carry: ['strong@0-0', 'strong@2-2'],
|
||||
name: 'a strong whose opener follows a word beside an em written `_`',
|
||||
nodes: [marked(['strong'], '_'), plain('0'), marked(['strong', 'em'], 'a)')],
|
||||
},
|
||||
{
|
||||
carry: ['em@1-1', 'strong@1-1'],
|
||||
name: 'a strong and em that cannot close inside a strike that reads',
|
||||
nodes: [marked(['strong'], '!'), marked(['strike', 'em', 'strong'], '['), marked(['strike'], '0')],
|
||||
},
|
||||
{
|
||||
carry: ['em@2-2'],
|
||||
name: 'the em that cannot open, not the strong over more nodes holding it',
|
||||
nodes: [plain('b'), marked(['strong'], '(a'), marked(['strong', 'em'], ')'), plain('c')],
|
||||
},
|
||||
]
|
||||
|
||||
for (const { carry, drop, name, nodes } of lines) {
|
||||
test(`unspellableRuns returns ${name}`, () => {
|
||||
for (const grouping of ['spanned', 'parted'] as const) {
|
||||
if (carry !== undefined) {
|
||||
const pass = linePass(nodes, 'paragraph', [], withheldMarkFallbacks(nodes, 'lossless'), grouping)
|
||||
assert.ok(pass.ok)
|
||||
assert.ok(pass.value.fallback !== undefined && pass.value.fallback !== 'opening-link')
|
||||
assert.deepEqual(named(pass.value.fallback), carry)
|
||||
}
|
||||
if (drop !== undefined) {
|
||||
const leaves = portableLeaves(nodes, 'paragraph', { depth: 0, path: [], sources: new Map() })
|
||||
assert.ok(leaves.ok)
|
||||
const judged = portableJudgement(leaves.value, grouping, 'paragraph', [])
|
||||
assert.ok(judged.ok)
|
||||
assert.ok(judged.value.kind === 'fallback' && judged.value.fallback.kind === 'unspellable-run')
|
||||
assert.deepEqual(named(judged.value.fallback.runs), drop)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function marked(types: readonly string[], text: string): AdfNode {
|
||||
const marks: AdfMark[] = types.map((type) => ({ type }))
|
||||
return { marks, text, type: 'text' }
|
||||
}
|
||||
|
||||
function named(runs: readonly NodeRange[]): string[] {
|
||||
return runs.map((run) => `${'mark' in run && isAdfMark(run.mark) ? run.mark.type : 'unmarked'}@${run.first}-${run.last}`).sort()
|
||||
}
|
||||
|
||||
function plain(text: string): AdfNode {
|
||||
return { text, type: 'text' }
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
| ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 153 | 0.4.0 | | question | **Decide whether `src/markdown/` root may import `emit/`, `parse/` or `portable/`, and hold the answer in `.oxlintrc.json` and §The source parts by ADF and format.** | 1 | 2 | 2 | 3 | 2 | code health by 3 | 26.2 |
|
||||
| 162 | 0.4.0 | | | **Pin each rule of the emphasis sweep with a hand-worked line, so every mutant that lets a pair fall where the one-run-per-pass reference keeps it fails a test.** | 2 | 1 | 1 | 2 | 4 | code health by 4 | 17.4 |
|
||||
| 175 | 0.4.0 | | | **Delete the guards in `tryTaskList` that `reduceToPortable` makes unreachable, once a test shows `adfToPortableMarkdown` reaches none of them.** | 1 | 1 | 1 | 1 | 2 | code health by 2 | 17.4 |
|
||||
| 45 | 0.4.0 | | defect | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 1 | 3 | 4 | 3 | 1 by 2 | 13.1 |
|
||||
| 127 | 0.4.0 | | principle | **Hold what `entity-references.test.ts`, `json-text.test.ts` and `result.test.ts` check by asserting outcomes, so none reads source text.** | 2 | 2 | 1 | 3 | 3 | code health by 2 | 13.1 |
|
||||
@@ -72,20 +71,6 @@ a paragraph opening with a spellable `!adf:link` (§Nothing recurses unbounded).
|
||||
`src/markdown/**` bans only `html/`; banning `emit/`, `parse/` and `portable/` for the root's own
|
||||
files, with its `ci.sh` probe, extends the decision.
|
||||
|
||||
### 162. Pin each rule of the emphasis sweep with a hand-worked line, so every mutant that lets a pair fall where the one-run-per-pass reference keeps it fails a test.
|
||||
|
||||
About 20 mutants of `src/markdown/emit/unspellable-runs.ts` survive the gate. Each lets a pair fall
|
||||
with the first where the one-run-per-pass reference keeps it, so the line comes out other than the
|
||||
reference writes it, against `docs/decisions.md` §With the first misread emphasis run,
|
||||
`unspellableRuns` returns only runs that, taken back early, leave the line as the one-run-per-pass
|
||||
reference writes it. They sit in `endsAlone`'s mark check, `holdsAlike`'s `held >= 0`,
|
||||
`mayBeWrittenWith`, the `'moves'` give-ups in `runEdge`, `wordBeside` and `pairStanding`,
|
||||
`pairSpellings`' carry-split rule and `holdsPairBefore`'s `<=`. The properties beside
|
||||
`inline-line.ts` and `inline-reduction.ts` miss them; the `runEdge` one survived 20000 runs on a
|
||||
random seed. Write one line per rule of that entry, its expected markdown worked from the entry,
|
||||
never from the code, then run Stryker on the file. A mutant that only takes fewer runs back costs a
|
||||
pass and may survive.
|
||||
|
||||
### 175. Delete the guards in `tryTaskList` that `reduceToPortable` makes unreachable, once a test shows `adfToPortableMarkdown` reaches none of them.
|
||||
|
||||
`reduceToPortable` runs first and turns a task list opening with a non-task into a bullet list. So
|
||||
|
||||
Reference in New Issue
Block a user