aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorSean Dewar <6256228+seandewar@users.noreply.github.com>2024-03-09 22:32:20 +0000
committerGitHub <noreply@github.com>2024-03-09 22:32:20 +0000
commitb596732831b5e947ce83c1153f0df10a0553c88d (patch)
tree0a6e874de0ba7fb9eb3ef71d89516281731401e7 /test
parentade1b12f49c3b3914c74847d791eb90ea90b56b7 (diff)
parentc3d22d32ee4b4c1911ec15f2a77683d09b09f845 (diff)
downloadrneovim-b596732831b5e947ce83c1153f0df10a0553c88d.tar.gz
rneovim-b596732831b5e947ce83c1153f0df10a0553c88d.tar.bz2
rneovim-b596732831b5e947ce83c1153f0df10a0553c88d.zip
Merge pull request #27330 from seandewar/win_set_config-fixes
fix(api): various window-related function fixes This is a big one!
Diffstat (limited to 'test')
-rw-r--r--test/functional/api/tabpage_spec.lua26
-rw-r--r--test/functional/api/window_spec.lua779
-rw-r--r--test/functional/lua/vim_spec.lua14
-rw-r--r--test/functional/ui/float_spec.lua86
-rw-r--r--test/functional/ui/statusline_spec.lua20
-rw-r--r--test/old/testdir/test_execute_func.vim22
-rw-r--r--test/old/testdir/test_scroll_opt.vim38
-rw-r--r--test/old/testdir/test_window_cmd.vim216
8 files changed, 1177 insertions, 24 deletions
diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua
index 36955c4ace..f7e6eed047 100644
--- a/test/functional/api/tabpage_spec.lua
+++ b/test/functional/api/tabpage_spec.lua
@@ -1,5 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
+local exec = helpers.exec
+local feed = helpers.feed
local api = helpers.api
local fn = helpers.fn
local request = helpers.request
@@ -86,6 +88,30 @@ describe('api/tabpage', function()
pcall_err(api.nvim_tabpage_set_win, tab1, win3)
)
end)
+
+ it('does not switch window when textlocked or in the cmdwin', function()
+ local target_win = api.nvim_get_current_win()
+ feed('q:')
+ local cur_win = api.nvim_get_current_win()
+ eq(
+ 'Vim:E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
+ pcall_err(api.nvim_tabpage_set_win, 0, target_win)
+ )
+ eq(cur_win, api.nvim_get_current_win())
+ command('quit!')
+
+ exec(([[
+ new
+ call setline(1, 'foo')
+ setlocal debug=throw indentexpr=nvim_tabpage_set_win(0,%d)
+ ]]):format(target_win))
+ cur_win = api.nvim_get_current_win()
+ eq(
+ 'Vim(normal):E5555: API call: Vim:E565: Not allowed to change text or change window',
+ pcall_err(command, 'normal! ==')
+ )
+ eq(cur_win, api.nvim_get_current_win())
+ end)
end)
describe('{get,set,del}_var', function()
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 097a546ef2..a10c8f48ef 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -1364,6 +1364,308 @@ describe('API/win', function()
},
}, layout)
end)
+
+ local function setup_tabbed_autocmd_test()
+ local info = {}
+ info.orig_buf = api.nvim_get_current_buf()
+ info.other_buf = api.nvim_create_buf(true, true)
+ info.tab1_curwin = api.nvim_get_current_win()
+ info.tab1 = api.nvim_get_current_tabpage()
+ command('tab split | split')
+ info.tab2_curwin = api.nvim_get_current_win()
+ info.tab2 = api.nvim_get_current_tabpage()
+ exec([=[
+ tabfirst
+ let result = []
+ autocmd TabEnter * let result += [["TabEnter", nvim_get_current_tabpage()]]
+ autocmd TabLeave * let result += [["TabLeave", nvim_get_current_tabpage()]]
+ autocmd WinEnter * let result += [["WinEnter", win_getid()]]
+ autocmd WinLeave * let result += [["WinLeave", win_getid()]]
+ autocmd WinNew * let result += [["WinNew", win_getid()]]
+ autocmd WinClosed * let result += [["WinClosed", str2nr(expand("<afile>"))]]
+ autocmd BufEnter * let result += [["BufEnter", win_getid(), bufnr()]]
+ autocmd BufLeave * let result += [["BufLeave", win_getid(), bufnr()]]
+ autocmd BufWinEnter * let result += [["BufWinEnter", win_getid(), bufnr()]]
+ autocmd BufWinLeave * let result += [["BufWinLeave", win_getid(), bufnr()]]
+ ]=])
+ return info
+ end
+
+ it('fires expected autocmds when creating splits without entering', function()
+ local info = setup_tabbed_autocmd_test()
+
+ -- For these, don't want BufWinEnter if visiting the same buffer, like :{s}buffer.
+ -- Same tabpage, same buffer.
+ local new_win = api.nvim_open_win(0, false, { split = 'left', win = info.tab1_curwin })
+ eq({
+ { 'WinNew', new_win },
+ }, eval('result'))
+ eq(info.tab1_curwin, api.nvim_get_current_win())
+
+ -- Other tabpage, same buffer.
+ command('let result = []')
+ new_win = api.nvim_open_win(0, false, { split = 'left', win = info.tab2_curwin })
+ eq({
+ { 'WinNew', new_win },
+ }, eval('result'))
+ eq(info.tab1_curwin, api.nvim_get_current_win())
+
+ -- Same tabpage, other buffer.
+ command('let result = []')
+ new_win = api.nvim_open_win(info.other_buf, false, { split = 'left', win = info.tab1_curwin })
+ eq({
+ { 'WinNew', new_win },
+ { 'BufWinEnter', new_win, info.other_buf },
+ }, eval('result'))
+ eq(info.tab1_curwin, api.nvim_get_current_win())
+
+ -- Other tabpage, other buffer.
+ command('let result = []')
+ new_win = api.nvim_open_win(info.other_buf, false, { split = 'left', win = info.tab2_curwin })
+ eq({
+ { 'WinNew', new_win },
+ { 'BufWinEnter', new_win, info.other_buf },
+ }, eval('result'))
+ eq(info.tab1_curwin, api.nvim_get_current_win())
+ end)
+
+ it('fires expected autocmds when creating and entering splits', function()
+ local info = setup_tabbed_autocmd_test()
+
+ -- Same tabpage, same buffer.
+ local new_win = api.nvim_open_win(0, true, { split = 'left', win = info.tab1_curwin })
+ eq({
+ { 'WinNew', new_win },
+ { 'WinLeave', info.tab1_curwin },
+ { 'WinEnter', new_win },
+ }, eval('result'))
+
+ -- Same tabpage, other buffer.
+ api.nvim_set_current_win(info.tab1_curwin)
+ command('let result = []')
+ new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab1_curwin })
+ eq({
+ { 'WinNew', new_win },
+ { 'WinLeave', info.tab1_curwin },
+ { 'WinEnter', new_win },
+ { 'BufLeave', new_win, info.orig_buf },
+ { 'BufEnter', new_win, info.other_buf },
+ { 'BufWinEnter', new_win, info.other_buf },
+ }, eval('result'))
+
+ -- For these, the other tabpage's prevwin and curwin will change like we switched from its old
+ -- curwin to the new window, so the extra events near TabEnter reflect that.
+ -- Other tabpage, same buffer.
+ api.nvim_set_current_win(info.tab1_curwin)
+ command('let result = []')
+ new_win = api.nvim_open_win(0, true, { split = 'left', win = info.tab2_curwin })
+ eq({
+ { 'WinNew', new_win },
+ { 'WinLeave', info.tab1_curwin },
+ { 'TabLeave', info.tab1 },
+
+ { 'WinEnter', info.tab2_curwin },
+ { 'TabEnter', info.tab2 },
+ { 'WinLeave', info.tab2_curwin },
+ { 'WinEnter', new_win },
+ }, eval('result'))
+
+ -- Other tabpage, other buffer.
+ api.nvim_set_current_win(info.tab2_curwin)
+ api.nvim_set_current_win(info.tab1_curwin)
+ command('let result = []')
+ new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab2_curwin })
+ eq({
+ { 'WinNew', new_win },
+ { 'WinLeave', info.tab1_curwin },
+ { 'TabLeave', info.tab1 },
+
+ { 'WinEnter', info.tab2_curwin },
+ { 'TabEnter', info.tab2 },
+ { 'WinLeave', info.tab2_curwin },
+ { 'WinEnter', new_win },
+
+ { 'BufLeave', new_win, info.orig_buf },
+ { 'BufEnter', new_win, info.other_buf },
+ { 'BufWinEnter', new_win, info.other_buf },
+ }, eval('result'))
+
+ -- Other tabpage, other buffer; but other tabpage's curwin has a new buffer active.
+ api.nvim_set_current_win(info.tab2_curwin)
+ local new_buf = api.nvim_create_buf(true, true)
+ api.nvim_set_current_buf(new_buf)
+ api.nvim_set_current_win(info.tab1_curwin)
+ command('let result = []')
+ new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab2_curwin })
+ eq({
+ { 'WinNew', new_win },
+ { 'BufLeave', info.tab1_curwin, info.orig_buf },
+ { 'WinLeave', info.tab1_curwin },
+ { 'TabLeave', info.tab1 },
+
+ { 'WinEnter', info.tab2_curwin },
+ { 'TabEnter', info.tab2 },
+ { 'BufEnter', info.tab2_curwin, new_buf },
+ { 'WinLeave', info.tab2_curwin },
+ { 'WinEnter', new_win },
+ { 'BufLeave', new_win, new_buf },
+ { 'BufEnter', new_win, info.other_buf },
+ { 'BufWinEnter', new_win, info.other_buf },
+ }, eval('result'))
+ end)
+
+ it('OK when new window is moved to other tabpage by autocommands', function()
+ -- Use nvim_win_set_config in the autocommands, as other methods of moving a window to a
+ -- different tabpage (e.g: wincmd T) actually creates a new window.
+ local tab0 = api.nvim_get_current_tabpage()
+ local tab0_win = api.nvim_get_current_win()
+ command('tabnew')
+ local new_buf = api.nvim_create_buf(true, true)
+ local tab1 = api.nvim_get_current_tabpage()
+ local tab1_parent = api.nvim_get_current_win()
+ command(
+ 'tabfirst | autocmd WinNew * ++once call nvim_win_set_config(0, #{split: "left", win: '
+ .. tab1_parent
+ .. '})'
+ )
+ local new_win = api.nvim_open_win(new_buf, true, { split = 'left' })
+ eq(tab1, api.nvim_get_current_tabpage())
+ eq(new_win, api.nvim_get_current_win())
+ eq(new_buf, api.nvim_get_current_buf())
+
+ -- nvim_win_set_config called after entering. It doesn't follow a curwin that is moved to a
+ -- different tabpage, but instead moves to the win filling the space, which is tab0_win.
+ command(
+ 'tabfirst | autocmd WinEnter * ++once call nvim_win_set_config(0, #{split: "left", win: '
+ .. tab1_parent
+ .. '})'
+ )
+ new_win = api.nvim_open_win(new_buf, true, { split = 'left' })
+ eq(tab0, api.nvim_get_current_tabpage())
+ eq(tab0_win, api.nvim_get_current_win())
+ eq(tab1, api.nvim_win_get_tabpage(new_win))
+ eq(new_buf, api.nvim_win_get_buf(new_win))
+
+ command(
+ 'tabfirst | autocmd BufEnter * ++once call nvim_win_set_config(0, #{split: "left", win: '
+ .. tab1_parent
+ .. '})'
+ )
+ new_win = api.nvim_open_win(new_buf, true, { split = 'left' })
+ eq(tab0, api.nvim_get_current_tabpage())
+ eq(tab0_win, api.nvim_get_current_win())
+ eq(tab1, api.nvim_win_get_tabpage(new_win))
+ eq(new_buf, api.nvim_win_get_buf(new_win))
+ end)
+
+ it('does not fire BufWinEnter if win_set_buf fails', function()
+ exec([[
+ set nohidden modified
+ autocmd WinNew * ++once only!
+ let fired = v:false
+ autocmd BufWinEnter * ++once let fired = v:true
+ ]])
+ eq(
+ 'Failed to set buffer 2',
+ pcall_err(api.nvim_open_win, api.nvim_create_buf(true, true), false, { split = 'left' })
+ )
+ eq(false, eval('fired'))
+ end)
+
+ it('fires Buf* autocommands when `!enter` if window is entered via autocommands', function()
+ exec([[
+ autocmd WinNew * ++once only!
+ let fired = v:false
+ autocmd BufEnter * ++once let fired = v:true
+ ]])
+ api.nvim_open_win(api.nvim_create_buf(true, true), false, { split = 'left' })
+ eq(true, eval('fired'))
+ end)
+
+ it('no heap-use-after-free if target buffer deleted by autocommands', function()
+ local cur_buf = api.nvim_get_current_buf()
+ local new_buf = api.nvim_create_buf(true, true)
+ command('autocmd WinNew * ++once call nvim_buf_delete(' .. new_buf .. ', #{force: 1})')
+ api.nvim_open_win(new_buf, true, { split = 'left' })
+ eq(cur_buf, api.nvim_get_current_buf())
+ end)
+
+ it('checks if splitting disallowed', function()
+ command('split | autocmd WinEnter * ++once call nvim_open_win(0, 0, #{split: "right"})')
+ matches("E242: Can't split a window while closing another$", pcall_err(command, 'quit'))
+
+ command('only | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left"})')
+ matches(
+ 'E1159: Cannot split a window when closing the buffer$',
+ pcall_err(command, 'new | quit')
+ )
+
+ local w = api.nvim_get_current_win()
+ command(
+ 'only | new | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left", win: '
+ .. w
+ .. '})'
+ )
+ matches(
+ 'E1159: Cannot split a window when closing the buffer$',
+ pcall_err(api.nvim_win_close, w, true)
+ )
+
+ -- OK when using window to different buffer than `win`s.
+ w = api.nvim_get_current_win()
+ command(
+ 'only | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left", win: '
+ .. w
+ .. '})'
+ )
+ command('new | quit')
+ end)
+
+ it('restores last known cursor position if BufWinEnter did not move it', function()
+ -- This test mostly exists to ensure BufWinEnter is executed before enter_buffer's epilogue.
+ local buf = api.nvim_get_current_buf()
+ insert([[
+ foo
+ bar baz .etc
+ i love autocommand bugs!
+ supercalifragilisticexpialidocious
+ marvim is actually a human
+ llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch
+ ]])
+ api.nvim_win_set_cursor(0, { 5, 2 })
+ command('set nostartofline | enew')
+ local new_win = api.nvim_open_win(buf, false, { split = 'left' })
+ eq({ 5, 2 }, api.nvim_win_get_cursor(new_win))
+
+ exec([[
+ only!
+ autocmd BufWinEnter * ++once normal! j6l
+ ]])
+ new_win = api.nvim_open_win(buf, false, { split = 'left' })
+ eq({ 2, 6 }, api.nvim_win_get_cursor(new_win))
+ end)
+
+ it('does not block all win_set_buf autocommands if !enter and !noautocmd', function()
+ local new_buf = fn.bufadd('foobarbaz')
+ exec([[
+ let triggered = ""
+ autocmd BufReadCmd * ++once let triggered = bufname()
+ ]])
+ api.nvim_open_win(new_buf, false, { split = 'left' })
+ eq('foobarbaz', eval('triggered'))
+ end)
+
+ it('sets error when no room', function()
+ matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_open_win, 0, true, { split = 'above', win = 0 })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_open_win, 0, true, { split = 'below', win = 0 })
+ )
+ end)
end)
describe('set_config', function()
@@ -1471,6 +1773,15 @@ describe('API/win', function()
config = api.nvim_win_get_config(win)
eq('', config.relative)
eq('below', config.split)
+
+ eq(
+ "non-float with 'win' requires at least 'split' or 'vertical'",
+ pcall_err(api.nvim_win_set_config, 0, { win = 0 })
+ )
+ eq(
+ "non-float with 'win' requires at least 'split' or 'vertical'",
+ pcall_err(api.nvim_win_set_config, 0, { win = 0, relative = '' })
+ )
end)
it('creates top-level splits', function()
@@ -1663,6 +1974,474 @@ describe('API/win', function()
},
}, fn.winlayout())
end)
+
+ it('closing new curwin when moving window to other tabpage works', function()
+ command('split | tabnew')
+ local t2_win = api.nvim_get_current_win()
+ command('tabfirst | autocmd WinEnter * ++once quit')
+ local t1_move_win = api.nvim_get_current_win()
+ -- win_set_config fails to switch away from "t1_move_win" because the WinEnter autocmd that
+ -- closed the window we're switched to returns us to "t1_move_win", as it filled the space.
+ eq(
+ 'Failed to switch away from window ' .. t1_move_win,
+ pcall_err(api.nvim_win_set_config, t1_move_win, { win = t2_win, split = 'left' })
+ )
+ eq(t1_move_win, api.nvim_get_current_win())
+
+ command('split | split | autocmd WinEnter * ++once quit')
+ t1_move_win = api.nvim_get_current_win()
+ -- In this case, we closed the window that we got switched to, but doing so didn't switch us
+ -- back to "t1_move_win", which is fine.
+ api.nvim_win_set_config(t1_move_win, { win = t2_win, split = 'left' })
+ neq(t1_move_win, api.nvim_get_current_win())
+ end)
+
+ it('messing with "win" or "parent" when moving "win" to other tabpage', function()
+ command('split | tabnew')
+ local t2 = api.nvim_get_current_tabpage()
+ local t2_win1 = api.nvim_get_current_win()
+ command('split')
+ local t2_win2 = api.nvim_get_current_win()
+ command('split')
+ local t2_win3 = api.nvim_get_current_win()
+
+ command('tabfirst | autocmd WinEnter * ++once call nvim_win_close(' .. t2_win1 .. ', 1)')
+ local cur_win = api.nvim_get_current_win()
+ eq(
+ 'Windows to split were closed',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_win1, split = 'left' })
+ )
+ eq(cur_win, api.nvim_get_current_win())
+
+ command('split | autocmd WinLeave * ++once quit!')
+ cur_win = api.nvim_get_current_win()
+ eq(
+ 'Windows to split were closed',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_win2, split = 'left' })
+ )
+ neq(cur_win, api.nvim_get_current_win())
+
+ exec([[
+ split
+ autocmd WinLeave * ++once
+ \ call nvim_win_set_config(0, #{relative:'editor', row:0, col:0, width:5, height:5})
+ ]])
+ cur_win = api.nvim_get_current_win()
+ eq(
+ 'Floating state of windows to split changed',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_win3, split = 'left' })
+ )
+ eq('editor', api.nvim_win_get_config(0).relative)
+ eq(cur_win, api.nvim_get_current_win())
+
+ command('autocmd WinLeave * ++once wincmd J')
+ cur_win = api.nvim_get_current_win()
+ eq(
+ 'Floating state of windows to split changed',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_win3, split = 'left' })
+ )
+ eq('', api.nvim_win_get_config(0).relative)
+ eq(cur_win, api.nvim_get_current_win())
+
+ -- Try to make "parent" floating. This should give the same error as before, but because
+ -- changing a split from another tabpage into a float isn't supported yet, check for that
+ -- error instead for now.
+ -- Use ":silent!" to avoid the one second delay from printing the error message.
+ exec(([[
+ autocmd WinLeave * ++once silent!
+ \ call nvim_win_set_config(%d, #{relative:'editor', row:0, col:0, width:5, height:5})
+ ]]):format(t2_win3))
+ cur_win = api.nvim_get_current_win()
+ api.nvim_win_set_config(0, { win = t2_win3, split = 'left' })
+ matches(
+ 'Cannot change window from different tabpage into float$',
+ api.nvim_get_vvar('errmsg')
+ )
+ -- The error doesn't abort moving the window (or maybe it should, if that's wanted?)
+ neq(cur_win, api.nvim_get_current_win())
+ eq(t2, api.nvim_win_get_tabpage(cur_win))
+ end)
+
+ it('expected autocmds when moving window to other tabpage', function()
+ local new_curwin = api.nvim_get_current_win()
+ command('split')
+ local win = api.nvim_get_current_win()
+ command('tabnew')
+ local parent = api.nvim_get_current_win()
+ exec([[
+ tabfirst
+ let result = []
+ autocmd WinEnter * let result += ["Enter", win_getid()]
+ autocmd WinLeave * let result += ["Leave", win_getid()]
+ autocmd WinNew * let result += ["New", win_getid()]
+ ]])
+ api.nvim_win_set_config(0, { win = parent, split = 'left' })
+ -- Shouldn't see WinNew, as we're not creating any new windows, just moving existing ones.
+ eq({ 'Leave', win, 'Enter', new_curwin }, eval('result'))
+ end)
+
+ it('no autocmds when moving window within same tabpage', function()
+ local parent = api.nvim_get_current_win()
+ exec([[
+ split
+ let result = []
+ autocmd WinEnter * let result += ["Enter", win_getid()]
+ autocmd WinLeave * let result += ["Leave", win_getid()]
+ autocmd WinNew * let result += ["New", win_getid()]
+ ]])
+ api.nvim_win_set_config(0, { win = parent, split = 'left' })
+ -- Shouldn't see any of those events, as we remain in the same window.
+ eq({}, eval('result'))
+ end)
+
+ it('checks if splitting disallowed', function()
+ command('split | autocmd WinEnter * ++once call nvim_win_set_config(0, #{split: "right"})')
+ matches("E242: Can't split a window while closing another$", pcall_err(command, 'quit'))
+
+ command('autocmd BufHidden * ++once call nvim_win_set_config(0, #{split: "left"})')
+ matches(
+ 'E1159: Cannot split a window when closing the buffer$',
+ pcall_err(command, 'new | quit')
+ )
+
+ -- OK when using window to different buffer.
+ local w = api.nvim_get_current_win()
+ command('autocmd BufHidden * ++once call nvim_win_set_config(' .. w .. ', #{split: "left"})')
+ command('new | quit')
+ end)
+
+ --- Returns a function to get information about the window layout, sizes and positions of a
+ --- tabpage.
+ local function define_tp_info_function()
+ exec_lua([[
+ function tp_info(tp)
+ return {
+ layout = vim.fn.winlayout(vim.api.nvim_tabpage_get_number(tp)),
+ pos_sizes = vim.tbl_map(
+ function(w)
+ local pos = vim.fn.win_screenpos(w)
+ return {
+ row = pos[1],
+ col = pos[2],
+ width = vim.fn.winwidth(w),
+ height = vim.fn.winheight(w)
+ }
+ end,
+ vim.api.nvim_tabpage_list_wins(tp)
+ )
+ }
+ end
+ ]])
+
+ return function(tp)
+ return exec_lua('return tp_info(...)', tp)
+ end
+ end
+
+ it('attempt to move window with no room', function()
+ -- Fill the 2nd tabpage full of windows until we run out of room.
+ -- Use &laststatus=0 to ensure restoring missing statuslines doesn't affect things.
+ command('set laststatus=0 | tabnew')
+ matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
+ command('vsplit | wincmd | | wincmd p')
+ local t2 = api.nvim_get_current_tabpage()
+ local t2_cur_win = api.nvim_get_current_win()
+ local t2_top_split = fn.win_getid(1)
+ local t2_bot_split = fn.win_getid(fn.winnr('$'))
+ local t2_float = api.nvim_open_win(
+ 0,
+ false,
+ { relative = 'editor', row = 0, col = 0, width = 10, height = 10 }
+ )
+ local t2_float_config = api.nvim_win_get_config(t2_float)
+ local tp_info = define_tp_info_function()
+ local t2_info = tp_info(t2)
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'below' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'below' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t2_float, { win = t2_top_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t2_float, { win = t2_top_split, split = 'below' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t2_float, { win = t2_bot_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t2_float, { win = t2_bot_split, split = 'below' })
+ )
+ eq(t2_cur_win, api.nvim_get_current_win())
+ eq(t2_info, tp_info(t2))
+ eq(t2_float_config, api.nvim_win_get_config(t2_float))
+
+ -- Try to move windows from the 1st tabpage to the 2nd.
+ command('tabfirst | split | wincmd _')
+ local t1 = api.nvim_get_current_tabpage()
+ local t1_cur_win = api.nvim_get_current_win()
+ local t1_float = api.nvim_open_win(
+ 0,
+ false,
+ { relative = 'editor', row = 5, col = 3, width = 7, height = 6 }
+ )
+ local t1_float_config = api.nvim_win_get_config(t1_float)
+ local t1_info = tp_info(t1)
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'below' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'below' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t1_float, { win = t2_top_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t1_float, { win = t2_top_split, split = 'below' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t1_float, { win = t2_bot_split, split = 'above' })
+ )
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t1_float, { win = t2_bot_split, split = 'below' })
+ )
+ eq(t1_cur_win, api.nvim_get_current_win())
+ eq(t1_info, tp_info(t1))
+ eq(t1_float_config, api.nvim_win_get_config(t1_float))
+ end)
+
+ it('attempt to move window from other tabpage with no room', function()
+ -- Fill up the 1st tabpage with horizontal splits, then create a 2nd with only a few. Go back
+ -- to the 1st and try to move windows from the 2nd (while it's non-current) to it. Check that
+ -- window positions and sizes in the 2nd are unchanged.
+ command('set laststatus=0')
+ matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
+
+ command('tab split')
+ local t2 = api.nvim_get_current_tabpage()
+ local t2_top = api.nvim_get_current_win()
+ command('belowright split')
+ local t2_mid_left = api.nvim_get_current_win()
+ command('belowright vsplit')
+ local t2_mid_right = api.nvim_get_current_win()
+ command('split | wincmd J')
+ local t2_bot = api.nvim_get_current_win()
+ local tp_info = define_tp_info_function()
+ local t2_info = tp_info(t2)
+ eq({
+ 'col',
+ {
+ { 'leaf', t2_top },
+ {
+ 'row',
+ {
+ { 'leaf', t2_mid_left },
+ { 'leaf', t2_mid_right },
+ },
+ },
+ { 'leaf', t2_bot },
+ },
+ }, t2_info.layout)
+
+ local function try_move_t2_wins_to_t1()
+ for _, w in ipairs({ t2_bot, t2_mid_left, t2_mid_right, t2_top }) do
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, w, { win = 0, split = 'below' })
+ )
+ eq(t2_info, tp_info(t2))
+ end
+ end
+ command('tabfirst')
+ try_move_t2_wins_to_t1()
+ -- Go to the 2nd tabpage to ensure nothing changes after win_comp_pos, last_status, .etc.
+ -- from enter_tabpage.
+ command('tabnext')
+ eq(t2_info, tp_info(t2))
+
+ -- Check things are fine with the global statusline too, for good measure.
+ -- Set it while the 2nd tabpage is current, so last_status runs for it.
+ command('set laststatus=3')
+ t2_info = tp_info(t2)
+ command('tabfirst')
+ try_move_t2_wins_to_t1()
+ end)
+
+ it('handles cmdwin and textlock restrictions', function()
+ command('tabnew')
+ local t2 = api.nvim_get_current_tabpage()
+ local t2_win = api.nvim_get_current_win()
+ command('tabfirst')
+ local t1_move_win = api.nvim_get_current_win()
+ command('split')
+
+ -- Can't move the cmdwin, or its old curwin to a different tabpage.
+ local old_curwin = api.nvim_get_current_win()
+ feed('q:')
+ eq(
+ 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
+ pcall_err(api.nvim_win_set_config, 0, { split = 'left', win = t2_win })
+ )
+ eq(
+ 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
+ pcall_err(api.nvim_win_set_config, old_curwin, { split = 'left', win = t2_win })
+ )
+ -- But we can move other windows.
+ api.nvim_win_set_config(t1_move_win, { split = 'left', win = t2_win })
+ eq(t2, api.nvim_win_get_tabpage(t1_move_win))
+ command('quit!')
+
+ -- Can't configure windows such that the cmdwin would become the only non-float.
+ command('only!')
+ feed('q:')
+ eq(
+ 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
+ pcall_err(
+ api.nvim_win_set_config,
+ old_curwin,
+ { relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
+ )
+ )
+ -- old_curwin is now no longer the only other non-float, so we can make it floating now.
+ local t1_new_win = api.nvim_open_win(
+ api.nvim_create_buf(true, true),
+ false,
+ { split = 'left', win = old_curwin }
+ )
+ api.nvim_win_set_config(
+ old_curwin,
+ { relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
+ )
+ eq('editor', api.nvim_win_get_config(old_curwin).relative)
+ -- ...which means we shouldn't be able to also make the new window floating too!
+ eq(
+ 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
+ pcall_err(
+ api.nvim_win_set_config,
+ t1_new_win,
+ { relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
+ )
+ )
+ -- Nothing ought to stop us from making the cmdwin itself floating, though...
+ api.nvim_win_set_config(0, { relative = 'editor', row = 0, col = 0, width = 5, height = 5 })
+ eq('editor', api.nvim_win_get_config(0).relative)
+ -- We can't make our new window from before floating too, as it's now the only non-float.
+ eq(
+ 'Cannot change last window into float',
+ pcall_err(
+ api.nvim_win_set_config,
+ t1_new_win,
+ { relative = 'editor', row = 0, col = 0, width = 5, height = 5 }
+ )
+ )
+ command('quit!')
+
+ -- Can't switch away from window before moving it to a different tabpage during textlock.
+ exec(([[
+ new
+ call setline(1, 'foo')
+ setlocal debug=throw indentexpr=nvim_win_set_config(0,#{split:'left',win:%d})
+ ]]):format(t2_win))
+ local cur_win = api.nvim_get_current_win()
+ matches(
+ 'E565: Not allowed to change text or change window$',
+ pcall_err(command, 'normal! ==')
+ )
+ eq(cur_win, api.nvim_get_current_win())
+ end)
+
+ it('updates statusline when moving bottom split', function()
+ local screen = Screen.new(10, 10)
+ screen:set_default_attr_ids({
+ [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
+ [1] = { bold = true, reverse = true }, -- StatusLine
+ })
+ screen:attach()
+ exec([[
+ set laststatus=0
+ belowright split
+ call nvim_win_set_config(0, #{split: 'above', win: win_getid(winnr('#'))})
+ ]])
+ screen:expect([[
+ ^ |
+ {0:~ }|*3
+ {1:[No Name] }|
+ |
+ {0:~ }|*3
+ |
+ ]])
+ end)
+
+ it("updates tp_curwin of moved window's original tabpage", function()
+ local t1 = api.nvim_get_current_tabpage()
+ command('tab split | split')
+ local t2 = api.nvim_get_current_tabpage()
+ local t2_alt_win = api.nvim_get_current_win()
+ command('vsplit')
+ local t2_cur_win = api.nvim_get_current_win()
+ command('tabprevious')
+ eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
+
+ -- tp_curwin is unchanged when moved within the same tabpage.
+ api.nvim_win_set_config(t2_cur_win, { split = 'left', win = t2_alt_win })
+ eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
+
+ -- Also unchanged if the move failed.
+ command('let &winwidth = &columns | let &winminwidth = &columns')
+ matches(
+ 'E36: Not enough room$',
+ pcall_err(api.nvim_win_set_config, t2_cur_win, { split = 'left', win = 0 })
+ )
+ eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
+ command('set winminwidth& winwidth&')
+
+ -- But is changed if successfully moved to a different tabpage.
+ api.nvim_win_set_config(t2_cur_win, { split = 'left', win = 0 })
+ eq(t2_alt_win, api.nvim_tabpage_get_win(t2))
+ eq(t1, api.nvim_win_get_tabpage(t2_cur_win))
+
+ -- Now do it for a float, which has different altwin logic.
+ command('tabnext')
+ t2_cur_win =
+ api.nvim_open_win(0, true, { relative = 'editor', row = 5, col = 5, width = 5, height = 5 })
+ eq(t2_alt_win, fn.win_getid(fn.winnr('#')))
+ command('tabprevious')
+ eq(t2_cur_win, api.nvim_tabpage_get_win(t2))
+
+ api.nvim_win_set_config(t2_cur_win, { split = 'left', win = 0 })
+ eq(t2_alt_win, api.nvim_tabpage_get_win(t2))
+ eq(t1, api.nvim_win_get_tabpage(t2_cur_win))
+ end)
end)
describe('get_config', function()
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index a262d239e8..add3df6d8a 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -3664,6 +3664,20 @@ describe('lua stdlib', function()
]]
)
end)
+
+ it('layout in current tabpage does not affect windows in others', function()
+ command('tab split')
+ local t2_move_win = api.nvim_get_current_win()
+ command('vsplit')
+ local t2_other_win = api.nvim_get_current_win()
+ command('tabprevious')
+ matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
+ command('vsplit')
+
+ -- Without vim-patch:8.2.3862, this gives E36, despite just the 1st tabpage being full.
+ exec_lua('vim.api.nvim_win_call(..., function() vim.cmd.wincmd "J" end)', t2_move_win)
+ eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2))
+ end)
end)
describe('vim.iconv', function()
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index cdb3b79963..8b7107fdf2 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -550,6 +550,43 @@ describe('float window', function()
eq({ w0 }, api.nvim_list_wins())
end)
+ it('win_splitmove() can move float into a split', function()
+ command('split')
+ eq({'col', {{'leaf', 1001}, {'leaf', 1000}}}, fn.winlayout())
+
+ local win1 = api.nvim_open_win(0, true, {relative = 'editor', row = 1, col = 1, width = 5, height = 5})
+ fn.win_splitmove(win1, 1001, {vertical = true})
+ eq({'col', {{'row', {{'leaf', win1}, {'leaf', 1001}}}, {'leaf', 1000}}}, fn.winlayout())
+ eq('', api.nvim_win_get_config(win1).relative)
+
+ -- Should be unable to create a split relative to a float, though.
+ local win2 = api.nvim_open_win(0, true, {relative = 'editor', row = 1, col = 1, width = 5, height = 5})
+ eq('Vim:E957: Invalid window number', pcall_err(fn.win_splitmove, win1, win2, {vertical = true}))
+ end)
+
+ it('tp_curwin updated if external window is moved into split', function()
+ local screen = Screen.new(20, 7)
+ screen:attach { ext_multigrid = true }
+
+ command('tabnew')
+ local external_win = api.nvim_open_win(0, true, {external = true, width = 5, height = 5})
+ eq(external_win, api.nvim_get_current_win())
+ eq(2, fn.tabpagenr())
+ command('tabfirst')
+ api.nvim_set_current_win(external_win)
+ eq(external_win, api.nvim_get_current_win())
+ eq(1, fn.tabpagenr())
+
+ command('wincmd J')
+ eq(external_win, api.nvim_get_current_win())
+ eq(false, api.nvim_win_get_config(external_win).external)
+ command('tabnext')
+ eq(2, fn.tabpagenr())
+ neq(external_win, api.nvim_get_current_win())
+
+ screen:detach()
+ end)
+
describe('with only one tabpage,', function()
local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1}
local old_buf, old_win
@@ -836,6 +873,39 @@ describe('float window', function()
end)
end)
+ describe(':close on non-float with floating windows', function()
+ it('does not quit Nvim if BufWinLeave makes it the only non-float', function()
+ exec([[
+ let firstbuf = bufnr()
+ new
+ let midwin = win_getid()
+ new
+ setlocal bufhidden=wipe
+ call nvim_win_set_config(midwin,
+ \ #{relative: 'editor', row: 5, col: 5, width: 5, height: 5})
+ autocmd BufWinLeave * ++once exe firstbuf .. 'bwipe!'
+ ]])
+ eq('Vim(close):E855: Autocommands caused command to abort', pcall_err(command, 'close'))
+ assert_alive()
+ end)
+
+ pending('does not crash if BufWinLeave makes it the only non-float in tabpage', function()
+ exec([[
+ tabnew
+ let firstbuf = bufnr()
+ new
+ let midwin = win_getid()
+ new
+ setlocal bufhidden=wipe
+ call nvim_win_set_config(midwin,
+ \ #{relative: 'editor', row: 5, col: 5, width: 5, height: 5})
+ autocmd BufWinLeave * ++once exe firstbuf .. 'bwipe!'
+ ]])
+ eq('Vim(close):E855: Autocommands caused command to abort', pcall_err(command, 'close'))
+ assert_alive()
+ end)
+ end)
+
local function with_ext_multigrid(multigrid)
local screen, attrs
before_each(function()
@@ -9101,6 +9171,22 @@ describe('float window', function()
]]}
end
end)
+
+ it('attempt to turn into split with no room', function()
+ eq('Vim(split):E36: Not enough room', pcall_err(command, 'execute "split |"->repeat(&lines)'))
+ command('vsplit | wincmd | | wincmd p')
+ api.nvim_open_win(0, true, {relative = "editor", row = 0, col = 0, width = 5, height = 5})
+ local config = api.nvim_win_get_config(0)
+ eq('editor', config.relative)
+
+ local layout = fn.winlayout()
+ local restcmd = fn.winrestcmd()
+ eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd K'))
+ eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd J'))
+ eq(layout, fn.winlayout())
+ eq(restcmd, fn.winrestcmd())
+ eq(config, api.nvim_win_get_config(0))
+ end)
end
describe('with ext_multigrid', function()
diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua
index fee4b64d44..000e2b1b04 100644
--- a/test/functional/ui/statusline_spec.lua
+++ b/test/functional/ui/statusline_spec.lua
@@ -11,6 +11,7 @@ local exec = helpers.exec
local exec_lua = helpers.exec_lua
local eval = helpers.eval
local sleep = vim.uv.sleep
+local pcall_err = helpers.pcall_err
local mousemodels = { 'extend', 'popup', 'popup_setpos' }
@@ -474,6 +475,25 @@ describe('global statusline', function()
|
]])
end)
+
+ it('horizontal separators unchanged when failing to split-move window', function()
+ exec([[
+ botright split
+ let &winwidth = &columns
+ let &winminwidth = &columns
+ ]])
+ eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L'))
+ command('mode')
+ screen:expect([[
+ |
+ {1:~ }|*5
+ ────────────────────────────────────────────────────────────|
+ ^ |
+ {1:~ }|*6
+ {2:[No Name] 0,0-1 All}|
+ |
+ ]])
+ end)
end)
it('statusline does not crash if it has Arabic characters #19447', function()
diff --git a/test/old/testdir/test_execute_func.vim b/test/old/testdir/test_execute_func.vim
index 2edae39b8f..d9909e92a6 100644
--- a/test/old/testdir/test_execute_func.vim
+++ b/test/old/testdir/test_execute_func.vim
@@ -3,6 +3,7 @@
source view_util.vim
source check.vim
source vim9.vim
+source term_util.vim
func NestedEval()
let nested = execute('echo "nested\nlines"')
@@ -177,6 +178,27 @@ func Test_win_execute_visual_redraw()
bwipe!
endfunc
+func Test_win_execute_on_startup()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ vim9script
+ [repeat('x', &columns)]->writefile('Xfile1')
+ silent tabedit Xfile2
+ var id = win_getid()
+ silent tabedit Xfile3
+ autocmd VimEnter * win_execute(id, 'close')
+ END
+ call writefile(lines, 'XwinExecute')
+ let buf = RunVimInTerminal('-p Xfile1 -Nu XwinExecute', {})
+
+ " this was crashing on exit with EXITFREE defined
+ call StopVimInTerminal(buf)
+
+ call delete('XwinExecute')
+ call delete('Xfile1')
+endfunc
+
func Test_execute_cmd_with_null()
call assert_equal("", execute(v:_null_string))
call assert_equal("", execute(v:_null_list))
diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim
index a1987ed3c9..8130f7a1ac 100644
--- a/test/old/testdir/test_scroll_opt.vim
+++ b/test/old/testdir/test_scroll_opt.vim
@@ -963,4 +963,42 @@ func Test_smoothscroll_insert_bottom()
call StopVimInTerminal(buf)
endfunc
+func Test_smoothscroll_in_zero_width_window()
+ set cpo+=n number smoothscroll
+ set winwidth=99999 winminwidth=0
+
+ vsplit
+ call assert_equal(0, winwidth(winnr('#')))
+ call win_execute(win_getid(winnr('#')), "norm! \<C-Y>")
+
+ only!
+ set winwidth& winminwidth&
+ set cpo-=n nonumber nosmoothscroll
+endfunc
+
+func Test_smoothscroll_textoff_small_winwidth()
+ set smoothscroll number
+ call setline(1, 'llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch')
+ vsplit
+
+ let textoff = getwininfo(win_getid())[0].textoff
+ execute 'vertical resize' textoff + 1
+ redraw
+ call assert_equal(0, winsaveview().skipcol)
+ execute "normal! 0\<C-E>"
+ redraw
+ call assert_equal(1, winsaveview().skipcol)
+ execute 'vertical resize' textoff - 1
+ " This caused a signed integer overflow.
+ redraw
+ call assert_equal(1, winsaveview().skipcol)
+ execute 'vertical resize' textoff
+ " This caused an infinite loop.
+ redraw
+ call assert_equal(1, winsaveview().skipcol)
+
+ %bw!
+ set smoothscroll& number&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_window_cmd.vim b/test/old/testdir/test_window_cmd.vim
index da1711a0a1..8898d3a02d 100644
--- a/test/old/testdir/test_window_cmd.vim
+++ b/test/old/testdir/test_window_cmd.vim
@@ -272,6 +272,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
@@ -284,7 +294,39 @@ 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 its window's width to 2 for the test.
+ wincmd j
+ set laststatus=0 winminwidth=0
+ vertical resize 2
+ set winminwidth&
+ 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))
+
+ 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())
+ 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()
@@ -1024,6 +1066,19 @@ func Test_win_splitmove()
leftabove split b
leftabove vsplit c
leftabove split d
+
+ " win_splitmove doesn't actually create or close any windows, so expect an
+ " unchanged winid and no WinNew/WinClosed events, like :wincmd H/J/K/L.
+ let s:triggered = []
+ augroup WinSplitMove
+ au!
+ " Nvim: WinNewPre not ported yet. Also needs full port of v9.1.0117 to pass.
+ " au WinNewPre * let s:triggered += ['WinNewPre']
+ au WinNew * let s:triggered += ['WinNew', win_getid()]
+ au WinClosed * let s:triggered += ['WinClosed', str2nr(expand('<afile>'))]
+ augroup END
+ let winid = win_getid()
+
call assert_equal(0, win_splitmove(winnr(), winnr('l')))
call assert_equal(bufname(winbufnr(1)), 'c')
call assert_equal(bufname(winbufnr(2)), 'd')
@@ -1046,6 +1101,11 @@ func Test_win_splitmove()
call assert_equal(bufname(winbufnr(3)), 'a')
call assert_equal(bufname(winbufnr(4)), 'd')
call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E1297:')
+ call assert_equal([], s:triggered)
+ call assert_equal(winid, win_getid())
+
+ unlet! s:triggered
+ au! WinSplitMove
only | bd
call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
@@ -1055,18 +1115,53 @@ func Test_win_splitmove()
tabnew
call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:')
tabclose
-endfunc
-func Test_floatwin_splitmove()
- vsplit
- let win2 = win_getid()
- let popup_winid = nvim_open_win(0, 0, {'relative': 'win',
- \ 'row': 3, 'col': 3, 'width': 12, 'height': 3})
- call assert_fails('call win_splitmove(popup_winid, win2)', 'E957:')
- call assert_fails('call win_splitmove(win2, popup_winid)', 'E957:')
+ 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
+
+ split
+ let close_win = winnr('#')
+ augroup WinSplitMove
+ au!
+ au WinEnter * ++once quit!
+ augroup END
+ call win_splitmove(close_win, winnr())
+ call assert_equal(0, win_id2win(close_win))
- call nvim_win_close(popup_winid, 1)
- bwipe
+ au! WinSplitMove
+ augroup! WinSplitMove
+ %bw!
endfunc
" Test for the :only command
@@ -2006,24 +2101,97 @@ func Test_new_help_window_on_error()
call assert_equal(expand("<cword>"), "'mod'")
endfunc
-func Test_smoothscroll_in_zero_width_window()
- let save_lines = &lines
- let save_columns = &columns
+func Test_splitmove_flatten_frame()
+ split
+ vsplit
- winsize 0 24
- set cpo+=n
- exe "noremap 0 \<C-W>n\<C-W>L"
- norm 000000
- set number smoothscroll
- exe "norm \<C-Y>"
+ 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_autocmd_window_force_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 AucmdWinForceRoom
+ 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! AucmdWinForceRoom
+ augroup! AucmdWinForceRoom
+ %bw!
+endfunc
+
+func Test_win_gotoid_splitmove_textlock_cmdwin()
+ call setline(1, 'foo')
+ new
+ let curwin = win_getid()
+ call setline(1, 'bar')
+
+ set debug+=throw indentexpr=win_gotoid(win_getid(winnr('#')))
+ call assert_fails('normal! ==', 'E565:')
+ call assert_equal(curwin, win_getid())
+ " No error if attempting to switch to curwin; nothing happens.
+ set indentexpr=assert_equal(1,win_gotoid(win_getid()))
+ normal! ==
+ call assert_equal(curwin, win_getid())
+
+ set indentexpr=win_splitmove(winnr('#'),winnr())
+ call assert_fails('normal! ==', 'E565:')
+ call assert_equal(curwin, win_getid())
+
+ %bw!
+ set debug-=throw indentexpr&
+
+ call feedkeys('q:'
+ \ .. ":call assert_fails('call win_splitmove(winnr(''#''), winnr())', 'E11:')\<CR>"
+ \ .. ":call assert_equal('command', win_gettype())\<CR>"
+ \ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx')
+
+ call feedkeys('q:'
+ \ .. ":call assert_fails('call win_gotoid(win_getid(winnr(''#'')))', 'E11:')\<CR>"
+ "\ No error if attempting to switch to curwin; nothing happens.
+ \ .. ":call assert_equal(1, win_gotoid(win_getid()))\<CR>"
+ \ .. ":call assert_equal('command', win_gettype())\<CR>"
+ \ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx')
+endfunc
" vim: shiftwidth=2 sts=2 expandtab