|
| 1 | +-- Fixture for issue #277: |
| 2 | +-- "[BUG] closeAllDiffTabs closes all diff-mode windows, destroying unrelated |
| 3 | +-- diffs (diffview.nvim)" |
| 4 | +-- https://github.com/coder/claudecode.nvim/issues/277 |
| 5 | +-- |
| 6 | +-- Two defects under test: |
| 7 | +-- 1. tools/close_all_diff_tabs.lua closes EVERY `&diff` window (no ownership |
| 8 | +-- check), so diffview.nvim / fugitive / native `:diffthis` layouts are |
| 9 | +-- destroyed when the Claude CLI fires closeAllDiffTabs (it does so at the |
| 10 | +-- start of a user turn whenever an IDE is connected). |
| 11 | +-- 2. find_main_editor_window (tools/open_file.lua and diff.lua) does not |
| 12 | +-- exclude `&diff` windows, so openFile/openDiff target a diffview window |
| 13 | +-- and :edit into it, corrupting the diff layout. |
| 14 | +-- |
| 15 | +-- The fixture pulls in diffview.nvim (cloned on first run) and exposes a |
| 16 | +-- window-state probe for scripted verification: |
| 17 | +-- nvim --server <sock> --remote-expr 'v:lua.Repro277State()' |
| 18 | +-- |
| 19 | +-- Usage (from repo root): |
| 20 | +-- source fixtures/nvim-aliases.sh && vv issue-277 |
| 21 | +-- or scripted: |
| 22 | +-- scripts/repro_issue_277.sh |
| 23 | +-- |
| 24 | +-- Manual repro: open a file in a git repo with uncommitted changes, |
| 25 | +-- :DiffviewOpen, connect claude (--ide), submit any prompt -> the side-by-side |
| 26 | +-- diff windows close, only the Diffview file panel survives. |
| 27 | + |
| 28 | +local config_dir = vim.fn.stdpath("config") |
| 29 | +local repo_root = vim.fn.fnamemodify(config_dir, ":h:h") |
| 30 | +vim.opt.rtp:prepend(repo_root) |
| 31 | + |
| 32 | +vim.g.mapleader = " " |
| 33 | +vim.g.maplocalleader = "\\" |
| 34 | + |
| 35 | +-- --------------------------------------------------------------------------- |
| 36 | +-- diffview.nvim (cloned into stdpath("data") on first run; no plugin manager) |
| 37 | +-- --------------------------------------------------------------------------- |
| 38 | +local diffview_dir = vim.fn.stdpath("data") .. "/diffview.nvim" |
| 39 | +if vim.fn.isdirectory(diffview_dir) == 0 then |
| 40 | + vim.notify("issue-277 fixture: cloning diffview.nvim ...") |
| 41 | + local out = vim.fn.system({ |
| 42 | + "git", |
| 43 | + "clone", |
| 44 | + "--depth=1", |
| 45 | + "https://github.com/sindrets/diffview.nvim", |
| 46 | + diffview_dir, |
| 47 | + }) |
| 48 | + assert(vim.v.shell_error == 0, "failed to clone diffview.nvim: " .. out) |
| 49 | +end |
| 50 | +vim.opt.rtp:prepend(diffview_dir) |
| 51 | + |
| 52 | +local ok_dv, diffview = pcall(require, "diffview") |
| 53 | +assert(ok_dv, "Failed to load diffview.nvim: " .. tostring(diffview)) |
| 54 | +diffview.setup({}) |
| 55 | + |
| 56 | +-- --------------------------------------------------------------------------- |
| 57 | +-- claudecode.nvim (dev version from this repo) |
| 58 | +-- --------------------------------------------------------------------------- |
| 59 | +local ok, claudecode = pcall(require, "claudecode") |
| 60 | +assert(ok, "Failed to load claudecode.nvim from repo root: " .. tostring(claudecode)) |
| 61 | + |
| 62 | +claudecode.setup({ |
| 63 | + auto_start = true, -- server + lock file immediately, so scripts can connect |
| 64 | + -- "warn", not "debug": multi-line debug echoes trip nvim's hit-enter prompt, |
| 65 | + -- which blocks --remote-expr probes in the scripted repro. |
| 66 | + log_level = "warn", |
| 67 | + terminal = { |
| 68 | + provider = "native", |
| 69 | + auto_close = false, |
| 70 | + }, |
| 71 | +}) |
| 72 | + |
| 73 | +vim.o.showtabline = 2 |
| 74 | +vim.o.laststatus = 2 |
| 75 | + |
| 76 | +-- --------------------------------------------------------------------------- |
| 77 | +-- Window-state probe (for --remote-expr / on-screen verification) |
| 78 | +-- --------------------------------------------------------------------------- |
| 79 | + |
| 80 | +---Compact state of every window across all tabpages. |
| 81 | +---@return string JSON: [{win,tab,name,buftype,filetype,diff}...] |
| 82 | +function _G.Repro277State() |
| 83 | + local out = {} |
| 84 | + for _, win in ipairs(vim.api.nvim_list_wins()) do |
| 85 | + local buf = vim.api.nvim_win_get_buf(win) |
| 86 | + local name = vim.api.nvim_buf_get_name(buf) |
| 87 | + out[#out + 1] = { |
| 88 | + win = win, |
| 89 | + tab = vim.api.nvim_tabpage_get_number(vim.api.nvim_win_get_tabpage(win)), |
| 90 | + name = vim.fn.fnamemodify(name, ":t") ~= "" and vim.fn.fnamemodify(name, ":~:.") or "[No Name]", |
| 91 | + buftype = vim.bo[buf].buftype, |
| 92 | + filetype = vim.bo[buf].filetype, |
| 93 | + diff = vim.wo[win].diff, |
| 94 | + } |
| 95 | + end |
| 96 | + return vim.json.encode(out) |
| 97 | +end |
| 98 | + |
| 99 | +---WebSocket endpoint of the running claudecode server ("port token", or "" if |
| 100 | +---not started yet). Lets scripts connect without scanning ~/.claude/ide. |
| 101 | +---@return string |
| 102 | +function _G.Repro277Server() |
| 103 | + local cc = require("claudecode") |
| 104 | + if cc.state.port and cc.state.auth_token then |
| 105 | + return cc.state.port .. " " .. cc.state.auth_token |
| 106 | + end |
| 107 | + return "" |
| 108 | +end |
| 109 | + |
| 110 | +---Count of windows currently in diff mode (quick assertion helper). |
| 111 | +---@return integer |
| 112 | +function _G.Repro277DiffWinCount() |
| 113 | + local n = 0 |
| 114 | + for _, win in ipairs(vim.api.nvim_list_wins()) do |
| 115 | + if vim.wo[win].diff then |
| 116 | + n = n + 1 |
| 117 | + end |
| 118 | + end |
| 119 | + return n |
| 120 | +end |
| 121 | + |
| 122 | +vim.api.nvim_create_user_command("ReproState", function() |
| 123 | + vim.notify(_G.Repro277State()) |
| 124 | +end, { desc = "Show issue-277 window state" }) |
| 125 | + |
| 126 | +-- Native (plugin-free) diff variant of the same bug: two `:diffsplit` windows. |
| 127 | +vim.api.nvim_create_user_command("ReproNativeDiff", function(cmd_opts) |
| 128 | + local args = vim.split(cmd_opts.args, "%s+") |
| 129 | + assert(#args == 2, "usage: :ReproNativeDiff <file_a> <file_b>") |
| 130 | + vim.cmd("edit " .. vim.fn.fnameescape(args[1])) |
| 131 | + vim.cmd("vertical diffsplit " .. vim.fn.fnameescape(args[2])) |
| 132 | +end, { nargs = "+", complete = "file", desc = "Open a native vimdiff of two files" }) |
| 133 | + |
| 134 | +vim.keymap.set("n", "<leader>aw", function() |
| 135 | + vim.notify(("diff windows: %d"):format(_G.Repro277DiffWinCount())) |
| 136 | +end, { desc = "Show diff window count" }) |
0 commit comments