pmacs/pmacs-gpu
Levi Neuwirth 840a33838a
feat(gpu): the scroll indicator asks where the bytes landed
The GPU's readout reckoned in source lines while its window holds
visual rows: `format_scroll_indicator` compares `visible` (rows that
fit) against `current_line_starts.len()` (source lines). The GPU has
always wrapped, so this is not new in this lane — but the lane is
where it became nameable, because `ui.line-wrap` is now what decides
which formula applies. A one-line file took the first branch,
`total_lines <= 1`, and reported "All" with most of itself below the
window.

Under wrap it now goes through `pmacs_protocol:📜:classify`,
the same rule the TUI took in eaf3df8, with only the string spelling
local (framing §5d.6).

The load-bearing part is how `first_visible` / `last_visible` are
decided. Two cheaper predicates are available and both are wrong:

  - `view_range.0 == 0` / `view_range.1 == len` describe the SHAPED
    span, which carries SCROLL_OVERSCAN source lines past the window.
    A slice reaching EOF says nothing about EOF being on screen. This
    is the guess that broke extreme_sizes_render_with_contained_popups
    when it was tried earlier and got reverted rather than shipped.
  - `scroll_top == 0` ignores `code_scroll_residual`, so scrolling
    into the middle of a wrapped first line still claims "Top".

So `code_byte_painted` asks cosmic-text where the byte actually
landed and intersects it with the drawable clip — `caret_rect`'s
existing test, generalized off the own cursor. Wrapped continuation
runs below the band and overscan lines shaped past the bottom both
fail it, because layout is what decides, not arithmetic over it.

`compose_status_runs` takes `&mut self` for this. That is the point
rather than a wart: the alternative is a cached per-frame
(first_visible, last_visible) pair, which is a value maintained
beside the layout and free to disagree with it — the same shape as
the `code_wrap` shadow field this lane already removed once.

One bug the tests found rather than confirmed. The first version
rejected an empty `view_range`, a guard borrowed from the caret and
completion-anchor paths where it means "nothing shaped". A file
ending in a newline has a final empty line, and a viewport parked on
it is `(len, len)` with one real row — so reaching the bottom of any
such file reported a percentage instead of "Bot". `code_byte_px`
already returns `None` when nothing is shaped, which is what that
guard was reaching for.

Bite, per clause. Replacing the pixel clip with the range test alone
fails a_wrapped_single_line_is_not_all,
a_slice_that_reaches_eof_is_not_yet_bot,
a_sub_line_residual_moves_off_top — and independently
extreme_sizes_render_with_contained_popups, the pre-existing test
that rejected this same shortcut before. Restoring the empty-range
guard fails an_empty_final_line_still_counts_as_bot and
a_slice_that_reaches_eof_is_not_yet_bot.

Gates: fmt, workspace clippy -D warnings, git diff --check,
PMACS_REQUIRE_GPU=1 cargo test -p pmacs-gpu 228/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
2026-08-07 19:15:36 +02:00
..
fonts feat(math): bundle Latin Modern Math and read its MATH constants 2026-07-24 18:27:34 -04:00
src feat(gpu): the scroll indicator asks where the bytes landed 2026-08-07 19:15:36 +02:00
Cargo.toml feat(release): binaries on tag — Distribution Stage 1 2026-08-01 14:40:47 -04:00