Repository navigation
Conversation
Both bindings call a background light when (299 R + 587 G + 114 B) / 1000 is above 128. Core divides in floating point; Rust divided integers, which floors, so 111,537 colors such as #818080 were light in Core and dark in Rust. Rust now compares the weighted sum with 128000, which is Core's result exactly. The rows live in core/src/theme-mode.conformance.json, which Core's RendererThemeMode test and Rust's theme test both run, so the two rules cannot drift apart again. serde_json is a new dev-dependency to read it. Source +1: the doc comment states the rule. Issue: 541
Core's Diff dropped a blank line inside a hunk whose leading space a tool had stripped, and numbered the lines after it one too low. It now draws it as the context line it is, in both views. Rust's Diff took a second file whose ---/+++ headers use a tab for one more part of the first file and drew its hunks too; it now stops there, as Core does. The rows live in core/src/renderables/diff.conformance.json, which Core's Diff test renders through DiffRenderable and Rust's diff test runs through its parser. Lines after a hunk's counts are ignored, as git apply and GNU patch ignore them; Rust already did. Core shows a parse error for them, including git format-patch's signature; those rows run as test.todo in Core until that is fixed. Issue: 543
The Rust Markdown parser trimmed and split block lines on any Unicode whitespace, so a line such as "- - -" drew a horizontal rule, and an NBSP before "#", "-", ">" or a fence started a heading, list, quote or code block. Model replies and pasted text often carry NBSPs. CommonMark, and Core through marked, use only spaces and tabs for block structure and treat other spaces as text. The parser now does the same. It also lets a thematic break end a list, as CommonMark example 60 asks. The block rows live in core/src/renderables/markdown-blocks.conformance.json: CommonMark 0.31.2's thematic break, setext heading and fenced code examples, the record's rows and the rule rows Rust checked by hand. Core runs them through marked, Rust through its parser. Source +4 lines: the SPACE constant and the list loop's rule check. Issue: 540
Core counts an Input's maxLength in UTF-16 code units, as its docs and
HTML's maxlength do, but cut the value with substring, so an emoji across
the limit left half a surrogate pair, stored as U+FFFD ("hell�").
The value setter also cut before it stripped newlines. Rust's Input
counted Unicode scalar values instead, so "😀😀" with a limit of 2 stayed
whole in Rust and was cut to "😀" in Core.
Both Inputs now count UTF-16 code units and leave out a character that
does not fit whole; Core retreats only when a pair straddles the cut.
Rust's Textarea keeps counting characters, as it documents: Core's
Textarea has no maxLength to share.
The rows live in core/src/renderables/input-max-length.conformance.json,
which Core's Input test and Rust's widget test both run.
Issue: 529
With the rule fix, "* * *" became a paragraph, but Rust's inline parser still read "* *" as emphasis: it checked only an ASCII space next to a delimiter, and strong text checked none. The widget drew " *" where CommonMark keeps all three stars. Emphasis and strong text now never border any whitespace (CommonMark 6.2). The block corpus gains a text field: the concealed frame both widgets draw, which Core's Markdown test and Rust's tests/markdown.rs compare. Core conceals such emphasis too, through tree-sitter's Markdown inline grammar; those rows name that Core defect and run as test.todo in Core. Three rows pin the space-and-tab trims of fence info strings, table cells and quote lines. The CommonMark examples now carry their attribution and license notice. Issue: 540
The Rust tests read Core's *.conformance.json files, but a pull request that changed only those files selected rust=false, so a new or edited row reached main without the Rust half of its check.
Core's marked path and the Rust widget each had their own Markdown tests, and the shared block file held 123 rows with block names only. The native parser needs one corpus that says what each document renders to. markdown.conformance.json replaces it: 781 rows of one shape (name, chunks, HTML, failing runners, frames), holding every CommonMark 0.31.2 example, the GFM extension examples, the earlier parity rows, the Rust frame fixtures and the parser design's measurement fixtures, streamed reflow and OpenCode cases, and math vectors. Core checks marked's HTML and its streaming parse against each row; Rust checks its block tree and its streamed blocks; both check frames. A row lists the runners that disagree with the spec, and a runner fails when a row stops matching its listing either way: marked misses 72 rows' HTML and 53 streams, Rust's block tree 142 rows. No source changes. The new checks take about 0.2 s in Core and 0.3 s in Rust's debug tests. Issue: 540 Issue: 561 Issue: 567
The first corpus rewrote the spec's HTML before storing it, which deleted visible text (a leading NBSP, a tab, the slash of a raw HTML tag). It took its GFM examples from cmark-gfm's test file instead of the published spec, and missed single-tilde strikethrough and protocol autolinks. A failing check waived every other check of its row, so a wrong last frame or a dropped fence stayed green, and Rust compared every table as one tag. Each row now stores the HTML as its spec publishes it, and runners compare it after the part of the specs' own normalizer that marked's spellings need. The GFM rows are the 27 published extension examples. Spec 602 and 606 keep CommonMark's answer, since a GFM autolink cannot follow `<`. Expected failures name one check each (marked's HTML, its block tags and its stream, Rust's blocks and stream, and each frame), Rust's block check includes table rows and cell text, and Core takes the first paint after a streamed chunk. A new row checks a held line once the stream ends, and the chunked math vectors have frames. No source changes.
CommonMark reads Unicode punctuation and whitespace for emphasis flanking, full case folding for link labels, and the HTML5 named character references. The Markdown engine needs them; nothing in the native library has them. A Python script generates the file from its Unicode database (16.0.0 here) and the HTML5 entity list. Source addition: generated data the engine reads (new subsystem, M1).
Core and Rust each parse Markdown their own way and disagree with the spec on 225 corpus checks. The native engine (step M1 of the native Markdown parser design) is meant to replace both, so it has to be right first. It parses complete lines only, keeps the source and a preorder block table, and derives a leaf's text again from the source when a reader needs it. This adds the block parser (CommonMark's line algorithm, GFM tables and task items, display math), the inline parser (emphasis, links, definitions found later, GFM strikethrough and autolinks, inline math), the hook-filter latch, and a test-only HTML emitter. Nothing in the bindings reaches it. The shared corpus gains two engine checks: every one of its 786 rows renders the spec's HTML and streams like a fresh parse. Source addition: a new subsystem that M2 and M3 bind (design, Steps).
A streamed paragraph was parsed whole when it closed, so the engine had no display to write while it grew and no bound on the work per append. The inline parser now takes the leaf's content as it grows: a construct that needs bytes that have not arrived stops at its start and runs again on the next append, long searches resume where they stopped, and closers resolve as they arrive unless a bracket may still become a link. Whole parses run the same code with the content final. The open paragraph keeps this state, also for a pending line that can only be paragraph text, and counts the display bytes a tail write would carry. read_leaf returns a leaf's display, styled chunks, span flags and URL pool. A shared seeded model harness with a shrinker drives the engine through appends, replaces, finish and change reads against fresh parses; a test sink replays the tail writes and checks them against a whole parse of each leaf. Source addition: the live leaf and read_leaf (design: open-leaf token tail, What the engine writes).
A container could open past the open stack's 32 entries when a list or table needed more than one level. Each container now opens only with room for what it needs below it; deeper markers stay text in the innermost leaf, as cmark bounds them. The live leaf's tail write now starts at the first display byte that differs, so a word that turns from pending bytes into plain text rewrites nothing. Tests hold every store to its limit with nothing published on a rejection, and the work rows to linear bounds: about 2x the document scanned and 1x written, 3x scanned for a stray backtick; no allocation per append beyond capacity growth. Source addition: the depth rule and the exact tail start (design invariants 4 and 5).
Each new list item looked for the previous one by walking the whole list, so a growing list cost quadratic time: 5.3M instructions to parse the 33 KB list fixture whole. The block whose last child is wanted is always the open one at the end of the table, so walking back from the end reads only that child's subtree: 2.3M whole, 3.5M streamed in 30-byte appends.
Mutations of the engine survived three tests: the token limit moved by one token, a table split that kept the paragraph's live state, and a removal reported for a block no read listed. The limit row now checks both sides of the boundary, the model rejects a reported removal of an unread block, and a stream table covers a paragraph that becomes a table, a setext heading or definitions while it streams. UNRESOLVED is checked against a parse with every definition. The fold's whitespace check could not fail (the limit is a word start); it is an assertion now.
With the hook on and no filter, the latch walked the blocks added since its last pass and read past the table when a paragraph that held only definitions went away. Every prose leaf is hooked anyway, so the flag is set when the leaf is created and cleared when a paragraph becomes a table. The model now swaps filters and runs with an empty one.
The user chose that an unresolved reference label does not hold a block's commit (D98). The model checks it as P4'; this row streams the study's shape (a paragraph with [1] and a[i], then more blocks) and checks the committed count after each append.
…read The engine held nearly every pending line: a binding had no block to show for "- tw" or "# A", and the live leaf's display depended on how the stream was split. B (D93 Q4) holds a pending line only while its kind can still change. The engine now lays the pending line out provisionally after the complete lines (undone at the next call), so blocks, read_leaf and the live leaf show it at once: unclosed openers drawn as closed, the untokenized tail drawn by renderPending, and only definition paragraphs, possible delimiter rows and a paragraph's last line from its first pipe held. A model check now asserts after every append that the live tail writes rebuild read_leaf, that frames never lose drawn text (P7, with B's stated exceptions), and corpus frames are checked as engine-text:N. The work counters were charged per line, not where bytes were read, and hid two quadratic loops. They are now separate counters (line, block, hook, inline, display, sent), each gated, and line scanning, label scans and B's holds resume where they stopped. Also: a rejected set_options changes nothing; the hook filter is copied into the doc; a zero-capacity read acknowledges nothing; a definition change re-lists leaves whose output changes; a pending table row latches every filter match; labels count code points.
The token limit made an opener literal once it was 4,096 tokens back, but the live leaf only dropped tokens when the delimiter stack was empty; openers that never close kept it from emptying, and a 7,206-byte paragraph kept 4,801 tokens (bigboi-2). At twice the limit the live leaf now drops the settled tokens before the first unresolved one, with their content, and parses the rest again (the first unresolved token is never more than the limit back, so each byte is parsed again at most twice). An expired opener stays drawn as it was until its leaf closes, so an expiry costs no tail write per append; the close writes the leaf again once. The content copy's capacity follows what it holds. A limits row checks length and capacity of every store after each append.
CommonMark 6.2 renders `*a***b**` as <em>a</em><strong>b</strong>: the middle run closes the emphasis and opens the strong span. The test HTML emitter and read_leaf treated a run as either a closer or an opener, so the HTML was malformed and read_leaf left `b` unstyled (bigboi-5). Both now end the run's closing spans, write its literal rest, then start its opening spans. The corpus gains a review source: bigboi's cmark-gfm differential cases with the spec's answer (cmark-gfm's for `*a***b**`; M1's where GFM 6.9 and CommonMark 6.2 support it against cmark-gfm), and the 999/1,000 code point label boundary, so the oracle no longer shares the emitter's blind spot. marked and Rust tags come from their runners.
When definitions left the start of the paragraph the live leaf followed, or arrived after a pending leaf was parsed without them, the closing leaf fell back to a whole parse without writing its display again: a bound node would keep the stale text, and the fallback skipped the tail sink check (opus-4 (a)). The close now writes the whole display and the sink checks it. Streamed rows cover that case and a pending table row whose cells both hold the filter (opus-4 (b)); a test pins that a late definition lists again the paragraph it changes. Cleanup: commit state lives only in the commit cursor (Flags.final removed); trailingWordStart and ExtendedKind.email were unused; table alignment is the engine's columnAlignment, which the test emitter calls; container matching for stored lines reuses continues.
…onst A definition that arrives re-inlines the leaves with an unresolved label and lists those that used it. The lookup recorded the use by writing the doc's watch fields through @constcast from the parser's const references (opus-6). The re-inline now parses with a small Watch reference context that notes the use itself; the doc keeps no watch state.
The model's host now keeps the read_leaf output (bytes and URLs) of each leaf it was told about and checks that a closed leaf never shows other output without being listed again; a new command replaces the source with its link targets swapped, so definitions change while their users' bytes stay (bigboi-3). It found that a math option change restyled leaves with unchanged bytes without listing them; set_options now lists them as a definition change does. The limits test pins the hook filter's exact 64-byte item and 256-byte pool, that the doc keeps its own copy of the caller's filter, and that a rejected set_options leaves blocks, finish state, options and pending changes as they were (bigboi-4, opus-5).
Mutations showed two blind spots. The display counter charged the pending-line renderer by where its scan state ended, so a hold that read its bytes again on every append still looked linear; the renderer now counts the bytes it reads, and a hold that resolves as text is gated at 4x (it draws its bytes once more). And P7 ran only in the model, so a backslash drawn before a line break that later turned into a hard break passed; streamed rows now check that no frame loses drawn text, at finish too, with a row for that case. Math's \( and \) count as B's exceptions instead of every backslash. The changes test pins that a replace changing only a definition's target lists the leaf that uses it, and the model replaces targets more often.
A paragraph that held only definitions is removed when a rule ends it; the live leaf must let it go then. Streamed rows now check after finish that the live leaf shows what read_leaf does, with that row (a mutation that kept the leaf passed every other test).
# Conflicts: # packages/core/src/renderables/__tests__/markdown-parser.test.ts # packages/core/src/renderables/markdown.conformance.json # packages/rust/crates/opentui/tests/markdown.rs # scripts/ci-changes.sh
…us frame The orchestrator's call on M1's question: a | line is held from its first pipe, and text drawn before it stays. The reflow table header row's first frame is now "a"; the engine meets it, so its engine-text:1 tag goes. Core and Rust still fail that frame, as listed.
When the pending line started a list item or a heading, the live leaf moved on and the paragraph it left never got its final display: a bound node kept `23 = 6` for `- 2*3 = 6`. The layout now marks such a paragraph as ending; the live parse finishes it then (its final feed and tail write, no second parse at close), and any prose leaf the live parse does not follow is written whole when it closes or a definition changes it. The test sink keeps one mirror per block and checks every node against read_leaf after each append and at finish. Producing the tail cost more than the bytes it sent: the tail was cut from a copy of the whole pending display, a held pipe redrew the line before it on every append, and every append laid the pending line out again (a long table row or heading cost its length per byte). Tail writes now slice tokens and the drawn bytes, comparisons read in place, the cut at a held pipe is kept while only later bytes arrive, a held line's decision is cached with the bytes that keep it, and an append that only grows a line grows its layout instead of redoing it. A new counter, assembled bytes, counts every copy, sink or not; bigboi's and opus's rows are gated at 2x for assembled and sent bytes. Also: a held pipe no longer stops token retirement (its cut is kept when its bytes go); a replace lists a leaf whose chunks pick other URL pool ranges; a paragraph's leading definitions are decided over its complete lines, so its prose shows one line after them; a code span's stripped leading space is not drawn; a pending row's cells latch filter matches; the late-definition row keeps another label unresolved so only the definition's use lists it.
A line of 8,192 dashes stays held to its end; its decision is cached with the bytes that keep it, gated like the other rows. Two traces the model found become stream rows: an escaped space in a pending table row trims, and a paragraph's own definition arrives as the paragraph the layout ended closes.
A definition's title may continue on the next line; the paragraph waits for it to close (a mutation that showed it as prose passed every other row).
A mutation showed the last-line wait was redundant: an attempt that may still succeed has an open label, destination or title, which definitionOpen already holds; one that failed on bytes present is decided. The rule goes.
The closer search counts on from the end of a run that touched the content's end. A search that ran out after plain bytes kept the run end of an earlier search, so a later resume skipped bytes and missed a closer (model seed 8,095 at 20,000 seeds); it now clears it. The trace is a stream row.
…roperty Three review rounds closed the reported cases and each next round found new instances of the same three classes: superlinear work, a store over its limit, and a pending line laid out otherwise than the same line once complete. The model now checks the classes after every call, on random streams and on streams of one construct repeated many times: every work counter within k times the bytes fed, k stated per counter; every store within its limit; and the pending line's layout against the exact parse of the completed line, B's four exceptions aside. On 4551a04 each class fails. The fixes. One scanner per kind serves both layouts: a table row's cells and an ATX heading's closing sequence are scanned from the line's start for a complete line and resumed for a pending one, and a line whose content starts a display math opener stays a candidate whichever block holds it, so a `\[x\]` after a table becomes math, not a cell. A provisional layout saves each block it changes once. A definition title is scanned on from where it stopped, and a possible definition builds no tokens. A held table header line keeps its tokens from the pipe on, and a line longer than 4,096 bytes is no header, so the hold is bounded (this also fixes a held line whose settled tokens were retired past its pipe, then released: a debug assertion). The comparison skips a grown token's known prefix; a hold's cut keeps its display while the tokens before it stand, draws on from the last cut, and is not written again while the same hold stands. HTML tags and held block markers are scanned on from where they stopped. A paragraph of only definitions that the pending line ends is hidden, as the completed line removes it. Table cells are no longer written to nodes (the design binds paragraphs and headings; a host reads cells with read_leaf). The node check after a call covers the nodes it wrote or listed, every node at finish.
Rows for each F4 fix a mutation showed the CI budget alone would miss: a retire that reaches a short held line, the content clamp after a 70,000-byte word, a pending cell and paragraph listed again after they grow, a replace that rebinds every node, an escaped space in a cell (one scanner now lays out complete and pending rows alike, so the stream rows cannot tell), and a definitions-only paragraph continued by a setext line.
The pending line's layout read every paragraph it ended for definitions, to hide one that holds only definitions; such a paragraph starts with [. list.md in 30-byte appends: 5.98M instructions to 5.48M.
…roperty A paragraph's own leading definitions are extracted when it closes, so the completed line can resolve a reference the pending layout showed with its brackets: B's late-definition exception (model seed 8,928 at 20,000 seeds).
Round 4 of both reviews. An inline link's quoted title was scanned again from its `]` on every append (2,072 bytes: 2.1M inline visits): definitions and inline links now share one resumable title scanner, and an inline tail keeps its destination and the title's resume point. The hook filter read its longest item's length again per call (a 64-byte filter: 63,520 visits over 1,024 one-byte appends): each item now keeps its KMP state across calls, and a match in the pending line latches the layout's leaf as it is found. The repeated constructs now include inline link titles and long filters, and fail first on the old engine. The finished parse stays GFM: a header line of any length starts a table (cmark-gfm makes 4,097 and 8,192 bytes tables too). Only B's hold is bounded: a line of more than 4,096 bytes shows as text, and if the next line makes it a header, that frame changes once. Rows: bigboi's two appends that reach the retire guard (the folded display passed what was drawn when the guard was off), f4-12's trace as a work row, and the filter and link title rows. CI runs budget B3 (families 8, model 128 and 32 seeds, no splits; about 6.4 s in a debug build); the splits and the wide sweeps are opt-in.
A parenthesized title cannot hold an unescaped ( (cmark-gfm leaves both shapes text); the mutant that dropped the rule from the shared scanner passed every other test.
D102: a display math line is never hidden while it streams. The pending line now shows as text, or as a table row, until its line ends, and becomes a one-line math block then: once, and never back. Before, it switched to math when the closer arrived, so `$$x$$ y` showed a math block and then text again, and a row switched to math took the row back. The math candidate path that made the provisional switch goes (noteMathCandidate, its fields and the one-line growth rule). A lazy line fed to a paragraph's live parse that the exact parse gives another block leaves the paragraph written whole: its node kept the formula line (`para\n$$E=mc^2$`) that read_leaf drops (opus-14, bigboi round 4). And a pending scan with no new bytes keeps its state: a code span's `&` was held as an entity once the scan forgot the span (bigboi round 4's heading `# <!A` + backtick + ` x &`). Property (c) now checks display math across calls: a math block may appear once on a line and never reverts to text; within a call, the completed line may switch to math. bigboi's prefix differential is a row family: documents of mixed fragments, streamed by 1 and 4 bytes, every prefix against a fresh doc of it and every written node against read_leaf (48 documents in CI, MARKDOWN_DIFFERENTIAL for more).
The steady-state test counted every allocator call against the engine's capacity growths. Where a growth cannot happen in place (macOS, Windows), ArrayList tries remap, which fails, then allocates: two calls for one growth, so the test failed there (6 calls for 3 growths) and passed on Linux. The engine does not allocate per append on any platform. The counter now counts the calls that obtain memory (an alloc, or a resize or remap that succeeded), and the test runs with an allocator that grows in place and with one that never does.
bigboi round 5: once an inline link's title closed, the tail dropped its saved scan, so while the spaces before ) arrived each feed read the title again from the ] (1,555 bytes: 661,783 inline visits). The tail now keeps the title's end and the white space read after it until ) or a byte that rules the link out. A work row (2,048-byte title, 1,024 spaces: 2.6M inline visits before) and the title family's trailing spaces (failed first, seed 506 at 3,000) pin it.
bigboi round 5 (D102, D93: math is plain until decided): a pending $$ or \[ followed only by spaces was held, so the frame showed nothing. The marker hold for math goes, and the provisional layout leaves every math block start to the complete line: the opener shows as text, or as a table row, and its line ending makes it math once. Rows: the opener alone and with spaces at top level, after a paragraph line and after a table row, by its two-byte prefix and at the line ending (failed first).
bigboi round 5: each filter item reads every new byte, so 16 items cost 16 times the bytes, and the stated bound left the item count out. The filter now has at most 16 items (set_options and init reject more, as the other limits), and the bound is stated per item: items x (bytes + the longest item less one per call). opus-16: with the overlap in the bound, F5's KMP state goes and the one substring scanner serves the latch, the pending line and the header range (markdown.zig -47). Rows: 16 one-byte items over 1,024 one-byte appends (16,432 visits; the old bound allowed 4,360), a 17-item filter rejected, and the families set 16 items.
…s' bytes The tear prototype found two assertion failures that the models missed because they ran with one option set and never generated the bytes the repros use. Its D1 row (math, `@\n$$_` then `$$\n`) passes here, as do B and D; D2 needs M2's conceal and hyperlink options, which M1 does not have, so rows E and F run with the options M1 has. The class: the command model now also runs with the hook off and math on from the start (set_options switches math and the filter mid-stream in every run), its soup holds the repros' pieces (`@`, `∓`, setext underlines after a link, `$$_`), and a depth-2 sweep runs every pair of 16 pieces, then finish, under five option sets (1.2 s). The shrinker keeps a candidate only if it fails the same property at a step of the same kind (the report: a property alone could drift to another bug). They found two defects. A filter with the hook off latched a table header's cells (latchRange did not check the hook). A pending empty ATX heading kept its content start before the spaces that followed `#`, where a fresh parse starts it after them; the heading scanner now moves the start past leading white space, and the live leaf follows. B3 is restated: 64, 16 and 16 model seeds, about 9.4 s of CPU in debug.
The command model found it only at wider seeds; a stream row (`# ` and the task-marker trace, then a space and a backslash) catches the mutant at B3.
The 20,000-seed run with math switched mid-stream found a frame that drew `∓` decoded, then raw once the inline math closed around it (seed 4,603): B's math exception, the same as a backtick opener's content, which P7 already skips for a leaf holding a backtick. It now also skips a leaf holding `$` or `\(` with math on.
The 20,000-seed run found a pending word `www.a∓b` drawn with the entity decoded; once the word ended it was an extended autolink, whose display is its source, so drawn text was taken back (seeds 4,860 and 63). The pending scan keeps the word's start and holds an entity or a punctuation escape while the word may still be an autolink (www., http://, https://). Rows: three such words by 1 and 3 bytes (failed first).
… rule bigboi round 6: the pending display's wait for a possible extended autolink checked three prefixes at a word's start, so `(www.a.b/©x` and `ftp://a.b/©x` drew `©` that the finished link shows raw. extendedLinkStart (a boundary, then www. or a scheme and ://) is now the one rule: the parse's www and URL steps and the pending scan use it, the scan reading each byte of a word once. Rows: both inputs and a parenthesized https link, byte by byte, before and after finish (failed first).
bigboi round 6: 16 one-byte items, a match early on a 1,024-byte line, then its newline: 32,816 hook visits against the stated 16,408. The line that held a pending match is searched again from that match when it completes, because the complete line's layout may put the match in other leaves (a table row's cells); keeping every match position instead would not be small. The bound is now items x (2(n + 1) + (longest - 1) x appends) + 8, in the work test (with the exact overlap, not a floor of 8) and the design; the model's property uses the exact overlap too. Row: that stream (fails under the old bound: 32,816 above 16,408).
…es it M2's F2-c: a stream whose last word is tokenized only by finish can push an unclosed opener past the token limit in that feed. The opener turned literal, but the feed marked expired openers changed before it parsed, so this one was not, and the node kept it concealed (`open …` for `**open …`; M2's seed 81). The mark now follows the parse. Row: `**open ` and 2,040 to 2,051 code spans, then a word of entities, in 30-byte appends (fails at 6f23cd4). The repeated-construct model now attaches a sink and fails on a node mismatch, as M2's (8cb56f3's test line).
M2's F2-b: bytes that only grow the pending line extend its layout without reading it again, but a fence opener's info string is the rest of that line, so the code block's info range stayed where the layout put it and the change was not listed. A line whose layout opens a fence now takes a new layout for new bytes. The markdown.zig hunk is M2's 012b6c3 verbatim, so M2's next merge of M1 is clean; M1's row reads changes directly (M2's ABI row stays in M2). Row: ```` ab`, in a task item and in a list item, appended in pieces (fails at 6f23cd4).
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Core and the Rust widget each parse Markdown on their own, and both parse a streamed document again from its start as it grows. This adds the native engine both will bind instead: CommonMark 0.31.2 with GFM tables, strikethrough, task lists, autolinks and math. Nothing calls it yet.
An append costs work in its own bytes. The engine keeps blocks in document order and reports which changed and how many top-level blocks are final. It lays out the pending line as if it ended, held only while its kind can still change, so a binding never parses.
read_leafgives a leaf's display text, styles and link targets; each paragraph and heading node gets writes that keep it equal toread_leaf.After every call, on random streams and on one construct repeated thousands of times, a model checks that each of seven work counters stays within a stated multiple of the bytes fed, each store within its limit, and that the completed line extends the pending line's layout. Streaming
list.mdin 30-byte appends costs 5.4M instructions; marked takes 21,430M. Each store has a limit, and a rejected call changes nothing.Tests: the shared corpus (794 rows: HTML, streamed, 80 frames), the shrinking models, work and limit tests.