diff --git a/lua/zen/init.lua b/lua/zen/init.lua index 03dc5ab..2789ba3 100644 --- a/lua/zen/init.lua +++ b/lua/zen/init.lua @@ -281,22 +281,30 @@ local function reposition_stack(position) heights[win] = vim.api.nvim_win_get_height(win) end - if position == "top" then - vim.api.nvim_win_set_config(windows[1], { split = "above", win = -1 }) - for i = 2, #windows do - vim.api.nvim_win_set_config(windows[i], { split = "below", win = windows[i - 1] }) - end - else - vim.api.nvim_win_set_config(windows[#windows], { split = "below", win = -1 }) - for i = #windows - 1, 1, -1 do - vim.api.nvim_win_set_config(windows[i], { split = "above", win = windows[i + 1] }) + -- Some Neovim versions raise `E242: Can't split a window while closing + -- another` when this runs from a handler that fires midway through a window + -- close (e.g. `WinClosed` recreating a side buffer, whose `BufWinEnter` + -- reaches here before the original close has unwound). Guard the splits so + -- that refusal cannot abort the surrounding close handler; the reposition is + -- re-run by that handler once the offending close has settled. + pcall(function() + if position == "top" then + vim.api.nvim_win_set_config(windows[1], { split = "above", win = -1 }) + for i = 2, #windows do + vim.api.nvim_win_set_config(windows[i], { split = "below", win = windows[i - 1] }) + end + else + vim.api.nvim_win_set_config(windows[#windows], { split = "below", win = -1 }) + for i = #windows - 1, 1, -1 do + vim.api.nvim_win_set_config(windows[i], { split = "above", win = windows[i + 1] }) + end end - end - for _, win in ipairs(windows) do - vim.api.nvim_win_set_height(win, heights[win]) - vim.api.nvim_win_set_width(win, vim.o.columns) - end + for _, win in ipairs(windows) do + vim.api.nvim_win_set_height(win, heights[win]) + vim.api.nvim_win_set_width(win, vim.o.columns) + end + end) end ---@param position "top" | "bottom" @@ -369,6 +377,9 @@ local function setup(options) vim.api.nvim_create_autocmd("CursorMoved", { -- TODO: use pattern for better perf callback = function(args) + if vim.o.columns <= get_main_width() then + return + end if is_buff_integration(args.buf) then local buf_info = vim.fn.getbufinfo(args.buf) diff --git a/tests/scripts/init_with_zen_small.lua b/tests/scripts/init_with_zen_small.lua index 54d6665..b56fbb9 100644 --- a/tests/scripts/init_with_zen_small.lua +++ b/tests/scripts/init_with_zen_small.lua @@ -9,18 +9,8 @@ require("trouble").setup({ open_no_results = true }) require("fyler").setup({}) require("neotest").setup({ adapters = {} }) require("zen").setup({ - top = { - { filetype = "fugitive" }, - }, - bottom = { - { filetype = "trouble" }, - }, - left = { - { filetype = "*", min_width = 46 }, - { filetype = "fyler_finder" }, - }, - right = { - { filetype = "*", min_width = 46 }, - { filetype = "neotest-summary" }, - }, + top = { { filetype = "fugitive" } }, + bottom = { { filetype = "trouble" } }, + left = { { filetype = "fyler_finder" } }, + right = { { filetype = "neotest-summary" } }, }) diff --git a/tests/test_integrations.lua b/tests/test_integrations.lua index 0382943..e342da3 100644 --- a/tests/test_integrations.lua +++ b/tests/test_integrations.lua @@ -61,6 +61,20 @@ T["left integration"]["opening an integration should close the existing integrat }) end +T["left integration"]["opening an integration on a small window"] = function() + child.restart({ "-u", "tests/scripts/init_with_zen_small.lua" }) + + child.cmd("Fyler kind=split_left_most") + + Helpers.expect.layout(child, { + type = "row", + children = { + { type = "leaf", filetype = "fyler_finder", buftype = "acwrite", width = 35, height = 50 }, + { type = "leaf", filetype = "", buftype = "", width = 104, height = 50 }, + }, + }) +end + T["top integration"] = MiniTest.new_set({}) T["top integration"]["opening"] = function() @@ -202,6 +216,46 @@ for _, case in ipairs({ { name = "above", }, { name = "below", } }) do end end +T["top integration"]["closing a git commit keeps the top and bottom stacks intact"] = function() + -- Regression: creating a commit in fugitive and then closing the gitcommit + -- window used to abort the reposition handlers with `E242: Can't split a + -- window while closing another`, because recreating a side buffer on + -- `WinClosed` re-enters `reposition_stack` mid-close. The close must leave + -- the fugitive (top) and trouble (bottom) stacks intact instead. + child.lua([[ + local tmpdir = vim.fn.tempname() + vim.fn.mkdir(tmpdir, "p") + vim.fn.system({ "git", "init", tmpdir }) + vim.fn.system({ "git", "-C", tmpdir, "config", "user.name", "Test" }) + vim.fn.system({ "git", "-C", tmpdir, "config", "user.email", "test@test.com" }) + vim.fn.system({ "git", "-C", tmpdir, "commit", "--allow-empty", "-m", "initial" }) + vim.fn.writefile({ "hello" }, tmpdir .. "/file") + vim.cmd("edit " .. tmpdir .. "/file") + ]]) + + child.cmd("Git") + child.cmd("Trouble diagnostics") + child.cmd("wincmd t") + child.cmd("Git commit --allow-empty") + child.cmd("q") + + Helpers.expect.layout(child, { + type = "col", + children = { + { type = "leaf", filetype = "fugitive", buftype = "nowrite", width = 240, height = 25 }, + { + type = "row", + children = { + { type = "leaf", filetype = "zen-left", buftype = "nofile", width = 46, height = 13 }, + { type = "leaf", filetype = "", buftype = "", width = 146, height = 13 }, + { type = "leaf", filetype = "zen-right", buftype = "nofile", width = 46, height = 13 }, + }, + }, + { type = "leaf", filetype = "trouble", buftype = "nofile", width = 240, height = 10 }, + }, + }) +end + T["bottom integration"] = MiniTest.new_set({}) T["bottom integration"]["opening"] = function()