Skip to content

evil-ghostel: anchor-inhibit hook so point can roam over an animated terminal (#454)#484

Merged
dakra merged 3 commits into
mainfrom
evil-anchor-inhibit-hook
Jun 30, 2026
Merged

evil-ghostel: anchor-inhibit hook so point can roam over an animated terminal (#454)#484
dakra merged 3 commits into
mainfrom
evil-anchor-inhibit-hook

Conversation

@dakra

@dakra dakra commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Summary

Resolves #454. Under evil + an animated (~30 fps) terminal, ghostel's per-redraw
viewport anchor snapped point to the live terminal cursor on every frame, fighting
two interactions:

  • Normal-state roaminghjkl motion off the live cursor was yanked back ~30×/sec.
  • Insert-state scrollback — a wheel-scroll into scrollback was immediately re-anchored to the bottom.

Changes

Core (ghostel.el)

  • New ghostel-anchor-inhibit-functions abnormal hook (mirrors ghostel-inhibit-redraw-functions): a non-nil return from any function skips anchoring that window — neither viewport nor point moves — for the redraw. FORCE anchors (paste/yank, mode switches) still win. This is the blessed extension point asked for in the issue, replacing the brittle define-advice workaround.
  • The deliberate ghostel-semi-char-mode / ghostel-char-mode anchors now pass FORCE, so an explicit mode switch always snaps back to the prompt even mid-roam.

evil-ghostel

  • Registers a buffer-local predicate (evil-ghostel--anchor-inhibit) that vetoes the anchor while point roams off the cursor in a motion-capable evil state, scoped to where evil-ghostel drives PTY input (evil-ghostel--active-p: semi-char, outside alt-screen) — so line/copy/emacs/alt-screen keep ghostel's own anchor handling.
  • Insert/emacs-state point-follow in evil-ghostel--around-redraw now only re-syncs to the live cursor while the window still tracks live output, so scrolling into scrollback sticks instead of being yanked back every frame.
  • Drops the read-only guard (*) on p/P: paste is sent over the PTY and writes nothing to the read-only buffer, so the guard wrongly errored "Buffer is read-only" in the common semi-char case (separate pre-existing bug found while testing; the fall-through to evil-paste-* still fails on its own when not driving the terminal).

Testing

  • Unit tests: hook veto + FORCE bypass, the predicate's truth table incl. the active-p/alt-screen scoping, the insert-state scrollback gate, and interactive p/P on a read-only buffer (the existing paste test called the command directly and missed the interactive * barf).
  • Live-verified in a sandboxed evil + ghostel + fish session: normal-state roaming holds while an animated counter runs, auto-follow resumes on return to insert/cursor, insert-state scroll-into-scrollback sticks, and a mode switch snaps back to the prompt. Plus a general evil-ghostel regression sweep (operators, motions, insert/append, paste, undo, visual, escape/alt-screen routing, copy/emacs mode) — no regressions.
  • make all green.

dakra added 3 commits June 30, 2026 17:15
Per-redraw anchoring snapped point to the live terminal cursor in every
follow-capable input mode, so evil normal-state motion (hjkl) over an
animated terminal was yanked back to the cursor ~30 times a second
(issue #454).

Add a `ghostel-inhibit-anchor-functions' abnormal hook: a non-nil return
from any function skips anchoring that window (neither viewport nor point
moves) for the redraw.  FORCE anchors (paste/yank, mode switches) still
win.  evil-ghostel registers a buffer-local predicate that vetoes while
point roams off the cursor in a motion-capable evil state, replacing the
brittle define-advice workaround from the issue.

The predicate is scoped to where evil-ghostel drives PTY input
(`evil-ghostel--active-p': semi-char, outside alt-screen), so the other
input modes keep ghostel's own anchor handling.  The deliberate
mode-switch anchors in `ghostel-semi-char-mode'/`ghostel-char-mode' now
pass FORCE so an explicit switch always snaps back to the prompt.
In insert/emacs state `evil-ghostel--around-redraw' re-synced point to the
live terminal cursor on every redraw, so a wheel-scroll into scrollback was
yanked back to the bottom ~30 times a second under an animated terminal
(issue #454, seam 2) — redisplay re-scrolls to keep the dragged point visible.

Gate that re-sync on `evil-ghostel--window-following-p': follow the cursor
only while the window still tracks the live output (or no window shows the
buffer).  Once the user scrolls into scrollback the window stops following,
so point is left put and the viewport holds its position.
…ostel

`(interactive "*P")' ran barf-if-buffer-read-only before the body, so p/P
errored "Buffer is read-only" in the (read-only) ghostel buffer — the common
semi-char case.  The live-terminal path writes nothing to the buffer; it
pastes over the PTY (point movement is allowed in a read-only buffer), so the
guard is wrong.  The fall-through to evil-paste-* still fails on its own
insert when not driving the terminal.

The existing test called the command directly, bypassing the interactive
spec; add one that drives it through call-interactively in a read-only buffer.
@dakra
dakra force-pushed the evil-anchor-inhibit-hook branch from 5bf2290 to 41ce0f6 Compare June 30, 2026 15:15
@dakra
dakra merged commit 41ce0f6 into main Jun 30, 2026
18 of 19 checks passed
@dakra
dakra deleted the evil-anchor-inhibit-hook branch June 30, 2026 15:16
@dakra
dakra temporarily deployed to github-pages June 30, 2026 15:17 — 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.

Cleaner extension point for evil/normal-state roaming over an animated terminal (anchor + wheel scroll)?

1 participant