From 0fd44a5ad81ade342cb54d8984965bdedd2272c8 Mon Sep 17 00:00:00 2001 From: Sean Dewar <6256228+seandewar@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:28:15 +0100 Subject: patch 9.1.0116: win_split_ins may not check available room Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt --- src/testdir/test_window_cmd.vim | 154 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 13 deletions(-) (limited to 'src/testdir') diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index 6b7dccbb00..c898a233af 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -290,6 +290,16 @@ func Test_window_split_no_room() for s in range(1, hor_split_count) | split | endfor call assert_fails('split', 'E36:') + botright vsplit + wincmd | + let layout = winlayout() + let restcmd = winrestcmd() + call assert_fails('wincmd J', 'E36:') + call assert_fails('wincmd K', 'E36:') + call assert_equal(layout, winlayout()) + call assert_equal(restcmd, winrestcmd()) + only + " N vertical windows need >= 2*(N - 1) + 1 columns: " - 1 column + 1 separator for each window (except last window) " - 1 column for the last window which does not have separator @@ -302,7 +312,35 @@ func Test_window_split_no_room() for s in range(1, ver_split_count) | vsplit | endfor call assert_fails('vsplit', 'E36:') + split + wincmd | + let layout = winlayout() + let restcmd = winrestcmd() + call assert_fails('wincmd H', 'E36:') + call assert_fails('wincmd L', 'E36:') + call assert_equal(layout, winlayout()) + call assert_equal(restcmd, winrestcmd()) + + " Check that the last statusline isn't lost. + set laststatus=0 + let restcmd = winrestcmd() + wincmd j + call setwinvar(winnr('k'), '&statusline', '@#') + let last_stl_row = win_screenpos(0)[0] - 1 + redraw + call assert_equal('@#|', GetScreenStr(last_stl_row)) + call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) + call assert_fails('wincmd H', 'E36:') + call assert_fails('wincmd L', 'E36:') + call assert_equal(layout, winlayout()) + call assert_equal(restcmd, winrestcmd()) + call setwinvar(winnr('k'), '&statusline', '=-') + redraw + call assert_equal('=-|', GetScreenStr(last_stl_row)) + call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) + %bw! + set laststatus& endfunc func Test_window_exchange() @@ -1097,6 +1135,44 @@ func Test_win_splitmove() tabnew call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:') tabclose + + split + augroup WinSplitMove + au! + au WinEnter * ++once call win_gotoid(win_getid(winnr('#'))) + augroup END + call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:') + + augroup WinSplitMove + au! + au WinLeave * ++once quit + augroup END + call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:') + + split + split + augroup WinSplitMove + au! + au WinEnter * ++once let s:triggered = v:true + \| call assert_fails('call win_splitmove(winnr("$"), winnr())', 'E242:') + augroup END + quit + call assert_equal(v:true, s:triggered) + unlet! s:triggered + + new + augroup WinSplitMove + au! + au BufHidden * ++once let s:triggered = v:true + \| call assert_fails('call win_splitmove(winnr("#"), winnr())', 'E1159:') + augroup END + hide + call assert_equal(v:true, s:triggered) + unlet! s:triggered + + au! WinSplitMove + augroup! WinSplitMove + %bw! endfunc " Test for the :only command @@ -2061,23 +2137,75 @@ func Test_new_help_window_on_error() endfunc func Test_smoothscroll_in_zero_width_window() - let save_lines = &lines - let save_columns = &columns + set cpo+=n number smoothscroll + set winwidth=99999 winminwidth=0 - winsize 0 24 - set cpo+=n - exe "noremap 0 \n\L" - norm 000000 - set number smoothscroll - exe "norm \" + vsplit + call assert_equal(0, winwidth(winnr('#'))) + call win_execute(win_getid(winnr('#')), "norm! \") + + only! + set winwidth& winminwidth& + set cpo-=n nonumber nosmoothscroll +endfunc + +func Test_splitmove_flatten_frame() + split + vsplit + + wincmd L + let layout = winlayout() + wincmd K + wincmd L + call assert_equal(winlayout(), layout) only! - let &lines = save_lines - let &columns = save_columns - set cpo-=n - unmap 0 - set nonumber nosmoothscroll endfunc +func Test_splitmove_autocmd_window_no_room() + " Open as many windows as possible + while v:true + try + split + catch /E36:/ + break + endtry + endwhile + while v:true + try + vsplit + catch /E36:/ + break + endtry + endwhile + + wincmd j + vsplit + call assert_fails('wincmd H', 'E36:') + call assert_fails('wincmd J', 'E36:') + call assert_fails('wincmd K', 'E36:') + call assert_fails('wincmd L', 'E36:') + + edit unload me + enew + bunload! unload\ me + augroup SplitMoveAucmdWin + au! + au BufEnter * ++once let s:triggered = v:true + \| call assert_equal('autocmd', win_gettype()) + augroup END + let layout = winlayout() + let restcmd = winrestcmd() + " bufload opening the autocommand window shouldn't give E36. + call bufload('unload me') + call assert_equal(v:true, s:triggered) + call assert_equal(winlayout(), layout) + call assert_equal(winrestcmd(), restcmd) + + unlet! s:triggered + au! SplitMoveAucmdWin + augroup! SplitMoveAucmdWin + %bw! +endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit v1.2.3