Skip to content

fix(diff): give the user's windows back their sizes when a diff closes - #421

Merged
ThomasK33 merged 3 commits into
mainfrom
fix/diff-window-sizes-416
Nov 1, 2026
Merged

ThomasK33 merged 3 commits into
mainfrom
fix/diff-window-sizes-416

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Why

We are closing the open follow-up issues in coder/claudecode.nvim one small PR at a time. This one takes item 5 of #416.

A diff that opens in the user's tab runs wincmd =, which resizes every window in that tab. When the diff closes, Neovim shares the space of the closed windows out by its 'equalalways' rules. The user's windows end at other sizes:

The fix saves the sizes of the tab's tiled windows before the diff splits the tab, and sets them again when the diff closes.

What changed

  • lua/claudecode/diff.lua: new save_window_sizes(tab) and restore_window_sizes(saved). The diff state gets a window_sizes field ({ tab, layout, width, height, windows = { { win, width, height }, ... } }): the tab's winlayout() tree (no floating or external windows), the columns and rows its windows fill, and each window's size. The split layouts save it when the diff stays in the user's tab, and _cleanup_diff_state restores it after it closes the diff's windows and before it sets the Claude terminal's idle width. The setup error handler restores it too, when setup fails before the diff is registered.
    • A diff that opens in a tab another open diff already split shares that diff's sizes, so the last diff to close gives back the sizes from before the first one.
    • The restore does nothing when the tree changed (another diff is still open, or the user moved or added a window) or the space the windows fill changed (a resized screen, a tab line that appeared). It runs each resize twice, as winrestcmd() does.
  • lua/claudecode/diff_inline.lua: the unified layout saves the sizes before its fallback split or its vsplit. It restores them in cleanup_inline_diff, before it sets the terminal width back, and in cleanup_unregistered_inline_setup when setup fails.
  • tests/integration/diff_window_sizes_spec.lua (new, real Neovim): vertical and horizontal diffs next to a resized editor, close_tab while the user is in another tab, a split and a unified setup that a user's autocmd stops, no restore after a screen resize, a new tab line or CTRL-W K, a unified diff next to the user's own vimdiff, and two unified diffs in one tab closed first-opened first (unified tests 0.9+ only).
  • tests/mocks/vim.lua: the busted mock gets vim.fn.winlayout() (one row of the tab's tiled windows) and vim.deep_equal(), which the restore calls.
  • tests/integration/diff_current_tab_spec.lua: removed the loop that checked the user's diff windows keep 'diff'. It passed with and without the new-tab fix, because the diff never touches those windows. The created_new_tab and tab-return assertions keep guarding that case.
  • README.md (auto_resize_terminal section) and lua/claudecode/README.md (Diffs contract): say that closing a diff gives the windows their sizes back.
  • covered.lock: re-recorded. All code facts read TRUE, and the model passes.

Scope

  • Only diffs that open in the user's tab. This PR does not change diffs that open in their own tab (open_in_new_tab, or a current tab with the user's diff).
  • The focus still moves in the unified own-vimdiff case: after close_tab the cursor is in the right-hand diff window, as on main. Not part of this item.

Blast Radius

  • Every diff close in the user's tab now resizes that tab's windows back to their sizes before the diff. A user who resized windows while the diff was open, without adding or closing windows, gets the old sizes back.
  • With diff_opts.auto_resize_terminal = false, the Claude terminal in that tab also gets its width from before the diff back, then ClaudeCodeDiffClosed fires as before, so a handler's resize still wins.
  • No change to how a diff resolves or answers Claude.

Verification

  1. Red/green, diff_window_sizes_spec.lua on Neovim 0.8.3, 0.9.5 and 0.12.3. With the two Lua files from main, the first four tests fail (3 on 0.8.3, where the unified one is pending). With the fix, all pass (7 on 0.8.3, 10 on 0.9.5 and 0.12.3). Each review follow-up test fails when its part of the fix is removed (the space check, the shared sizes, the restore in each setup-failure path), and the tab line and CTRL-W K tests fail on the round-2 head 5fd6e29.

  2. Window-choice matrix (the fix(diff): skip floating windows, give a user's diff tab a new tab, and keep the other tab's terminal width #410 verifier's harness: 22 layouts × vertical/horizontal/unified × open_in_new_tab 0/1 × 3 files × accept/deny, on 0.8.3, 0.9.5 and 0.12.3, 2112 runs). On main, the current tab's sizes differ after close_tab in 24 runs, all of them the two Follow-ups from #410-#415: flaky memory test, WebSocket close code, openFile known gap, lock conflicts #416 layouts. With the fix: 0. Before/after rows differ only in after_close of those 24 runs. Layout after open, answers, file texts and tab choice are the same.

  3. Resized nested layouts (a | (c over b) with a at 40 or 50 columns and c at 10 lines, with and without the Claude terminal, auto_resize_terminal on and off, 120 runs on 0.8.3 and 0.12.3): main loses the sizes in 120 of 120, the fix keeps them in 120 of 120.

  4. Live run with vcn.sh (Neovim in a real terminal, the WebSocket client plays the Claude CLI: openDiff, :w, close_tab):

    Case Before openDiff After close_tab on main d9987ba After close_tab with the fix
    horizontal, hello.txt resized, Claude terminal hello 30 | other 80 | TERM 48 hello 53 | other 57 | TERM 48 hello 30 | other 80 | TERM 48
    unified, user's vimdiff resized other 50 | third 109 other 79 | third 80 other 50 | third 109

    Horizontal case after close_tab, main and then the fix:

    horizontal case on main after close_tab
    horizontal case with the fix after close_tab

    Unified case after close_tab, main and then the fix:

    unified case on main after close_tab
    unified case with the fix after close_tab

  5. Gates on the head: mise run check (0 warnings), mise run test (1036 successes, 0 failures), scripts/run_integration_tests_individually.sh on 0.12.3 (73 passed) and 0.8.3 (59 passed), stylua --check, prettier --check, verify-diff-lifecycle/check.sh PASS (all facts TRUE; the one new call site only reads window_sizes from active_diffs). The matrices in steps 2 and 3 were rerun on 7be62ae with the same results.

Part of #416


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

A diff that opens in the user's tab runs `wincmd =`, which resizes every
window in that tab. Closing the diff's windows then shares their space out
by 'equalalways' rules, so the user's windows ended at other sizes: a
window resized to 30 columns came back 53 wide, and two editors or the
windows of the user's own vimdiff ended with their widths swapped by one
column (horizontal and unified layouts).

Both layouts now save the sizes of the tab's tiled windows before the diff
splits it, and set them again when the diff closes, if that tab still has
the same windows in the same order. The Claude terminal's width is handled
afterwards as before.

The own-diff-tab test's 'diff' loop passed with or without the new-tab fix
(the diff never touched the user's windows), so it is removed. The
created_new_tab and tab-return assertions keep guarding that case.

Part of #416

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-01T17:24:31.876803Z 7be62ae Manual request
🔒 Security Review ✅ Completed 2026-11-01T17:24:41.264316Z 7be62ae Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: e050fbaf4e

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e050fbaf4e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lua/claudecode/diff.lua Resolved
Comment thread lua/claudecode/diff.lua Resolved
Comment thread lua/claudecode/diff_inline.lua Resolved
…reen resizes, or setup fails

Review follow-ups for the window size restore:

- A diff that opens in a tab another open diff already split shares that
  diff's sizes, the ones from before the first diff. The last diff to
  close gives them back, in either close order. Before, closing the first
  diff first left the user's windows equalized.
- Nothing is resized when the screen size changed while the diff was
  open: sizes taken for the old screen do not fit the new one.
- A diff whose setup fails after it split the tab (for example a user's
  autocmd that errors) gives the sizes back too, in both the split and
  the unified layouts.

Part of #416

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 5fd6e29936

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5fd6e29936

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lua/claudecode/diff.lua Resolved
Comment thread lua/claudecode/diff.lua Resolved
Comment thread lua/claudecode/diff.lua Resolved
…oring sizes

Review follow-ups for the window size restore:

- The saved sizes now hold the tab's winlayout() tree, and the restore
  needs the same tree. A layout change that keeps the window handles and
  their order (A | B turned into A above B with CTRL-W K) no longer gets
  the old side-by-side sizes.
- The tree comes from winlayout(), which leaves out floating and external
  windows, so an external window (multigrid UI) neither joins the saved
  sizes nor blocks the restore.
- The restore compares the columns and rows the windows fill, counted
  over the tree, instead of 'columns' and 'lines'. A tab line that
  appears while the diff is open (a second tab with showtabline=1) takes
  a row, and the old heights are not forced back into the smaller space.

The busted vim mock gets winlayout() and vim.deep_equal(), which the
restore now calls.

Part of #416

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 7be62aef46

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 7be62aef46

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

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