mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
Answer stability round 1: 0 🔴, 3 🟡; pin the blank line's spaces in an item's code block, drop the timing-only tests, and file tab-indented lines as item 120
This commit is contained in:
+3
-2
@@ -828,8 +828,9 @@ changed, and a before-and-after figure in its PR; the gate times nothing. Measur
|
||||
`adfDocumentFault`'s shape and depth walks stay two — the parting gives depth its own code — at
|
||||
52 ms for a 9 MB document the emit takes 314 ms over. The 500-level guard runs after the block walk,
|
||||
so it bounds nothing in it: `matchBlankLine` continues a blank line through the open list items in
|
||||
one step, and an item reads a line's indentation only up to its own width, which took 20000 nested
|
||||
items and as many blank lines from 10.7 s to 39 ms.
|
||||
one step, which took 20000 nested items and as many blank lines from 10.7 s to 39 ms; and an item
|
||||
reads a line's indentation only up to its own width, though a tab an item's width splits still
|
||||
costs the whole line at each level (`todo.md` item 120).
|
||||
|
||||
## Only the hard break holds a raw newline
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ test("releases an indented code block's held blank lines as a walk rather than a
|
||||
assert.deepEqual(kinds(` a\n${'\n'.repeat(200000)} b\n`), ['code'])
|
||||
})
|
||||
|
||||
// A level is one list item. The gate times nothing; a quadratic walk outlasts Bun's 5 s test timeout.
|
||||
// A level is one list item.
|
||||
function deepestItem(markdown: string): { depth: number; kinds: string[] } {
|
||||
let blocks = parseBlocks(markdown, flavourClaims.lossless).blocks
|
||||
let depth = 0
|
||||
@@ -134,19 +134,21 @@ function deepestItem(markdown: string): { depth: number; kinds: string[] } {
|
||||
return { depth, kinds: blocks.map((block) => block.kind) }
|
||||
}
|
||||
|
||||
test('continues a blank line through a deep run of list items in time linear in the lines', () => {
|
||||
const depth = 20000
|
||||
test('continues a blank line and an indented line through list items nested past the 500 levels the parser carries', () => {
|
||||
const depth = 1000
|
||||
assert.deepEqual(deepestItem(`${'- '.repeat(depth)}a\n${'\n'.repeat(depth)}`), { depth, kinds: ['paragraph'] })
|
||||
assert.deepEqual(deepestItem(`${'- '.repeat(depth)}a\n${'\n'.repeat(depth)}${' '.repeat(depth)}b\n`), { depth, kinds: ['paragraph', 'paragraph'] })
|
||||
assert.deepEqual(deepestItem(`${'- '.repeat(depth)}a\n${' '.repeat(depth)}b\n`), { depth, kinds: ['paragraph'] })
|
||||
})
|
||||
|
||||
test('stops a blank line at the first blockquote and at an empty item', () => {
|
||||
test('stops a blank line at the first blockquote', () => {
|
||||
assert.deepEqual(deepestItem('- > a\n\n b\n'), { depth: 1, kinds: ['blockquote', 'paragraph'] })
|
||||
assert.deepEqual(deepestItem('- - \n\n a\n'), { depth: 2, kinds: [] })
|
||||
assert.deepEqual(deepestItem('- > - a\n\n > b\n'), { depth: 1, kinds: ['blockquote', 'blockquote'] })
|
||||
})
|
||||
|
||||
test('continues a line through a deep run of list items in time linear in the line', () => {
|
||||
const depth = 20000
|
||||
assert.deepEqual(deepestItem(`${'- '.repeat(depth)}a\n${`${' '.repeat(depth)}b\n`.repeat(3)}`), { depth, kinds: ['paragraph'] })
|
||||
test('stops a blank line at an empty item, and gives a code block in an item the blank line without its spaces', () => {
|
||||
assert.deepEqual(deepestItem('- - \n\n a\n'), { depth: 2, kinds: [] })
|
||||
assert.deepEqual(parseBlocks('- ```\n \n x\n ```\n', flavourClaims.lossless).blocks.map((block) => block.kind === 'bulletList' && block.items[0]?.[0]), [
|
||||
{ kind: 'code', language: '', position: { line: 1, offset: 0 }, text: '\nx' },
|
||||
])
|
||||
})
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
|
||||
`Bar = 0.1`
|
||||
|
||||
`Next ID = 120`
|
||||
`Next ID = 121`
|
||||
|
||||
## Items
|
||||
|
||||
| ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 120 | 0.3.0 | | defect | **Read a tab-indented line under nested list items in time linear in the line, where today a tab an item's width splits copies the whole line at each level.** | 2 | 1 | 2 | 3 | 3 | 9 by 2 | 6.0 |
|
||||
| 115 | 0.3.0 | | defect | **Build text holding no `border` mark in `losslessMarkdownToAdf`, and have `adfToLosslessMarkdown` carry text holding one.** | 2 | 1 | 2 | 3 | 3 | 4 by 3 | 4.5 |
|
||||
| 100 | 0.3.0 | | decision | **Put the reading of an image's title as its caption back to a reader panel, now that a writer panel reads a title as hover text.** | 1 | 2 | 2 | 3 | 3 | 6 by 4 | 4.0 |
|
||||
| 33 | 0.3.0 | | defect | **Emit a line in time linear in its mark runs, in `adfToLosslessMarkdown` and `adfToPortableMarkdown`.** | 3 | 1 | 2 | 4 | 5 | 9 by 3 | 3.6 |
|
||||
@@ -23,7 +24,7 @@
|
||||
| 102 | 0.3.0 | | decision | **Place a block a portable task list refuses at its path in the returned document without replaying `splitTaskList` on stand-in nodes.** | 2 | 1 | 2 | 3 | 4 | 10 by 4 | 2.0 |
|
||||
| 59 | 0.3.0 | | decision | **Group the directive grammar into `src/markdown/directive/`, move `Read<T>` to `result.ts`, and move `Flavour` and `flavourClaims` to `markdown/flavour.ts`.** | 3 | 1 | 2 | 4 | 5 | 10 by 5 | 1.5 |
|
||||
| 62 | 0.3.0 | | decision | **Move the portable flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/portable/`, so `emit/` no longer imports `portable/`.** | 3 | 1 | 2 | 4 | 5 | 10 by 5 | 1.5 |
|
||||
| 73 | 0.3.0 | 33, 59, 60, 62, 91, 92, 95, 100, 102, 109, 115 | | **Announce the package where someone needing an ADF converter already reads: JRACLOUD-77436, the Atlassian developer community and Stack Overflow's ADF-to-markdown questions.** | 1 | 2 | 2 | 3 | 2 | 3 by 8 | 32.0 |
|
||||
| 73 | 0.3.0 | 33, 59, 60, 62, 91, 92, 95, 100, 102, 109, 115, 120 | | **Announce the package where someone needing an ADF converter already reads: JRACLOUD-77436, the Atlassian developer community and Stack Overflow's ADF-to-markdown questions.** | 1 | 2 | 2 | 3 | 2 | 3 by 8 | 32.0 |
|
||||
| 81 | 0.4.0 | | | **Fail the gate unless the README's error tables list exactly the codes `ConvertErrorCode` holds.** | 1 | 1 | 1 | 1 | 2 | 3 by 2 | 8.0 |
|
||||
| 45 | 0.4.0 | | defect | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 1 | 3 | 4 | 3 | 1 by 2 | 6.0 |
|
||||
| 6 | 0.4.0 | | decision | **Specify the HTML dialect.** | 3 | 2 | 1 | 4 | 6 | | 0.0 |
|
||||
@@ -57,6 +58,17 @@
|
||||
|
||||
## Details
|
||||
|
||||
### 120. Read a tab-indented line under nested list items in time linear in the line, where today a tab an item's width splits copies the whole line at each level.
|
||||
|
||||
`removeColumns` in `src/markdown/parse/blocks.ts` returns `' '.repeat(n) + line.text.slice(index)`
|
||||
where an item's width splits a tab, a fresh string, so the next level's `slice` copies the whole
|
||||
line instead of sharing it. Measured on 2026-10-18 on 20 tab-indented lines under 20000, 40000 and
|
||||
80000 nested list items: 176, 460 and 1812 ms, where the same lines indented with spaces took 73, 90
|
||||
and 173 ms. Keep the split tab's leftover columns as a number on `Line` beside `column` and `text`,
|
||||
so a cut never builds a string. `docs/decisions.md` §Cost fixes are measured, never timed names
|
||||
this exception (cites item 120); delete that clause with the fix. Found while reviewing item 98's
|
||||
chunk, 2026-10-18.
|
||||
|
||||
### 115. Build text holding no `border` mark in `losslessMarkdownToAdf`, and have `adfToLosslessMarkdown` carry text holding one.
|
||||
|
||||
`!adf:border[x]{color="#091e42" size=2}` builds text holding `border`, which
|
||||
|
||||
Reference in New Issue
Block a user