fix(virtual-list): report only ranges the list actually draws - #56
Merged
Conversation
`MoonVirtualList::on_visible_range` called its observer from the closure it hands to `uniform_list`. That closure has a second job: `measure_item` renders `item_to_measure_index..+1` to obtain a row height, from `request_layout` and again from `prepaint`, before the real range exists. An observer wired through it therefore saw a phantom `0..1` twice per frame ahead of every real range. An observer that evicts state for rows outside the range it is told about — keyboard focus, an open popup — then evicted everything but the measured row on every frame. Downstream, MoonTerminal's Connections tab blurred the core name field one frame after the click, so a core could not be renamed at all. `UniformList` gains its own `on_visible_range` channel, invoked once per prepaint with the range that goes to the item renderer, flipped indices included. The reporting rule has two halves the tests pin down: a list holding no rows reports `0..0`, while a list that holds rows but shows none of them — zero height, mid-collapse, not yet sized — stays silent, so a squeezed list is never mistaken for an empty one. Both conditions are needed: an emptiness test alone reports `12..13` for a collapsed list at an offset that is not a whole multiple of a row, and a height test alone silences a merely padded list whose row is plainly visible, because Taffy floors the element at its own padding. The builder documents what the runtime does and does not promise: it reports per prepaint rather than per frame, `cx.notify` and `Window::refresh` are dropped mid-draw while `Window::blur` still takes effect, and a scroll the observer requests lands on the next frame. Covered by `virtual_list.visible_range_reporting`, a new audit contract naming six behavioural tests, and by one test in `moon-gpui` itself. `MoonVirtualList` keeps its public signature, so no baseline moves.
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.
Problem
MoonVirtualList::on_visible_rangeinvoked its observer from the closure handed touniform_list. That closure is also used to MEASURE one row:measure_itemrendersitem_to_measure_index..+1fromrequest_layoutand again fromprepaint, before the real range exists. The observer therefore saw a phantom0..1twice per frame ahead of every real range.An observer that evicts state for rows outside the reported range — keyboard focus, an open popup — then evicted everything but the measured row on every frame. Downstream, MoonTerminal's Connections tab blurred the core-name field one frame after the click, so a core could not be renamed at all. The other consumer, the chart stack, only survived because its handler is an idempotent assignment that the real range repaired later in the same frame.
Change
UniformListgains its ownon_visible_rangechannel, invoked once per prepaint with the range that goes to the item renderer (flipped indices included, while decorations keep receiving unflipped positions).MoonVirtualListforwards to it; its public signature is unchanged.Two halves to the reporting rule, and both are needed:
0..0;An emptiness test alone reports
12..13for a collapsed list whose offset is not a whole multiple of a row; a height test alone silences a merely padded list whose row is plainly visible, because Taffy floors the element at its own padding.The builder now documents what the runtime does not promise: reporting is per prepaint rather than per frame (a cached ancestor skips it, a retried
Window::transactpass repeats it),cx.notifyandWindow::refreshare dropped mid-draw whileWindow::blurstill takes effect, and a scroll requested from the observer lands on the next frame.Verification
tools\run-component-guardrails.ps1→ PASS (437 component tests, 7 gallery, audit/API/mirror baselines unmoved).cargo test -p moon-gpui→ 180 passed. Not part of CI, run locally because this touches the runtime.gpui::testcases plus one inmoon-gpui, held by a new audit contractvirtual_list.visible_range_reporting. Each was proven by mutation: wiring the observer back into the renderer yields[0..1, 0..1, 0..10]; reportingvisible_rangeinstead of the rendered range makes a flipped list claim40..50; the old padded-box guard yields[]instead of[11..12]; dropping the height half yields[12..13]for a collapsed list; deleting a test drops the contract toFail.[patch],cargo test --workspacegreen, and the Connections rename confirmed working by hand.docs/MOON_PATCH_QUEUE.mdrecords the patch so a re-sync of the GPUI extraction restores both halves of the rule.