Skip to content

mixin_markdown_widget: fix streaming block identity, highlight flicker, and selection details - #543

Merged
boyan01 merged 1 commit into
mainfrom
mixin_markdown_widget/step0-fixes
Oct 21, 2026
Merged

boyan01 merged 1 commit into
mainfrom
mixin_markdown_widget/step0-fixes

Conversation

@boyan01

@boyan01 boyan01 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Small fixes found while reviewing mixin_markdown_widget. No architecture change. This is the base of a stack; the selection refactor builds on top.

 appendChunk(chunk)
   reparse tail block
-    id = kind-count-hash(content)        # new id every chunk → remount, caches dropped
+    id = previous tail id (same kind-count, nested too, images excluded)

 MarkdownCodeHighlightCache.resolve(streamed code)
-  placeholder = plain text               # flashes plain each chunk
-  compute() per chunk
+  placeholder = last highlight + plain suffix
+  one highlight in flight per block, then run the latest source

 MarkdownDocumentView.build
-  dispose all link recognizers           # cached list/quote rows keep disposed ones
+  one recognizer per LinkInline, disposed when the link leaves the document
-  onTapLink identity change → clear row cache
+  rows call a stable forwarder; cache cleared only when links turn on/off

 double click
-  ASCII separator set                    # Chinese text selects a whole run
+  + CJK punctuation; non-ASCII runs narrow to ICU word boundaries

Also:

  • currentState as dynamic in the document view → internal MarkdownSelectableBlockHandle interface.
  • New MarkdownController.updateData(data, {streaming}): appends when data extends the current data, else full parse; commits unless streaming.
  • @Deprecated: MarkownWidget alias, MarkdownDocumentParser.parseAppending (ignores previousDocument).

Evidence

  • Before: streaming ids churned on every chunk:
    "Hello"     → paragraph-2-1968976203
    " world"    → paragraph-2-1498229191
    " again\n"  → paragraph-2-114815919
    
    markdown_link_recognizer_test.dart fails against the old lib/: the cached - [link](url) row holds a recognizer that was already disposed.
    After: ids stay the same across appends (paragraph, fenced code, list and its item children); the recognizer test passes.
  • flutter analyze: no issues. flutter test: 214 passed (192 existing, unchanged expectations + 22 new in markdown_controller_streaming_test, markdown_code_highlight_cache_test, markdown_link_recognizer_test, markdown_word_selection_test).
  • benchmark/incremental_append_benchmark.dart: baseline 112 ms → 107 ms, large-prefix 60 ms → 63 ms (noise).
  • Not covered by automation: visual highlight flicker and real-device CJK word segmentation (flutter_tester ICU has no CJK dictionary, so tests assert dictionary-independent properties).

Merge Danger

Door: two-way

Internal changes plus one additive API and two deprecations; reverting is a plain revert.

Blast Radius: streaming

Apps that stream markdown see the tail block keep its state instead of remounting. Highlight placeholders may briefly show a stale last token (e.g. an unclosed string) until the real highlight lands. codeBlockBuilder / bulletBuilder changes still do not invalidate cached rows (pre-existing).

…r, and selection details

- Keep the id of a streamed tail block (and its nested blocks) stable across
  appendChunk, so its state, code block scroll position, and caches survive
  each chunk. Full parses and setData keep content-hashed ids.
- While a streamed code block grows, show its last highlight with the new
  tail in plain text instead of flashing back to plain, and run at most one
  highlight per block at a time instead of one per chunk.
- Keep one tap recognizer per link while the link stays in the document, so
  cached rows never hold a disposed recognizer.
- Call onTapLink through a stable forwarder, so passing a new closure on
  every parent rebuild no longer clears the row cache.
- Double-click selects words in CJK text: CJK punctuation separates words and
  non-ASCII runs narrow to ICU word boundaries. ASCII text keeps its rule.
- Type the block state access of the document view instead of using dynamic.
- Add MarkdownController.updateData, which appends when the new text extends
  the current data and parses in full otherwise. Deprecate the misspelled
  MarkownWidget alias and MarkdownDocumentParser.parseAppending, which
  ignores its previous document.
@boyan01
boyan01 force-pushed the mixin_markdown_widget/step0-fixes branch from 11ae229 to 4805c51 Compare October 20, 2026 13:29
@boyan01
boyan01 merged commit 4d945ba into main Oct 21, 2026
7 checks passed
@boyan01
boyan01 deleted the mixin_markdown_widget/step0-fixes branch October 21, 2026 01:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant