Skip to content

fix(restore): preserve live buffer and cursor across close/restore - #15

Merged
WilliamHsieh merged 1 commit into
masterfrom
feature/restore-snapshot-state
Jun 10, 2026
Merged

fix(restore): preserve live buffer and cursor across close/restore#15
WilliamHsieh merged 1 commit into
masterfrom
feature/restore-snapshot-state

Conversation

@WilliamHsieh

Copy link
Copy Markdown
Owner

popup.opts.target_bufnr / lnum / col are captured once by the adapter at
peek time and never refreshed. If the user navigates inside a popup (gd
/ gf / :edit / direct cursor moves), the popup's window state diverges
from opts: the window shows a different buffer or has the cursor at a
different position. close_all + restore then reopens what was originally
peeked, not what the user was last viewing -- losing their navigation.

Snapshot the popup's live state into opts at two points:

  1. Popup:snapshot_state() reads nvim_win_get_buf + nvim_win_get_cursor
    and writes back to self.opts. Idempotent; rerunning is a no-op if
    the popup hasn't moved since the last snapshot.

  2. Two callers cover the close paths:

    • Popup:close (the programmatic close_all path; close_all suppresses
      WinLeave via eventignore so the autocmd below cannot catch it here).
    • A new WinLeave autocmd in init.lua (the focus-leaves and external
      :close paths; Popup:close is not invoked when the popup closes via
      nvim's own WinClosed -> on_popup_closed dispatch).

Every close path is covered by at least one snapshot point.

The autocmd reads is_overlook_popup via nvim_win_get_var rather than
vim.w because tests reassign vim.w = {} for isolation, which detaches
the proxy from real window-locals.

opts.col is 1-indexed (the adapters use vim.fn.getpos);
nvim_win_get_cursor returns 0-indexed col, so snapshot_state adds 1 for
the round-trip with set_cursor_position's math.max(0, col - 1) on
restore.

Adds two regression tests: one for the WinLeave path (user switches
focus before close_all), one for the Popup:close path (user is still
inside the popup at close_all time).

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

popup.opts.target_bufnr / lnum / col are captured once by the adapter at
peek time and never refreshed. If the user navigates inside a popup (gd
/ gf / :edit / direct cursor moves), the popup's window state diverges
from opts: the window shows a different buffer or has the cursor at a
different position. close_all + restore then reopens what was *originally*
peeked, not what the user was last viewing -- losing their navigation.

Snapshot the popup's live state into opts at two points:

1. Popup:snapshot_state() reads nvim_win_get_buf + nvim_win_get_cursor
   and writes back to self.opts. Idempotent; rerunning is a no-op if
   the popup hasn't moved since the last snapshot.

2. Two callers cover the close paths:
   - Popup:close (the programmatic close_all path; close_all suppresses
     WinLeave via eventignore so the autocmd below cannot catch it here).
   - A new WinLeave autocmd in init.lua (the focus-leaves and external
     `:close` paths; Popup:close is not invoked when the popup closes via
     nvim's own WinClosed -> on_popup_closed dispatch).

Every close path is covered by at least one snapshot point.

The autocmd reads `is_overlook_popup` via nvim_win_get_var rather than
vim.w because tests reassign `vim.w = {}` for isolation, which detaches
the proxy from real window-locals.

opts.col is 1-indexed (the adapters use vim.fn.getpos);
nvim_win_get_cursor returns 0-indexed col, so snapshot_state adds 1 for
the round-trip with set_cursor_position's `math.max(0, col - 1)` on
restore.

Adds two regression tests: one for the WinLeave path (user switches
focus before close_all), one for the Popup:close path (user is still
inside the popup at close_all time).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@WilliamHsieh
WilliamHsieh merged commit 0c93b24 into master Jun 10, 2026
5 checks passed
@WilliamHsieh
WilliamHsieh deleted the feature/restore-snapshot-state branch June 10, 2026 09:22
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