Expand description
The height index: pixel position ↔ row, in O(log n).
This is the structure that makes variable-height rows possible.
xtext could skip it because every row was fontsize × subline_count,
so “what is at pixel Y” was a division (gtk_xtext_find_char,
xtext.c:1339) and the scroll adjustment’s unit was fractional lines
(page_size = height / fontsize, xtext.c:919). Both assumptions have
to go for an image to be able to be 240 pixels tall.
Why chunked prefix sums and not a Fenwick tree. A Fenwick tree is the textbook answer for prefix sums with point updates, and it is the wrong shape here: it is indexed from a fixed origin, and this buffer grows at both ends (chat-history backfill prepends) and shrinks at the front (scrollback trim). Every prepend would renumber the whole tree.
Instead: rows live in fixed-target-size chunks in a VecDeque, each
chunk caching its own summed height, with a lazily-repaired running
prefix over the chunks. Front insert pushes a chunk; trim pops one;
neither touches the rest. A lookup binary-searches the chunk prefixes
and then scans within one chunk, which is bounded by [CHUNK_TARGET].
Estimated heights. A row that has never been laid out reports an
estimate rather than forcing a measure, so a resize costs O(visible)
instead of xtext’s O(entire scrollback) (gtk_xtext_calc_lines,
xtext.c:4395, walks every entry on every width change). The honest
cost is that the scrollbar’s extent is approximate until the estimates
are replaced. That is survivable precisely because scroll position is
anchored to a row rather than to a pixel value — see
crate::anchor.
Structs§
- Height
Index - Chunked prefix-sum index over row heights.
- Hit
- Where a pixel offset lands.