From 403fcacfc16fb3a1fbb4be22f77389e0f0616a6e Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Sat, 3 May 2025 19:30:24 +0100 Subject: [PATCH] fix(window): skip unfocusable and hidden floats with "{count}w" #33810 Problem: Using `w`, `W` or the ":wincmd" variants with a count can enter unfocusable or hidden floating windows. This is especially problematic when using the new in-development extui, which creates many unfocusable floats for various UI elements. Solution: Skip unfocusable and hidden floating windows. Instead, skip to the next focusable, non-hidden window in the current tabpage's window list. Reword the documentation a bit (hopefully an improvement?) --- runtime/doc/windows.txt | 17 +++++----- src/nvim/window.c | 11 +++++++ test/functional/ui/float_spec.lua | 53 +++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 5d278d7645..8c5c203d39 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -439,18 +439,17 @@ CTRL-W l Move cursor to Nth window right of current one. Uses the CTRL-W w *CTRL-W_w* *CTRL-W_CTRL-W* CTRL-W CTRL-W Without count: move cursor to the |focusable| window - below/right of the current one. If there is no (focusable) - window below or right, go to top-left window. With count: go - to Nth window (windows are numbered from top-left to - bottom-right). To obtain the window number see |bufwinnr()| - and |winnr()|. When N is larger than the number of windows go - to the last window. + below/right of the current one. If none, go to the top-left + window. With count: go to Nth window (numbered top-left to + bottom-right), skipping unfocusable windows. To obtain the + window number see |bufwinnr()| and |winnr()|. When N is + larger than the number of windows go to the last focusable + window. *CTRL-W_W* CTRL-W W Without count: move cursor to the |focusable| window - above/left of current one. If there is no window above or - left, go to bottom-right window. With count: go to Nth - window, like with CTRL-W w. + above/left of current one. If none, go to the bottom-right + window. With count: go to Nth window, like CTRL-W w. CTRL-W t *CTRL-W_t* *CTRL-W_CTRL-T* CTRL-W CTRL-T Move cursor to top-left window. diff --git a/src/nvim/window.c b/src/nvim/window.c index 017d41ddda..6c20d99e94 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -348,12 +348,23 @@ newwindow: } else { win_T *wp; if (Prenum) { // go to specified window + win_T *last_focusable = firstwin; for (wp = firstwin; --Prenum > 0;) { + if (!wp->w_floating || (!wp->w_config.hide && wp->w_config.focusable)) { + last_focusable = wp; + } if (wp->w_next == NULL) { break; } wp = wp->w_next; } + while (wp != NULL && wp->w_floating + && (wp->w_config.hide || !wp->w_config.focusable)) { + wp = wp->w_next; + } + if (wp == NULL) { // went past the last focusable window + wp = last_focusable; + } } else { if (nchar == 'W') { // go to previous window wp = curwin->w_prev; diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 13bc1317ef..bc2733d74f 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -5846,6 +5846,59 @@ describe('float window', function() | ]]) end + + api.nvim_open_win( + 0, + false, + { relative = "editor", width = 1, height = 1, row = 0, col = 0 } + ) + api.nvim_open_win( + 0, + false, + { relative = "editor", width = 1, height = 1, row = 0, col = 0, focusable = false } + ) + api.nvim_open_win( + 0, + false, + { relative = "editor", width = 1, height = 1, row = 0, col = 0, focusable = false } + ) + api.nvim_open_win( + 0, + false, + { relative = "editor", width = 1, height = 1, row = 0, col = 0, focusable = true } + ) + api.nvim_open_win( + 0, + false, + { relative = "editor", width = 1, height = 1, row = 0, col = 0, focusable = false } + ) + local nr_focusable = {} + for nr = 1, fn.winnr("$") do + table.insert(nr_focusable, api.nvim_win_get_config(fn.win_getid(nr)).focusable) + end + eq({true, false, true, false, false, true, false}, nr_focusable) + + command("1wincmd w") + eq(1, fn.winnr()) + command("2wincmd w") + eq(3, fn.winnr()) + command("3wincmd w") + eq(3, fn.winnr()) + command("4wincmd w") + eq(6, fn.winnr()) + command("5wincmd w") + eq(6, fn.winnr()) + command("6wincmd w") + eq(6, fn.winnr()) + command("7wincmd w") + eq(6, fn.winnr()) + + feed("1w") + eq(1, fn.winnr()) + feed("2w") + eq(3, fn.winnr()) + feed("999w") + eq(6, fn.winnr()) end) it("W", function()