Address issue #436: keep editor and preview panes in sync on large files#464
Merged
Conversation
The editor↔preview scroll sync built two parallel arrays of reference-point
Y-coordinates (editor via regex over markdown, preview via DOM query) and
assumed they corresponded 1:1 by index. The two detectors disagree
mid-document — headers inside fenced code blocks, '===' setext headers, and
7+ hash lines — so a single divergence shifted every subsequent index,
leaving the panes synced only at the very start and end of the document.
- Tag every reference point with a kind (image / header level) on both sides:
updateHeaderLocations.js now returns {ys, kinds}; the editor scan records
parallel type arrays.
- Make the editor scan agree with the rendered DOM: skip headers inside ```
and ~~~ fences, detect '===' setext (level 1) and '---' (level 2), treat
7+ hashes as non-headers (CommonMark/Hoedown), allow 0-3 leading spaces.
- Replace tail-truncation in validateHeaderLocationAlignment with an LCS
alignment over the kind sequences that drops unmatched points on the
correct side, restoring the 1:1 invariant syncScrollers relies on. Falls
back to MIN-count truncation when type tags are absent.
- Extract the line classifier and the aligner as pure class methods and add
real-assertion tests (counts, kinds, alignment) for fence / setext /
7-hash / image / mid-document-divergence cases.
syncScrollers/syncScrollersReverse and the issue #342 ownership machinery are
unchanged: the alignment pass restores the invariant they already assume.
Related to #436
Setext underline detection now permits 0-3 leading spaces and trailing
whitespace ("Text\n --- "), matching how the preview DOM renders those
lines as headers. Reduces editor/preview reference-point divergence.
Related to #436
Reflect the {ys, kinds} JS return shape and the LCS reference-point
alignment that replaces MIN-count tail-truncation.
Related to #436
cb343a7 to
7bf3aec
Compare
Contributor
Code Coverage ReportCurrent Coverage: 62.30% Coverage Details (Summary) |
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.
Summary
Editor↔preview scroll sync built two parallel arrays of reference-point Y-coordinates — the editor side via regex over the raw markdown (
MPDocument.mupdateHeaderLocations), the preview side via a DOM query (updateHeaderLocations.js) — and assumed they corresponded 1:1 by index. The two detectors disagree mid-document, and a single extra point on one side shifts every later index, so the panes ended up synced only at the very start and end of a large document (issue #436).Three detector divergences caused this on real files:
<pre><code>(no header) in the preview.===setext headers were never detected by the editor (only---was).^#+\sbut are not headers in CommonMark/Hoedown.Fix
updateHeaderLocations.jsnow returns{ys, kinds}(0 = image, 1–6 = header level); the editor scan records parallel type arrays. The publiceditorHeaderLocations/webViewHeaderLocationsstayNSArray<NSNumber*>so existing consumers are untouched.+editorReferenceKindsForMarkdown:outLineNumbers:: skips headers inside```/~~~fences, detects===(level 1) and---(level 2, allowing 0–3 leading spaces and trailing whitespace), treats 7+ hashes as paragraph text, allows 0–3 leading spaces on ATX. Fence detection lives in a small static helperMPScanFenceMarker.validateHeaderLocationAlignmentwith a new pure aligner+alignEditorYs:editorTypes:previewYs:previewTypes:…that runs an LCS over the coarse kind class (image vs header) and keeps only matched points, dropping unmatched ones on the correct side regardless of position. Falls back to MIN-count truncation when type tags are absent (preserves existing callers/tests).syncScrollers/syncScrollersReverseand the issue Editor still jumps while typing at end of long document #342 scroll-ownership machinery are unchanged — the alignment pass restores the 1:1 index invariant they already assume.The classifier and aligner are pure class methods, so they are unit-tested headless with real assertions (counts, kinds, alignment) rather than no-crash checks — including the mid-document-divergence case that reproduces this bug.
Related Issue
Related to #436
Manual Testing Plan
#lines, and===/---setext titles). Drag the editor scrollbar to the middle. Expected: the preview tracks the same section, not just at the top/bottom.#lines: a document with a code block full of shell comments (# ...) between two real headings should keep sync through and past the block.===titles: a doc usingTitle\n===setext headings should sync at those headings.#lines, and HRs should scroll without the editor and preview drifting apart.Review Notes
plans/scroll-sync.md,plans/scroll-sync-fix-analysis.md, andplans/test_coverage_improvement_plan.mdwere updated to reflect the new{ys, kinds}shape and the kind-tagged LCS alignment.