Preserve point and evil visual markers across redraws#164
Merged
dakra merged 2 commits intodakra:mainfrom Apr 21, 2026
Merged
Conversation
Full and partial redraw paths both destroy markers:
- Full: `env.eraseBuffer()' snaps every marker to `point-min'.
- Partial: `env.deleteRegion' + `env.insert' per dirty row drift
markers asymmetrically by insertion-type, random-walking them
away from the user's intended position.
Point is owned by the renderer (placed at the TUI cursor on exit),
but `mark' is user state — `C-SPC' in an emacs-state buffer, any
region command in normal-state — and must survive.
Snapshot `(mark-marker)` position at the top of `redraw' and
restore it on exit via `defer'. Clamp to `point-max' in case the
buffer shrank. No change when the mark was never set in the buffer
(`marker-position' returns nil).
Other markers (e.g. evil's `evil-visual-beginning' /
`evil-visual-end') stay the caller's concern — a package can wrap
`ghostel--redraw' with its own save/restore for state the native
module cannot know about.
Adds `mark-marker' / `marker-position' / `set-marker' to the
symbol cache and thin `Env' helpers. One ERT case verifying mark
survives a full-redraw cycle.
46edea9 to
e43f384
Compare
`evil-ghostel--around-redraw' used to save and restore only `point', and only in non-insert/emacs states. Native `ghostel--redraw' rewrites the viewport region on every call, moving every marker in the buffer — `evil-visual-beginning' and `evil-visual-end' drift asymmetrically by insertion-type, so `v' in a buffer with a streaming TUI (Claude Code, watch, streaming logs) shows a multi-row selection the moment `v' is pressed: mark had drifted backwards and `evil-visual-end' ratcheted forward while the user was idle in the buffer. Extend the advice to save and restore: - `point' in non-terminal states (unchanged). - `evil-visual-beginning' and `evil-visual-end' in `visual' state. `mark' is preserved by `ghostel--redraw' itself (see PR dakra#163 — the native-side fix lives in `src/render.zig', where all users benefit regardless of whether evil-ghostel is loaded). This PR layers on top of that — it can be merged after or before dakra#163; if merged first, `mark' stays drifting until the native patch lands. Tests: three mock-based cases covering point preservation in normal, point follow-through in emacs, and visual-marker preservation in visual.
e43f384 to
606ec4d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
evil-ghostel--around-redraw' currently saves and restores onlypoint', and only in non-insert/emacs states. Nativeghostel--redraw' rewrites the viewport region on every call, moving every marker in the buffer.evil-visual-beginning' /evil-visual-end' drift asymmetrically by insertion-type — sov' pressed in a buffer being updated by a streaming TUI (Claude Code, `watch', streaming logs) produces a multi-row selection instead of one char: both markers had already drifted apart while the user was idle in the buffer.Change
Extend the advice to save and restore, in addition to point:
evil-visual-beginning' andevil-visual-end' in `visual' state.insert' andemacs' continue to let point follow the TUI cursor. Alt-screen guard unchanged.Relationship to #163
Layered on top of the native
mark'-preservation PR (#163). Deliberately does **not** handlemark' here — it's universal buffer state and belongs at the native layer where all users benefit regardless of whether evil-ghostel is loaded. The layering is:markacross native redraws #163): `mark' — single buffer-local marker every Emacs package depends on.point' in normal/visual,evil-visual-beginning' / `evil-visual-end' — evil-specific state the native module can't know about.Order-independent: if this merges first, `mark' stays drifting until #163 lands; if #163 merges first, this closes the visual-marker gap. Both need to land for full coverage.
Tests
Three mock-based ERT cases: point preservation in normal, point follow-through in emacs, visual-marker preservation in visual.