Skip to content

Add OSC 133 imenu integration; fix line-mode scrollback navigation#243

Merged
dakra merged 2 commits into
mainfrom
osc133-imenu
May 9, 2026
Merged

Add OSC 133 imenu integration; fix line-mode scrollback navigation#243
dakra merged 2 commits into
mainfrom
osc133-imenu

Conversation

@dakra
Copy link
Copy Markdown
Owner

@dakra dakra commented May 8, 2026

Summary

Two related changes — the second was discovered while testing the first.

1. Stop resize redraw from re-anchoring scrollback in line mode

consult-line, consult-imenu, and any goto-char jump in line mode appeared to land on the target and then snap back to the live cursor. isearch worked because it advances window-start as it scrolls.

The minibuffer that consult opens resizes the ghostel window twice (open + close). Each resize forces a redraw with ghostel--redraw-resize-active = t. The predicate's resize branch classified a window as anchored whenever it was absent from ghostel--scroll-positions — meant to cover Emacs's own keep-point-visible drift on window-start but ignored window-point entirely. After the user navigated point into scrollback, no plain redraw ran while the minibuffer was open, so ghostel--scroll-positions stayed empty; the closing resize then re-anchored the window and yanked window-point back to the live cursor.

Fix: a window-point >= anchor guard on the resize branch. The drifted-window-start case still passes; the scrollback-jump case no longer does.

2. Add OSC 133 imenu integration

Each shell prompt with OSC 133 'A'/'B'/'C' markers becomes an imenu entry of the form <cwd> <command>, target landing on the prompt prefix's start. Composes with consult-imenu, imenu-list, evil's ]m/[m, etc.

The cwd is captured at OSC 133 'C' (command-start) and pushed onto a chronological list; reading default-directory lazily at index time would mis-attribute every prior prompt to the current cwd after a cd.

Position-based tracking (text properties or markers) does not survive: the renderer's per-row delete+reinsert wipes ad-hoc text properties on dirty rows, and eraseBuffer (resize-cols, force-full redraw, scrollback edge cases) collapses every marker to point-min. Pairing chronological cwds with the ghostel-prompt regions in buffer order at index time is robust to both: resize reflows the grid but preserves prompt order; scrollback eviction is detected as (cwd-count > region-count) and trims the oldest cwds.

Selecting an entry leaves line and copy modes untouched (the user is already free to navigate); only semi-char and char modes switch to emacs mode so point can move at all.

Test plan

  • make -j4 all — 331 elisp + 138 native tests pass; lint clean
  • New regression test ghostel-test-redraw-resize-preserves-scrollback-jump covers the consult-line scenario
  • 15 new imenu tests cover empty buffer, single/multi prompts, cwd attribution, multi-line commands, label truncation, scrollback eviction, and the goto behaviour in each input mode
  • Live verified in emacsclient: consult-line, consult-imenu, plain imenu all stick on the target line in line mode

dakra added 2 commits May 9, 2026 00:15
`consult-line', `consult-imenu', and any `goto-char' jump in line mode
appeared to land on the target line and then snap back to the live
cursor.  `isearch' worked because it advances `window-start' as it
scrolls.

The minibuffer that consult opens resizes the ghostel window twice
(open + close).  Each resize forces a redraw with
`ghostel--redraw-resize-active' = t.  The predicate's resize branch
classified a window as anchored whenever it was absent from
`ghostel--scroll-positions', which was meant to cover Emacs's own
`keep-point-visible' drift on `window-start' but ignored
`window-point' entirely.  After the user navigated point into
scrollback, no plain redraw ran while the minibuffer was open, so
`ghostel--scroll-positions' stayed empty; the closing resize then
re-anchored the window and `ghostel--anchor-window' yanked
`window-point' back to the live cursor.

The fix is a `(>= window-point anchor)' guard on the resize branch.
The drifted-`window-start' case still passes (point stays in the live
viewport), the scrollback-jump case no longer does.
Each shell prompt with OSC 133 'A'/'B'/'C' markers becomes an imenu
entry of the form "<cwd>  <command>", with the target landing on
the prompt prefix's start.  Composes with `consult-imenu',
`imenu-list', evil's `]m'/`[m', etc.

The cwd is captured at OSC 133 'C' (command-start) and pushed onto
a chronological list.  Reading `default-directory' lazily at index
time would mis-attribute every prior prompt to the current cwd
after a `cd'.

Position-based tracking (text properties or markers) does not
survive: the renderer's per-row delete+reinsert wipes ad-hoc text
properties on dirty rows, and `eraseBuffer' (resize-cols, force-full
redraw, scrollback edge cases) collapses every marker to `point-min'.
Pairing chronological cwds with the `ghostel-prompt' regions in
buffer order at index time is robust to both: resize reflows the
grid but preserves prompt order, and scrollback eviction is detected
as (cwd-count > region-count) and trims the oldest cwds.

Selecting an entry leaves line and copy modes untouched (the user is
already free to navigate); only semi-char and char modes switch to
emacs mode so point can move at all.
@dakra dakra merged commit 9917a33 into main May 9, 2026
22 checks passed
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.

1 participant