Skip to content

ghostel--window-anchored-p's +1-line tolerance snaps a one-line-up window back to the bottom during active output #400

Description

@sungwonida

Summary

ghostel--window-anchored-p's +1-line tolerance classifies a window scrolled up exactly one line as still anchored, so the per-redraw re-anchor loop in ghostel--redraw-now yanks it back to the bottom during active output. The user-visible effect: while a TUI is streaming, you scroll up one line to peek and the viewport snaps back down on the next frame; scrolling two or more lines sticks.

When it bites (measured, v0.34.0, GNU Emacs 30.2 NS + the prebuilt module)

  • Line-count fallback mode (Emacs 28 path / ghostel--pixel-anchor-supported-p nil): always. Every grid row ends in \n, so an anchored window shows exactly floor(window-screen-lines) rows and window-start sits exactly at that boundary; the predicate accepts floor+1, so a one-line-up window still passes and gets re-anchored on every redraw.
  • Pixel-anchor mode: only when the window body height is an exact multiple of the default line height (then vscroll is 0, there is no partial top line to consume the +1 slack, and the same misclassification occurs). For a 14px line height that's ~1/14 of window geometries — easy to hit after a resize.

Reproduced with a synthetic streaming driver in three geometries (non-multiple / exact-multiple / line-fallback): snapback occurs in the latter two with stock, never with a strict predicate.

Suggested fix

Make the predicate mirror the anchor's own arithmetic instead of allowing slack: anchored ⟺ window-start ≥ the buffer position ghostel--anchor-window itself would choose — i.e. point-max moved back (ceiling lines) rows in pixel mode (where a partially-scrolled top line is legitimate) and (floor lines) rows in vscroll-0 mode. With that change (tested as an :around replacement): one-line-up scroll sticks in all three geometries, bottom tracking under growth, resize re-anchoring, short buffers, and multiple windows on one buffer all behave as before.

The +1 slack looks like it was sized for the pixel anchor's partially-visible top line; deriving the threshold per-mode removes the need for slack entirely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions