Skip to content

Fix normal-state point tracking during terminal output in evil-ghostel#529

Merged
dakra merged 2 commits into
mainfrom
fix/228-normal-state-tracking
Jul 12, 2026
Merged

Fix normal-state point tracking during terminal output in evil-ghostel#529
dakra merged 2 commits into
mainfrom
fix/228-normal-state-tracking

Conversation

@dakra

@dakra dakra commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Problem

#228 regressed (reported in the latest comment): in evil normal state, point parked at the prompt stopped following the live cursor during output and stranded in scrollback.

Regression chain:

  1. 6a96954 fixed Cursor goes up with output in pi coding agent in normal state #228 with a last-cursor-line heuristic in the redraw advice.
  2. a6c0500 (command-remap rewrite) dropped it; tracking fell to the core anchor, which over-tracked and caused Cleaner extension point for evil/normal-state roaming over an animated terminal (anchor + wheel scroll)? #454.
  3. 0a539fb (Cleaner extension point for evil/normal-state roaming over an animated terminal (anchor + wheel scroll)? #454 fix) added the anchor veto comparing (point) against ghostel--cursor-char-pos — but after the render, when the cursor has already advanced while point stayed at its old text position. During streaming output the comparison always differs, the anchor is permanently vetoed, and point strands.

Fix

Commit 1 — parked tracking. Sample the parked check pre-render in evil-ghostel--around-redraw, where point and ghostel--cursor-char-pos are still same-frame values (no skew). While parked, snap point to the new cursor with goto-char ghostel--cursor-char-pos (exact by construction; column geometry can diverge on wide glyphs). The window-anchored guard is sampled pre-render too — a multi-line burst un-anchors the window until point snaps, which otherwise latches tracking off. Point moved off the cursor still stays put, and the veto keeps the viewport from being yanked back (#454 contract).

Commit 2 — row-wise tracking. Point elsewhere on the cursor's row (most commonly evil's EOL adjust parking point at cursor−1 after a keypress at a quiet prompt) follows row-wise: it rides to the new cursor's row and keeps its column. This closes the async-output gap (background jobs / agents streaming without a fresh RET) and matches the "prompt line" behavior discussed in #228.

Tests

  • ERT: mock + native regression tests for parked / row-roamed / off-row point across scrollback-growing redraws (the originals were deleted in the rewrite; the streaming case had no coverage).
  • New elate scenario test/elate/matrix/tracking-ghostel.json (7 groups: parked stream/burst, roamed, re-engage, esc-clamp, row-roam) — the operator matrix never exercises redraw-time point placement, which is why it stayed green through the regression. Verified red on the pre-fix code (3 groups fail) and fully green with both commits.
  • Live-verified under elate (zsh): slow streams, single-frame 200-line bursts, background-job bursts, insert-state control, roaming control.
  • make -j8 all green.

dakra added 2 commits July 12, 2026 09:14
The #454 anchor veto compares point against ghostel--cursor-char-pos
after the render, when the cursor has already advanced past output
that the buffer had not rendered when point was last placed - during
streaming output the comparison always differs, the anchor is
permanently vetoed, and point strands in scrollback (the #228 symptom,
reintroduced by 0a539fb after a6c0500 dropped the original
last-cursor-line heuristic).

Sample the parked check pre-render in evil-ghostel--around-redraw
instead: while point sits exactly at the previous frame's cursor in
normal/motion state (semi-char, window following), snap it to the new
cursor after the render.  The window check is pre-render too - a
multi-line burst un-anchors the window until point snaps - and the
snap targets ghostel--cursor-char-pos directly, since move-to-column
can diverge from the terminal's cell widths on wide glyphs and would
disengage tracking.  Point moved off the cursor still stays put, and
the anchor veto then keeps the viewport from being yanked back (#454).

Fixes #228.
Exact-position tracking disengages when point sits one char off the
cursor on the same row - most commonly evil's normal-state EOL adjust
after a keypress at a quiet prompt - and async output (background jobs,
agents streaming without a fresh RET) then strands point in scrollback.

Follow the cursor row-wise when point is elsewhere on its row: ride to
the new cursor's row and restore the user's column instead of snapping
onto the cursor.  Row membership is sampled pre-render alongside the
parked check.  Off the cursor's row point still stays put, and the
anchor veto still keeps the viewport from being yanked back (#454);
the viewport follows via redisplay in the row-roaming case.

The tracking matrix's esc-clamp group flips from xfail to pass, and a
new row-roam group pins the kept-column behavior against a background
burst.
@dakra
dakra merged commit 0d41dfb into main Jul 12, 2026
28 checks passed
@dakra
dakra deleted the fix/228-normal-state-tracking branch July 12, 2026 07:25
@dakra
dakra temporarily deployed to github-pages July 12, 2026 07:26 — with GitHub Actions Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cursor goes up with output in pi coding agent in normal state

1 participant