Repository navigation
Hold unfinished spans across soft line breaks, and scan only appended text - #14
Merged
Merged
Conversation
… text
Inline spans run on across soft line breaks and end with their paragraph,
as in CommonMark. The hold looked only at the open line, so `` `hello``
then a line break released the backtick, and the closing `` world` ``
took it back. It now holds from where an unfinished span opens in the open
paragraph, which starts after a blank line, a fence, a heading, a table
row or a rule, or at a new list item or quote.
Each streamed update compared the whole previous answer and rescanned the
open line, so small deltas added up to quadratic work. `StreamHold` now
has `append` (the last source with text added) and `replace` (new text,
a full pass). An append processes the lines completed since the last one
and the open paragraph from its last settled point. The renderer tells an
append from a replacement with one comparison, which also picks the
worker's append path in place of the comparison it made on its own, and
`render(source, { appended: true })` skips it for callers that know.
Finalized renders do no hold work.
3,000 appends after 2 MB of history now spend about as long in the hold
as after 20 KB (about 2 ms either way; the previous hold took 2.3 s).
Co-Authored-By: ace <bot@acehq.dev>
…rammar.rs Co-Authored-By: ace <bot@acehq.dev>
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.
Follow-up to #13, fixing two review findings.
1. Multiline spans still flickered
The hold looked only at the open line. Streaming
`helloand then a line break released the opening backtick, and the closingworld`took it back when the span became code.Inline spans now carry across soft line breaks and end with their paragraph, as in CommonMark. The hold starts where an unfinished span opens in the open paragraph. That paragraph starts after a blank line, a fence, a heading, a table row, or a rule or setext underline, or at a new list item or quote.
2. Every delta rescanned the history
shownLengthcompared the whole previous answer withstartsWithand rescanned the open line on every update, so small deltas added up to quadratic work.StreamHold.append(source)handles the last source with text added. It processes the lines completed since the last call once each, and the open paragraph from its last settled point.StreamHold.replace(source)is the full pass for new text.MarkitRenderer.renderdecides append vs. replace with one comparison. That decision also picks the worker's append path, replacing thestartsWiththat#renderPatchmade on its own. Callers that know they're appending can passrender(source, { appended: true })to skip it.Time spent in the hold for 3,000 appends, measured with each string flattened before the call:
Tests
tests/node/stream-hold.test.mjs:`hello\nstays held and becomeshello worldcode onceworld`arrives;tests/node/stream-hold-cost.test.mjschecks that 3,000 appends cost about the same after 2 MB as after 20 KB (3/3 runs pass), and that a replacement still reads the whole source.node --test tests/node/*.test.mjs: 15/15.shared,renderer,inline,code,github,react: 40/40, twice.