You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Broaden anchor-window clamp to cover CUP-park on empty trailing row
ad8536e narrowed the `ghostel--anchor-window' clamp to fire only when
libghostty reports pending-wrap, which fixed the #146 regression but
reopened a variant of #138 for TUIs that move their cursor to the
bottom of the screen via absolute positioning (CUP) rather than via
writing-then-wrapping. In that case `pt' equals `point-max' and the
cursor sits on the last viewport row, but pending-wrap is nil — so the
clamp doesn't fire and Emacs shifts `window-start' by one row to make
`pt' "visible," fighting the viewport pin.
Expose a second terminal-side predicate `ghostel--cursor-on-empty-row-p'
backed by a new `render.isRowEmptyAt' helper, and widen the clamp guard
to `(or pending-wrap cursor-on-empty-row)'. The predicate returns t iff
the row containing the cursor has no written cells and no cells with
non-default styling — exactly the condition under which `buildRowContent'
produces `byte_len == 0'. Wide-spacer-tail cells are skipped to mirror
`buildRowContent' (defensive — in practice spacer tails always follow a
wide grapheme).
Source `cy' from `RS_DATA_CURSOR_VIEWPORT_Y' gated by `...HAS_VALUE' so
the coordinate space matches the viewport row iterator that
`isRowEmptyAt' walks, and the predicate returns nil when the cursor
isn't visible in the current viewport. Caller owns the
`ghostty_render_state_update' refresh so it only happens once per call.
An earlier draft (#158) tried `pos-visible-in-window-p' but it reflects
the previous redisplay rather than the just-pinned `window-start', and
breaks in batch. The terminal-side predicate answers the real
question — is there anything on this row to anchor `pt' to? — without
consulting Emacs redisplay state.
Bump module version to 0.16.3 (new exported function).
Test changes:
- `ghostel-test-cursor-on-empty-row-p' covers the predicate across
fresh-terminal / post-write / post-CRLF / post-CUP cursor positions.
- `ghostel-test-anchor-window-clamps-on-empty-row' is the #157
regression test: feeds "foo\r\nbar\r\n" to park the cursor on an
empty last row, asserts PT lands at `point-max', pending-wrap is nil,
empty-row is t, and the clamp fires.
- `ghostel-test-anchor-window-no-clamp-on-populated-last-row'
complements #146: cursor at `point-max' on a last row that does have
content (e.g. a shell prompt) must NOT be clamped regardless of which
predicate is consulted.
Closes#157.
env.bindFunction("ghostel--alt-screen-p", 1, 1, &fnAltScreen, "Return t if terminal is on the alternate screen buffer.\n\n(ghostel--alt-screen-p TERM)");
46
46
env.bindFunction("ghostel--cursor-position", 1, 1, &fnCursorPosition, "Return terminal cursor position as (COL . ROW), 0-indexed.\n\n(ghostel--cursor-position TERM)");
47
47
env.bindFunction("ghostel--cursor-pending-wrap-p", 1, 1, &fnCursorPendingWrap, "Return t if the cursor is in pending-wrap state.\n\n(ghostel--cursor-pending-wrap-p TERM)");
48
+
env.bindFunction("ghostel--cursor-on-empty-row-p", 1, 1, &fnCursorOnEmptyRow, "Return t if the cursor row has no written cells or styled cells.\n\n(ghostel--cursor-on-empty-row-p TERM)");
48
49
env.bindFunction("ghostel--debug-state", 1, 1, &fnDebugState, "Return debug info about terminal/render state.\n\n(ghostel--debug-state TERM)");
49
50
env.bindFunction("ghostel--debug-feed", 2, 2, &fnDebugFeed, "Feed STR to terminal and return first row + cursor.\n\n(ghostel--debug-feed TERM STR)");
50
51
env.bindFunction("ghostel--copy-all-text", 1, 1, &fnCopyAllText, "Return entire scrollback as plain text string.\n\n(ghostel--copy-all-text TERM)");
0 commit comments