commit 0d658660c29e920e74c4dade3819d80dccad0dde
parent c7f38e3bc8fb99a490a0575ed7ce0624bd7b975a
Author: luukvbaal <luukvbaal@gmail.com>
Date: Sat, 14 Jun 2025 23:42:23 +0200
fix(window): don't enter unfocusable or hidden prevwin (#34486)
Problem: When closing a floating window, the next window to be entered
may be unfocusable or hidden.
Solution: Don't enter prevwin when it is unfocusable or hidden. Enter
firstwin instead (like for when prevwin is no longer valid).
Diffstat:
3 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/src/nvim/window.c b/src/nvim/window.c
@@ -2753,15 +2753,7 @@ int win_close(win_T *win, bool free_buf, bool force)
// Guess which window is going to be the new current window.
// This may change because of the autocommands (sigh).
- if (!win->w_floating) {
- wp = frame2win(win_altframe(win, NULL));
- } else {
- if (win_valid(prevwin) && prevwin != win) {
- wp = prevwin;
- } else {
- wp = firstwin;
- }
- }
+ wp = win->w_floating ? win_float_find_altwin(win, NULL) : frame2win(win_altframe(win, NULL));
// Be careful: If autocommands delete the window or cause this window
// to be the last one left, return now.
diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c
@@ -362,13 +362,15 @@ win_T *win_float_find_preview(void)
win_T *win_float_find_altwin(const win_T *win, const tabpage_T *tp)
FUNC_ATTR_NONNULL_ARG(1)
{
+ win_T *wp = prevwin;
if (tp == NULL) {
- return (win_valid(prevwin) && prevwin != win) ? prevwin : firstwin;
+ return (win_valid(wp) && wp != win && wp->w_config.focusable
+ && !wp->w_config.hide) ? wp : firstwin;
}
assert(tp != curtab);
- return (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) ? tp->tp_prevwin
- : tp->tp_firstwin;
+ wp = tabpage_win_valid(tp, tp->tp_prevwin) ? tp->tp_prevwin : tp->tp_firstwin;
+ return (wp->w_config.focusable && !wp->w_config.hide) ? wp : tp->tp_firstwin;
}
/// Inline helper function for handling errors and cleanup in win_float_create.
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
@@ -730,6 +730,32 @@ describe('API/win', function()
eq(prevwin, api.nvim_tabpage_get_win(tab))
assert_alive()
end)
+
+ it('closing a float does not enter unfocusable or hidden prevwin', function()
+ local firstwin = api.nvim_get_current_win()
+ local wins = {} ---@type integer[]
+ for _ = 1, 4 do
+ wins[#wins + 1] = api.nvim_open_win(0, true, {
+ relative = 'editor',
+ row = 10,
+ col = 10,
+ width = 50,
+ height = 10,
+ })
+ end
+ api.nvim_win_set_config(wins[3], { hide = true })
+ api.nvim_win_close(0, false)
+ eq(firstwin, api.nvim_get_current_win())
+ api.nvim_set_current_win(wins[2])
+ api.nvim_set_current_win(wins[3])
+ api.nvim_win_set_config(wins[2], { focusable = false })
+ api.nvim_win_close(0, false)
+ eq(firstwin, api.nvim_get_current_win())
+ api.nvim_set_current_win(wins[1])
+ api.nvim_set_current_win(wins[2])
+ api.nvim_win_close(0, false)
+ eq(wins[1], api.nvim_get_current_win())
+ end)
end)
describe('hide', function()