From e750f7c357f335b186ceed1063d5aee1d0a762c0 Mon Sep 17 00:00:00 2001 From: Shiva Priyan Date: Mon, 22 Jun 2026 21:13:18 -0400 Subject: [PATCH] vim-patch:9.2.0702: :windo and :tabdo create an extra window with 'winfixbuf' (#40358) Problem: With 'winfixbuf' set in the current window, :windo and :tabdo create an extra split window, even though they only visit existing windows/tabpages and don't change the current window's buffer (Collin Kennedy) Solution: Skip the 'winfixbuf' escape in ex_listdo() for :windo and :tabdo (ShivaPriyanShanmuga) fixes: vim/vim#14301 closes: vim/vim#20600 https://github.com/vim/vim/commit/5767d80b3794b17e5b61afec966fdeb7c0c4506f --- src/nvim/ex_cmds2.c | 5 +++- test/old/testdir/test_winfixbuf.vim | 41 +++++++++++++++++++---------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index f90cf0d144..b34d0326ce 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -459,7 +459,10 @@ int buf_write_all(buf_T *buf, bool forceit) /// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" void ex_listdo(exarg_T *eap) { - if (curwin->w_p_wfb) { + // ":windo" and ":tabdo" only visit existing windows/tabpages, they don't + // change the current window's buffer, so they can't escape a 'winfixbuf' + // window (which would create a split). + if (curwin->w_p_wfb && eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) { if ((eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) && !eap->forceit) { // Disallow :ldo if 'winfixbuf' is applied emsg(_(e_winfixbuf_cannot_go_to_buffer)); diff --git a/test/old/testdir/test_winfixbuf.vim b/test/old/testdir/test_winfixbuf.vim index a040b3f2da..c9a557b1c5 100644 --- a/test/old/testdir/test_winfixbuf.vim +++ b/test/old/testdir/test_winfixbuf.vim @@ -2860,36 +2860,34 @@ func Test_tNext() set tags& endfunc -" Call :tabdo and choose the next available 'nowinfixbuf' window. -func Test_tabdo_choose_available_window() +" Call :tabdo and stay in the 'winfixbuf' window: it only visits tabpages and +" doesn't change the current buffer, so it must not switch to another window +" even when a 'nowinfixbuf' window is available. +func Test_tabdo_stay_in_winfixbuf_window() call s:reset_all_buffers() let [l:first, _] = s:make_args_list() - " Make a split window that is 'nowinfixbuf' but make it the second-to-last - " window so that :tabdo will first try the 'winfixbuf' window, pass over it, - " and prefer the other 'nowinfixbuf' window, instead. - " " +-------------------+ " | 'nowinfixbuf' | " +-------------------+ " | 'winfixbuf' | <-- Cursor is here " +-------------------+ split - let l:nowinfixbuf_window = win_getid() " Move to the 'winfixbuf' window now exe "normal \j" let l:winfixbuf_window = win_getid() let l:expected_windows = s:get_windows_count() tabdo echo '' - call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:winfixbuf_window, win_getid()) call assert_equal(l:first, bufnr()) call assert_equal(l:expected_windows, s:get_windows_count()) endfunc -" Call :tabdo and create a new split window if all available windows are 'winfixbuf'. -func Test_tabdo_make_new_window() +" Call :tabdo and do not create a new window even when the only window is +" 'winfixbuf'. +func Test_tabdo_no_new_window() call s:reset_all_buffers() let [l:first, _] = s:make_buffers_list() @@ -2899,11 +2897,9 @@ func Test_tabdo_make_new_window() let l:current_windows = s:get_windows_count() tabdo echo '' - call assert_notequal(l:current, win_getid()) + call assert_equal(l:current, win_getid()) call assert_equal(l:first, bufnr()) - exe "normal \j" - call assert_equal(l:first, bufnr()) - call assert_equal(l:current_windows + 1, s:get_windows_count()) + call assert_equal(l:current_windows, s:get_windows_count()) endfunc " Fail :tag but :tag! is allowed @@ -3217,6 +3213,23 @@ func Test_windo() call assert_equal(l:current_window, win_getid()) endfunc +" Call :windo and do not create a new window even when the only window is +" 'winfixbuf'. +func Test_windo_no_new_window() + call s:reset_all_buffers() + + let [l:first, _] = s:make_buffers_list() + exe $"buffer! {l:first}" + + let l:current = win_getid() + let l:current_windows = s:get_windows_count() + + windo echo '' + call assert_equal(l:current, win_getid()) + call assert_equal(l:first, bufnr()) + call assert_equal(l:current_windows, s:get_windows_count()) +endfunc + " Fail :wnext but :wnext! is allowed func Test_wnext() call s:reset_all_buffers()