aboutsummaryrefslogtreecommitdiff
path: root/test/functional/api
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-05-24 19:18:11 +0000
committerJosh Rahm <joshuarahm@gmail.com>2024-05-24 19:18:11 +0000
commitff7ed8f586589d620a806c3758fac4a47a8e7e15 (patch)
tree729bbcb92231538fa61dab6c3d890b025484b7f5 /test/functional/api
parent376914f419eb08fdf4c1a63a77e1f035898a0f10 (diff)
parent28c04948a1c887a1cc0cb64de79fa32631700466 (diff)
downloadrneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.gz
rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.tar.bz2
rneovim-ff7ed8f586589d620a806c3758fac4a47a8e7e15.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'test/functional/api')
-rw-r--r--test/functional/api/autocmd_spec.lua78
-rw-r--r--test/functional/api/buffer_spec.lua193
-rw-r--r--test/functional/api/buffer_updates_spec.lua32
-rw-r--r--test/functional/api/command_spec.lua25
-rw-r--r--test/functional/api/extmark_spec.lua82
-rw-r--r--test/functional/api/highlight_spec.lua22
-rw-r--r--test/functional/api/keymap_spec.lua203
-rw-r--r--test/functional/api/menu_spec.lua8
-rw-r--r--test/functional/api/proc_spec.lua19
-rw-r--r--test/functional/api/server_notifications_spec.lua40
-rw-r--r--test/functional/api/server_requests_spec.lua65
-rw-r--r--test/functional/api/tabpage_spec.lua42
-rw-r--r--test/functional/api/ui_spec.lua22
-rw-r--r--test/functional/api/version_spec.lua8
-rw-r--r--test/functional/api/vim_spec.lua441
-rw-r--r--test/functional/api/window_spec.lua1162
16 files changed, 1952 insertions, 490 deletions
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua
index e89abf6c64..3f9883f43f 100644
--- a/test/functional/api/autocmd_spec.lua
+++ b/test/functional/api/autocmd_spec.lua
@@ -1,14 +1,15 @@
-local helpers = require('test.functional.helpers')(after_each)
-
-local clear = helpers.clear
-local command = helpers.command
-local eq = helpers.eq
-local neq = helpers.neq
-local exec_lua = helpers.exec_lua
-local matches = helpers.matches
-local api = helpers.api
-local source = helpers.source
-local pcall_err = helpers.pcall_err
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+local clear = n.clear
+local command = n.command
+local eq = t.eq
+local neq = t.neq
+local exec_lua = n.exec_lua
+local matches = t.matches
+local api = n.api
+local source = n.source
+local pcall_err = t.pcall_err
before_each(clear)
@@ -355,6 +356,44 @@ describe('autocmd api', function()
test({ 'list' })
test({ foo = 'bar' })
end)
+
+ it('function in arbitrary data is passed to all autocmds #28353', function()
+ eq(
+ 1303,
+ exec_lua([[
+ local res = 1
+
+ local fun = function(m, x)
+ res = res * m + x
+ end
+
+ local group = vim.api.nvim_create_augroup('MyTest', { clear = false })
+
+ vim.api.nvim_create_autocmd('User', {
+ group = group,
+ callback = function(payload)
+ payload.data.fun(10, payload.data.x)
+ end,
+ pattern = 'MyEvent',
+ })
+ vim.api.nvim_create_autocmd('User', {
+ group = group,
+ callback = function(payload)
+ payload.data.fun(100, payload.data.x)
+ end,
+ pattern = 'MyEvent',
+ })
+
+ vim.api.nvim_exec_autocmds('User', {
+ group = group,
+ pattern = 'MyEvent',
+ data = { x = 3, fun = fun },
+ })
+
+ return res
+ ]])
+ )
+ end)
end)
describe('nvim_get_autocmds', function()
@@ -610,15 +649,17 @@ describe('autocmd api', function()
it('can retrieve a callback from an autocmd', function()
local content = 'I Am A Callback'
api.nvim_set_var('content', content)
-
- local result = exec_lua([[
+ exec_lua([[
local cb = function() return vim.g.content end
vim.api.nvim_create_autocmd("User", {
pattern = "TestTrigger",
desc = "A test autocommand with a callback",
callback = cb,
})
- local aus = vim.api.nvim_get_autocmds({ event = 'User', pattern = 'TestTrigger'})
+ ]])
+
+ local result = exec_lua([[
+ local aus = vim.api.nvim_get_autocmds({ event = 'User', pattern = 'TestTrigger' })
local first = aus[1]
return {
cb = {
@@ -627,9 +668,14 @@ describe('autocmd api', function()
}
}
]])
+ eq({ cb = { type = 'function', can_retrieve = true } }, result)
- eq('function', result.cb.type)
- eq(true, result.cb.can_retrieve)
+ -- Also test with Vimscript
+ source([[
+ let s:aus = nvim_get_autocmds({'event': 'User', 'pattern': 'TestTrigger'})
+ let g:result = s:aus[0].callback()
+ ]])
+ eq(content, api.nvim_get_var('result'))
end)
it(
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index 78d220ff57..cf69958fd8 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -1,21 +1,23 @@
-local helpers = require('test.functional.helpers')(after_each)
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
-local clear = helpers.clear
-local eq = helpers.eq
-local ok = helpers.ok
-local describe_lua_and_rpc = helpers.describe_lua_and_rpc(describe)
-local api = helpers.api
-local fn = helpers.fn
-local request = helpers.request
-local exc_exec = helpers.exc_exec
-local exec_lua = helpers.exec_lua
-local feed_command = helpers.feed_command
-local insert = helpers.insert
+
+local clear = n.clear
+local eq = t.eq
+local ok = t.ok
+local describe_lua_and_rpc = n.describe_lua_and_rpc(describe)
+local api = n.api
+local fn = n.fn
+local request = n.request
+local exc_exec = n.exc_exec
+local exec_lua = n.exec_lua
+local feed_command = n.feed_command
+local insert = n.insert
local NIL = vim.NIL
-local command = helpers.command
-local feed = helpers.feed
-local pcall_err = helpers.pcall_err
-local assert_alive = helpers.assert_alive
+local command = n.command
+local feed = n.feed
+local pcall_err = t.pcall_err
+local assert_alive = n.assert_alive
describe('api/buf', function()
before_each(clear)
@@ -121,6 +123,65 @@ describe('api/buf', function()
eq({ 5, 2 }, api.nvim_win_get_cursor(win2))
end)
+ it('cursor position is maintained consistently with viewport', function()
+ local screen = Screen.new(20, 12)
+ screen:set_default_attr_ids {
+ [1] = { bold = true, foreground = Screen.colors.Blue1 },
+ [2] = { reverse = true, bold = true },
+ [3] = { reverse = true },
+ }
+ screen:attach()
+
+ local lines = { 'line1', 'line2', 'line3', 'line4', 'line5', 'line6' }
+ local buf = api.nvim_get_current_buf()
+
+ api.nvim_buf_set_lines(buf, 0, -1, true, lines)
+
+ command('6')
+ command('new')
+ screen:expect {
+ grid = [[
+ ^ |
+ {1:~ }|*4
+ {2:[No Name] }|
+ line5 |
+ line6 |
+ {1:~ }|*2
+ {3:[No Name] [+] }|
+ |
+ ]],
+ }
+
+ lines[5] = 'boogalo 5'
+ api.nvim_buf_set_lines(buf, 0, -1, true, lines)
+ screen:expect {
+ grid = [[
+ ^ |
+ {1:~ }|*4
+ {2:[No Name] }|
+ boogalo 5 |
+ line6 |
+ {1:~ }|*2
+ {3:[No Name] [+] }|
+ |
+ ]],
+ }
+
+ command('wincmd w')
+ screen:expect {
+ grid = [[
+ |
+ {1:~ }|*4
+ {3:[No Name] }|
+ boogalo 5 |
+ ^line6 |
+ {1:~ }|*2
+ {2:[No Name] [+] }|
+ |
+ ]],
+ }
+ end)
+
it('line_count has defined behaviour for unloaded buffers', function()
-- we'll need to know our bufnr for when it gets unloaded
local bufnr = api.nvim_buf_get_number(0)
@@ -323,20 +384,20 @@ describe('api/buf', function()
]],
}
- -- inserting just before topline scrolls up
api.nvim_buf_set_lines(buf, 3, 3, true, { 'mmm' })
screen:expect {
grid = [[
^ |
{1:~ }|*4
{2:[No Name] }|
- mmm |
wwweeee |
xxx |
yyy |
+ zzz |
{3:[No Name] [+] }|
|
]],
+ unchanged = true,
}
end)
@@ -402,7 +463,6 @@ describe('api/buf', function()
]],
}
- -- inserting just before topline scrolls up
api.nvim_buf_set_lines(buf, 3, 3, true, { 'mmm' })
screen:expect {
grid = [[
@@ -412,10 +472,10 @@ describe('api/buf', function()
mmm |
wwweeee |
{2:[No Name] [+] }|
- mmm |
wwweeee |
xxx |
yyy |
+ zzz |
{3:[No Name] [+] }|
|
]],
@@ -1316,12 +1376,7 @@ describe('api/buf', function()
-- immediate call to nvim_win_get_cursor should have returned the same position
eq({ 2, 12 }, cursor)
-- coladd should be 0
- eq(
- 0,
- exec_lua([[
- return vim.fn.winsaveview().coladd
- ]])
- )
+ eq(0, fn.winsaveview().coladd)
end)
it('does not change cursor screen column when cursor >EOL and row got shorter', function()
@@ -1335,9 +1390,7 @@ describe('api/buf', function()
-- turn on virtualedit
command('set virtualedit=all')
-- move cursor after eol
- exec_lua([[
- vim.fn.winrestview({ coladd = 5 })
- ]])
+ fn.winrestview({ coladd = 5 })
local cursor = exec_lua([[
vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, {
@@ -1356,12 +1409,7 @@ describe('api/buf', function()
-- immediate call to nvim_win_get_cursor should have returned the same position
eq({ 2, 26 }, cursor)
-- coladd should be increased so that cursor stays in the same screen column
- eq(
- 13,
- exec_lua([[
- return vim.fn.winsaveview().coladd
- ]])
- )
+ eq(13, fn.winsaveview().coladd)
end)
it(
@@ -1377,9 +1425,7 @@ describe('api/buf', function()
-- turn on virtualedit
command('set virtualedit=all')
-- move cursor after eol
- exec_lua([[
- vim.fn.winrestview({ coladd = 21 })
- ]])
+ fn.winrestview({ coladd = 21 })
local cursor = exec_lua([[
vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, {
@@ -1398,12 +1444,7 @@ describe('api/buf', function()
-- immediate call to nvim_win_get_cursor should have returned the same position
eq({ 1, 38 }, cursor)
-- coladd should be increased so that cursor stays in the same screen column
- eq(
- 2,
- exec_lua([[
- return vim.fn.winsaveview().coladd
- ]])
- )
+ eq(2, fn.winsaveview().coladd)
end
)
@@ -1420,9 +1461,7 @@ describe('api/buf', function()
-- turn on virtualedit
command('set virtualedit=all')
-- move cursor after eol just a bit
- exec_lua([[
- vim.fn.winrestview({ coladd = 3 })
- ]])
+ fn.winrestview({ coladd = 3 })
local cursor = exec_lua([[
vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, {
@@ -1441,12 +1480,7 @@ describe('api/buf', function()
-- immediate call to nvim_win_get_cursor should have returned the same position
eq({ 1, 22 }, cursor)
-- coladd should become 0
- eq(
- 0,
- exec_lua([[
- return vim.fn.winsaveview().coladd
- ]])
- )
+ eq(0, fn.winsaveview().coladd)
end
)
@@ -1464,9 +1498,7 @@ describe('api/buf', function()
-- turn on virtualedit
command('set virtualedit=all')
-- move cursor after eol
- exec_lua([[
- vim.fn.winrestview({ coladd = 28 })
- ]])
+ fn.winrestview({ coladd = 28 })
local cursor = exec_lua([[
vim.api.nvim_buf_set_text(0, 0, 15, 3, 11, {
@@ -1485,12 +1517,7 @@ describe('api/buf', function()
-- immediate call to nvim_win_get_cursor should have returned the same position
eq({ 2, 26 }, cursor)
-- coladd should be increased so that cursor stays in the same screen column
- eq(
- 13,
- exec_lua([[
- return vim.fn.winsaveview().coladd
- ]])
- )
+ eq(13, fn.winsaveview().coladd)
end
)
end)
@@ -1686,12 +1713,11 @@ describe('api/buf', function()
api.nvim_buf_set_text(0, 0, 0, 1, 3, { 'XXX', 'YYY' })
screen:expect([[
- XXX |
- YYY |
- ^ |
- ~ |
- |
-
+ XXX |
+ YYY |
+ ^ |
+ {1:~ }|
+ |
]])
end)
@@ -2024,6 +2050,37 @@ describe('api/buf', function()
eq(1, fn.filereadable(new_name))
os.remove(new_name)
end)
+
+ describe("with 'autochdir'", function()
+ local topdir
+ local oldbuf
+ local newbuf
+
+ before_each(function()
+ command('set shellslash')
+ topdir = fn.getcwd()
+ t.mkdir(topdir .. '/Xacd')
+
+ oldbuf = api.nvim_get_current_buf()
+ command('vnew')
+ newbuf = api.nvim_get_current_buf()
+ command('set autochdir')
+ end)
+
+ after_each(function()
+ n.rmdir(topdir .. '/Xacd')
+ end)
+
+ it('does not change cwd with non-current buffer', function()
+ api.nvim_buf_set_name(oldbuf, topdir .. '/Xacd/foo.txt')
+ eq(topdir, fn.getcwd())
+ end)
+
+ it('changes cwd with current buffer', function()
+ api.nvim_buf_set_name(newbuf, topdir .. '/Xacd/foo.txt')
+ eq(topdir .. '/Xacd', fn.getcwd())
+ end)
+ end)
end)
describe('nvim_buf_is_loaded', function()
diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua
index 262ca40e28..e030b45396 100644
--- a/test/functional/api/buffer_updates_spec.lua
+++ b/test/functional/api/buffer_updates_spec.lua
@@ -1,13 +1,15 @@
-local helpers = require('test.functional.helpers')(after_each)
-local clear = helpers.clear
-local eq, ok = helpers.eq, helpers.ok
-local fn = helpers.fn
-local api = helpers.api
-local command, eval, next_msg = helpers.command, helpers.eval, helpers.next_msg
-local nvim_prog = helpers.nvim_prog
-local pcall_err = helpers.pcall_err
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+local clear = n.clear
+local eq, ok = t.eq, t.ok
+local fn = n.fn
+local api = n.api
+local command, eval, next_msg = n.command, n.eval, n.next_msg
+local nvim_prog = n.nvim_prog
+local pcall_err = t.pcall_err
local sleep = vim.uv.sleep
-local write_file = helpers.write_file
+local write_file = t.write_file
local origlines = {
'original line 1',
@@ -34,7 +36,7 @@ local function sendkeys(keys)
end
local function open(activate, lines)
- local filename = helpers.tmpname()
+ local filename = t.tmpname()
write_file(filename, table.concat(lines, '\n') .. '\n', true)
command('edit ' .. filename)
local b = api.nvim_get_current_buf()
@@ -511,11 +513,11 @@ describe('API: buffer events:', function()
-- create several new sessions, in addition to our main API
local sessions = {}
- local pipe = helpers.new_pipename()
+ local pipe = n.new_pipename()
eval("serverstart('" .. pipe .. "')")
- sessions[1] = helpers.connect(pipe)
- sessions[2] = helpers.connect(pipe)
- sessions[3] = helpers.connect(pipe)
+ sessions[1] = n.connect(pipe)
+ sessions[2] = n.connect(pipe)
+ sessions[3] = n.connect(pipe)
local function request(sessionnr, method, ...)
local status, rv = sessions[sessionnr]:request(method, ...)
@@ -814,7 +816,7 @@ describe('API: buffer events:', function()
clear()
sleep(250)
-- response
- eq(true, helpers.request('nvim_buf_attach', 0, false, {}))
+ eq(true, n.request('nvim_buf_attach', 0, false, {}))
-- notification
eq({
[1] = 'notification',
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index f73b9c8b13..a16c6a88e3 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -1,17 +1,18 @@
-local helpers = require('test.functional.helpers')(after_each)
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
local NIL = vim.NIL
-local clear = helpers.clear
-local command = helpers.command
-local eq = helpers.eq
-local api = helpers.api
-local matches = helpers.matches
-local source = helpers.source
-local pcall_err = helpers.pcall_err
-local exec_lua = helpers.exec_lua
-local assert_alive = helpers.assert_alive
-local feed = helpers.feed
-local fn = helpers.fn
+local clear = n.clear
+local command = n.command
+local eq = t.eq
+local api = n.api
+local matches = t.matches
+local source = n.source
+local pcall_err = t.pcall_err
+local exec_lua = n.exec_lua
+local assert_alive = n.assert_alive
+local feed = n.feed
+local fn = n.fn
describe('nvim_get_commands', function()
local cmd_dict = {
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index 2acfbfc949..7b2fe209ba 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -1,20 +1,21 @@
-local helpers = require('test.functional.helpers')(after_each)
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
-local request = helpers.request
-local eq = helpers.eq
-local ok = helpers.ok
-local pcall_err = helpers.pcall_err
-local insert = helpers.insert
-local feed = helpers.feed
-local clear = helpers.clear
-local command = helpers.command
-local exec = helpers.exec
-local api = helpers.api
-local assert_alive = helpers.assert_alive
+local request = n.request
+local eq = t.eq
+local ok = t.ok
+local pcall_err = t.pcall_err
+local insert = n.insert
+local feed = n.feed
+local clear = n.clear
+local command = n.command
+local exec = n.exec
+local api = n.api
+local assert_alive = n.assert_alive
local function expect(contents)
- return eq(contents, helpers.curbuf_contents())
+ return eq(contents, n.curbuf_contents())
end
local function set_extmark(ns_id, id, line, col, opts)
@@ -460,7 +461,7 @@ describe('API/extmarks', function()
-- This shouldn't seg fault
screen:expect([[
12345^ 1 |
- ~ |*8
+ {1:~ }|*8
|
]])
end)
@@ -513,7 +514,7 @@ describe('API/extmarks', function()
insert('abc')
screen:expect([[
ab^c12345 |
- ~ |*8
+ {1:~ }|*8
|
]])
local rv = get_extmark_by_id(ns, marks[1])
@@ -1568,7 +1569,7 @@ describe('API/extmarks', function()
sign_text = '>>',
spell = true,
virt_lines = {
- { { 'lines', 'Macro' }, { '???' } },
+ { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } },
{ { 'stack', { 'Type', 'Search' } }, { '!!!' } },
},
virt_lines_above = true,
@@ -1603,7 +1604,7 @@ describe('API/extmarks', function()
sign_text = '>>',
spell = true,
virt_lines = {
- { { 'lines', 'Macro' }, { '???' } },
+ { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } },
{ { 'stack', { 'Type', 'Search' } }, { '!!!' } },
},
virt_lines_above = true,
@@ -1734,16 +1735,17 @@ describe('API/extmarks', function()
command('d2')
screen:expect([[
S2^aaa bbb ccc |
- aaa bbb ccc |*3
- |*2
+ {7: }aaa bbb ccc |*3
+ {7: } |
+ |
]])
-- mark is restored with undo_restore == true
command('silent undo')
screen:expect([[
- S1 ^aaa bbb ccc |
- S1S2aaa bbb ccc |
- S2 aaa bbb ccc |
- aaa bbb ccc |*2
+ S1{7: }^aaa bbb ccc |
+ S2S1aaa bbb ccc |
+ S2{7: }aaa bbb ccc |
+ {7: }aaa bbb ccc |*2
|
]])
-- decor is not removed twice
@@ -1894,6 +1896,24 @@ describe('Extmarks buffer api with many marks', function()
end
eq(ns_marks[ns1], get_marks(ns1))
eq(ns_marks[ns2], get_marks(ns2))
+
+ api.nvim_buf_clear_namespace(0, ns1, 0, 10)
+ for id, mark in pairs(ns_marks[ns1]) do
+ if mark[1] < 10 then
+ ns_marks[ns1][id] = nil
+ end
+ end
+ eq(ns_marks[ns1], get_marks(ns1))
+ eq(ns_marks[ns2], get_marks(ns2))
+
+ api.nvim_buf_clear_namespace(0, ns1, 20, -1)
+ for id, mark in pairs(ns_marks[ns1]) do
+ if mark[1] >= 20 then
+ ns_marks[ns1][id] = nil
+ end
+ end
+ eq(ns_marks[ns1], get_marks(ns1))
+ eq(ns_marks[ns2], get_marks(ns2))
end)
it('can delete line', function()
@@ -1964,7 +1984,7 @@ describe('API/win_extmark', function()
grid = [[
non ui-watched line |
ui-watched lin^e |
- ~ |
+ {1:~ }|
|
]],
extmarks = {
@@ -2052,7 +2072,7 @@ describe('API/win_extmark', function()
grid = [[
ui-watched linupdat^e|
e |
- ~ |
+ {1:~ }|
|
]],
extmarks = {
@@ -2079,9 +2099,9 @@ describe('API/win_extmark', function()
grid = [[
## grid 1
[4:--------------------]|*3
- [No Name] [+] |
+ {3:[No Name] [+] }|
[2:--------------------]|*2
- [No Name] [+] |
+ {2:[No Name] [+] }|
[3:--------------------]|
## grid 2
non ui-watched line |
@@ -2091,7 +2111,7 @@ describe('API/win_extmark', function()
## grid 4
non ui-watched line |
ui-watched lin^e |
- ~ |
+ {1:~ }|
]],
extmarks = {
[2] = {
@@ -2112,13 +2132,13 @@ describe('API/win_extmark', function()
grid = [[
## grid 1
[4:--------------------]|*3
- [No Name] [+] |
+ {3:[No Name] [+] }|
[2:--------------------]|*2
- [No Name] [+] |
+ {2:[No Name] [+] }|
[3:--------------------]|
## grid 2
non ui-watched line |
- ui-watched linupd@@@|
+ ui-watched linupd{1:@@@}|
## grid 3
|
## grid 4
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index 1973d3e1c7..dd0611f184 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -1,14 +1,16 @@
-local helpers = require('test.functional.helpers')(after_each)
-local clear = helpers.clear
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
-local eq, eval = helpers.eq, helpers.eval
-local command = helpers.command
-local exec_capture = helpers.exec_capture
-local api = helpers.api
-local fn = helpers.fn
-local pcall_err = helpers.pcall_err
-local ok = helpers.ok
-local assert_alive = helpers.assert_alive
+
+local clear = n.clear
+local eq, eval = t.eq, n.eval
+local command = n.command
+local exec_capture = n.exec_capture
+local api = n.api
+local fn = n.fn
+local pcall_err = t.pcall_err
+local ok = t.ok
+local assert_alive = n.assert_alive
describe('API: highlight', function()
clear()
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index 0decd710e9..995711507f 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -1,17 +1,19 @@
-local helpers = require('test.functional.helpers')(after_each)
-
-local clear = helpers.clear
-local command = helpers.command
-local eq, neq = helpers.eq, helpers.neq
-local exec_lua = helpers.exec_lua
-local exec = helpers.exec
-local feed = helpers.feed
-local fn = helpers.fn
-local api = helpers.api
-local source = helpers.source
-local pcall_err = helpers.pcall_err
-
-local shallowcopy = helpers.shallowcopy
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+local clear = n.clear
+local command = n.command
+local eq, neq = t.eq, t.neq
+local exec_lua = n.exec_lua
+local exec = n.exec
+local feed = n.feed
+local fn = n.fn
+local api = n.api
+local matches = t.matches
+local source = n.source
+local pcall_err = t.pcall_err
+
+local shallowcopy = t.shallowcopy
local sleep = vim.uv.sleep
local sid_api_client = -9
@@ -398,7 +400,9 @@ describe('nvim_get_keymap', function()
0,
exec_lua([[
GlobalCount = 0
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]])
)
@@ -738,7 +742,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
end
end
- it('can set mappings containing literal keycodes', function()
+ it('can set mappings containing C0 control codes', function()
api.nvim_set_keymap('n', '\n\r\n', 'rhs', {})
local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs')
eq(expected, get_mapargs('n', '<NL><CR><NL>'))
@@ -951,7 +955,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -963,34 +969,38 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it(':map command shows lua mapping correctly', function()
exec_lua [[
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() print('jkl;') end,
+ })
]]
- assert.truthy(
- string.match(
- exec_lua [[return vim.api.nvim_exec2(':nmap asdf', { output = true }).output]],
- '^\nn asdf <Lua %d+>'
- )
+ matches(
+ '^\nn asdf <Lua %d+>',
+ exec_lua [[return vim.api.nvim_exec2(':nmap asdf', { output = true }).output]]
)
end)
it('mapcheck() returns lua mapping correctly', function()
exec_lua [[
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() print('jkl;') end,
+ })
]]
- assert.truthy(string.match(fn.mapcheck('asdf', 'n'), '^<Lua %d+>'))
+ matches('^<Lua %d+>', fn.mapcheck('asdf', 'n'))
end)
- it('maparg() returns lua mapping correctly', function()
+ it('maparg() and maplist() return lua mapping correctly', function()
eq(
0,
exec_lua([[
GlobalCount = 0
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]])
)
- assert.truthy(string.match(fn.maparg('asdf', 'n'), '^<Lua %d+>'))
+ matches('^<Lua %d+>', fn.maparg('asdf', 'n'))
local mapargs = fn.maparg('asdf', 'n', false, true)
mapargs.callback = nil
@@ -1010,11 +1020,20 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
call maparg('asdf', 'n', v:false, v:true).callback()
]])
eq(2, exec_lua([[return GlobalCount]]))
+
+ api.nvim_eval([[
+ maplist()->filter({_, m -> m.lhs == 'asdf'})->foreach({_, m -> m.callback()})
+ ]])
+ eq(3, exec_lua([[return GlobalCount]]))
end)
it('can make lua expr mappings replacing keycodes', function()
exec_lua [[
- vim.api.nvim_set_keymap('n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true })
+ vim.api.nvim_set_keymap('n', 'aa', '', {
+ callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end,
+ expr = true,
+ replace_keycodes = true,
+ })
]]
feed('aa')
@@ -1024,7 +1043,10 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('can make lua expr mappings without replacing keycodes', function()
exec_lua [[
- vim.api.nvim_set_keymap('i', 'aa', '', {callback = function() return '<space>' end, expr = true })
+ vim.api.nvim_set_keymap('i', 'aa', '', {
+ callback = function() return '<space>' end,
+ expr = true,
+ })
]]
feed('iaa<esc>')
@@ -1034,7 +1056,10 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('lua expr mapping returning nil is equivalent to returning an empty string', function()
exec_lua [[
- vim.api.nvim_set_keymap('i', 'aa', '', {callback = function() return nil end, expr = true })
+ vim.api.nvim_set_keymap('i', 'aa', '', {
+ callback = function() return nil end,
+ expr = true,
+ })
]]
feed('iaa<esc>')
@@ -1047,7 +1072,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
0,
exec_lua [[
VisibleCount = 0
- vim.api.nvim_set_keymap('i', '<F2>', '', {callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end})
+ vim.api.nvim_set_keymap('i', '<F2>', '', {
+ callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end,
+ })
return VisibleCount
]]
)
@@ -1060,7 +1087,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
0,
exec_lua [[
OpCount = 0
- vim.api.nvim_set_keymap('o', '<F2>', '', {callback = function() OpCount = OpCount + 1 end})
+ vim.api.nvim_set_keymap('o', '<F2>', '', {
+ callback = function() OpCount = OpCount + 1 end,
+ })
return OpCount
]]
)
@@ -1075,7 +1104,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -1085,7 +1116,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq(1, exec_lua [[return GlobalCount]])
exec_lua [[
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount - 1 end,
+ })
]]
feed('asdf\n')
@@ -1098,7 +1131,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_set_keymap('n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -1107,14 +1142,12 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq(1, exec_lua [[return GlobalCount]])
- exec_lua [[
- vim.api.nvim_del_keymap('n', 'asdf' )
- ]]
+ exec_lua [[vim.api.nvim_del_keymap('n', 'asdf' )]]
feed('asdf\n')
eq(1, exec_lua [[return GlobalCount]])
- eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
+ eq('\nNo mapping found', n.exec_capture('nmap asdf'))
end)
it('no double-free when unmapping simplifiable lua mappings', function()
@@ -1122,7 +1155,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_set_keymap('n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_set_keymap('n', '<C-I>', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -1131,29 +1166,30 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq(1, exec_lua [[return GlobalCount]])
- exec_lua [[
- vim.api.nvim_del_keymap('n', '<C-I>')
- ]]
+ exec_lua [[vim.api.nvim_del_keymap('n', '<C-I>')]]
feed('<C-I>\n')
eq(1, exec_lua [[return GlobalCount]])
- eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
+ eq('\nNo mapping found', n.exec_capture('nmap <C-I>'))
end)
it('can set descriptions on mappings', function()
api.nvim_set_keymap('n', 'lhs', 'rhs', { desc = 'map description' })
eq(generate_mapargs('n', 'lhs', 'rhs', { desc = 'map description' }), get_mapargs('n', 'lhs'))
- eq('\nn lhs rhs\n map description', helpers.exec_capture('nmap lhs'))
+ eq('\nn lhs rhs\n map description', n.exec_capture('nmap lhs'))
end)
it('can define !-mode abbreviations with lua callbacks', function()
exec_lua [[
GlobalCount = 0
- vim.api.nvim_set_keymap('!a', 'foo', '', {expr = true, callback = function()
- GlobalCount = GlobalCount + 1
- return tostring(GlobalCount)
- end})
+ vim.api.nvim_set_keymap('!a', 'foo', '', {
+ expr = true,
+ callback = function()
+ GlobalCount = GlobalCount + 1
+ return tostring(GlobalCount)
+ end,
+ })
]]
feed 'iThe foo and the bar and the foo again<esc>'
@@ -1166,10 +1202,13 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('can define insert mode abbreviations with lua callbacks', function()
exec_lua [[
GlobalCount = 0
- vim.api.nvim_set_keymap('ia', 'foo', '', {expr = true, callback = function()
- GlobalCount = GlobalCount + 1
- return tostring(GlobalCount)
- end})
+ vim.api.nvim_set_keymap('ia', 'foo', '', {
+ expr = true,
+ callback = function()
+ GlobalCount = GlobalCount + 1
+ return tostring(GlobalCount)
+ end,
+ })
]]
feed 'iThe foo and the bar and the foo again<esc>'
@@ -1182,10 +1221,13 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('can define cmdline mode abbreviations with lua callbacks', function()
exec_lua [[
GlobalCount = 0
- vim.api.nvim_set_keymap('ca', 'foo', '', {expr = true, callback = function()
- GlobalCount = GlobalCount + 1
- return tostring(GlobalCount)
- end})
+ vim.api.nvim_set_keymap('ca', 'foo', '', {
+ expr = true,
+ callback = function()
+ GlobalCount = GlobalCount + 1
+ return tostring(GlobalCount)
+ end,
+ })
]]
feed 'iThe foo and the bar and the foo again<esc>'
@@ -1290,7 +1332,7 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
it('does not crash when setting mapping in a non-existing buffer #13541', function()
pcall_err(api.nvim_buf_set_keymap, 100, '', 'lsh', 'irhs<Esc>', {})
- helpers.assert_alive()
+ n.assert_alive()
end)
it('can make lua mappings', function()
@@ -1298,7 +1340,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -1310,7 +1354,11 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
it('can make lua expr mappings replacing keycodes', function()
exec_lua [[
- vim.api.nvim_buf_set_keymap(0, 'n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true })
+ vim.api.nvim_buf_set_keymap(0, 'n', 'aa', '', {
+ callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end,
+ expr = true,
+ replace_keycodes = true,
+ })
]]
feed('aa')
@@ -1320,7 +1368,10 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
it('can make lua expr mappings without replacing keycodes', function()
exec_lua [[
- vim.api.nvim_buf_set_keymap(0, 'i', 'aa', '', {callback = function() return '<space>' end, expr = true })
+ vim.api.nvim_buf_set_keymap(0, 'i', 'aa', '', {
+ callback = function() return '<space>' end,
+ expr = true,
+ })
]]
feed('iaa<esc>')
@@ -1333,7 +1384,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -1343,7 +1396,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
eq(1, exec_lua [[return GlobalCount]])
exec_lua [[
- vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
+ vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount - 1 end,
+ })
]]
feed('asdf\n')
@@ -1356,7 +1411,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -1365,14 +1422,12 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
eq(1, exec_lua [[return GlobalCount]])
- exec_lua [[
- vim.api.nvim_buf_del_keymap(0, 'n', 'asdf' )
- ]]
+ exec_lua [[vim.api.nvim_buf_del_keymap(0, 'n', 'asdf' )]]
feed('asdf\n')
eq(1, exec_lua [[return GlobalCount]])
- eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
+ eq('\nNo mapping found', n.exec_capture('nmap asdf'))
end)
it('no double-free when unmapping simplifiable lua mappings', function()
@@ -1380,7 +1435,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
0,
exec_lua [[
GlobalCount = 0
- vim.api.nvim_buf_set_keymap(0, 'n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ vim.api.nvim_buf_set_keymap(0, 'n', '<C-I>', '', {
+ callback = function() GlobalCount = GlobalCount + 1 end,
+ })
return GlobalCount
]]
)
@@ -1389,13 +1446,11 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
eq(1, exec_lua [[return GlobalCount]])
- exec_lua [[
- vim.api.nvim_buf_del_keymap(0, 'n', '<C-I>')
- ]]
+ exec_lua [[vim.api.nvim_buf_del_keymap(0, 'n', '<C-I>')]]
feed('<C-I>\n')
eq(1, exec_lua [[return GlobalCount]])
- eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
+ eq('\nNo mapping found', n.exec_capture('nmap <C-I>'))
end)
end)
diff --git a/test/functional/api/menu_spec.lua b/test/functional/api/menu_spec.lua
index 44b9039393..76eef164c9 100644
--- a/test/functional/api/menu_spec.lua
+++ b/test/functional/api/menu_spec.lua
@@ -1,9 +1,9 @@
-local helpers = require('test.functional.helpers')(after_each)
+local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
-local clear = helpers.clear
-local command = helpers.command
-local feed = helpers.feed
+local clear = n.clear
+local command = n.command
+local feed = n.feed
describe('update_menu notification', function()
local screen
diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua
index 50c441792c..d2dd655b17 100644
--- a/test/functional/api/proc_spec.lua
+++ b/test/functional/api/proc_spec.lua
@@ -1,14 +1,15 @@
-local helpers = require('test.functional.helpers')(after_each)
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
-local clear = helpers.clear
-local eq = helpers.eq
-local fn = helpers.fn
-local neq = helpers.neq
-local nvim_argv = helpers.nvim_argv
-local request = helpers.request
-local retry = helpers.retry
+local clear = n.clear
+local eq = t.eq
+local fn = n.fn
+local neq = t.neq
+local nvim_argv = n.nvim_argv
+local request = n.request
+local retry = t.retry
local NIL = vim.NIL
-local is_os = helpers.is_os
+local is_os = t.is_os
describe('API', function()
before_each(clear)
diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua
index d1608a951c..7b4c4e8312 100644
--- a/test/functional/api/server_notifications_spec.lua
+++ b/test/functional/api/server_notifications_spec.lua
@@ -1,11 +1,12 @@
-local helpers = require('test.functional.helpers')(after_each)
-local assert_log = helpers.assert_log
-local eq, clear, eval, command, next_msg =
- helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.next_msg
-local api = helpers.api
-local exec_lua = helpers.exec_lua
-local retry = helpers.retry
-local assert_alive = helpers.assert_alive
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+local eq, clear, eval, command, next_msg = t.eq, n.clear, n.eval, n.command, n.next_msg
+local api = n.api
+local exec_lua = n.exec_lua
+local retry = t.retry
+local assert_alive = n.assert_alive
+local check_close = n.check_close
local testlog = 'Xtest-server-notify-log'
@@ -18,6 +19,7 @@ describe('notify', function()
end)
after_each(function()
+ check_close()
os.remove(testlog)
end)
@@ -31,18 +33,18 @@ describe('notify', function()
end)
end)
- describe('passing 0 as the channel id', function()
- it('sends the notification/args to all subscribed channels', function()
- api.nvim_subscribe('event2')
+ describe('channel id 0', function()
+ it('broadcasts the notification/args to all channels', function()
eval('rpcnotify(0, "event1", 1, 2, 3)')
eval('rpcnotify(0, "event2", 4, 5, 6)')
eval('rpcnotify(0, "event2", 7, 8, 9)')
+ eq({ 'notification', 'event1', { 1, 2, 3 } }, next_msg())
eq({ 'notification', 'event2', { 4, 5, 6 } }, next_msg())
eq({ 'notification', 'event2', { 7, 8, 9 } }, next_msg())
- api.nvim_unsubscribe('event2')
- api.nvim_subscribe('event1')
+
eval('rpcnotify(0, "event2", 10, 11, 12)')
eval('rpcnotify(0, "event1", 13, 14, 15)')
+ eq({ 'notification', 'event2', { 10, 11, 12 } }, next_msg())
eq({ 'notification', 'event1', { 13, 14, 15 } }, next_msg())
end)
@@ -75,17 +77,6 @@ describe('notify', function()
end)
end)
- it('unsubscribe non-existing event #8745', function()
- clear { env = {
- NVIM_LOG_FILE = testlog,
- } }
- api.nvim_subscribe('event1')
- api.nvim_unsubscribe('doesnotexist')
- assert_log("tried to unsubscribe unknown event 'doesnotexist'", testlog, 10)
- api.nvim_unsubscribe('event1')
- assert_alive()
- end)
-
it('cancels stale events on channel close', function()
local catchan = eval("jobstart(['cat'], {'rpc': v:true})")
local catpath = eval('exepath("cat")')
@@ -94,7 +85,6 @@ describe('notify', function()
exec_lua(
[[
vim.rpcnotify(..., "nvim_call_function", 'chanclose', {..., 'rpc'})
- vim.rpcnotify(..., "nvim_subscribe", "daily_rant")
return vim.api.nvim_get_chan_info(...)
]],
catchan
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 298dbac217..bdd340f6c6 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -1,17 +1,18 @@
-- Test server -> client RPC scenarios. Note: unlike `rpcnotify`, to evaluate
-- `rpcrequest` calls we need the client event loop to be running.
-local helpers = require('test.functional.helpers')(after_each)
-
-local clear, eval = helpers.clear, helpers.eval
-local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop
-local nvim_prog, command, fn = helpers.nvim_prog, helpers.command, helpers.fn
-local source, next_msg = helpers.source, helpers.next_msg
-local ok = helpers.ok
-local api = helpers.api
-local spawn, merge_args = helpers.spawn, helpers.merge_args
-local set_session = helpers.set_session
-local pcall_err = helpers.pcall_err
-local assert_alive = helpers.assert_alive
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+local clear, eval = n.clear, n.eval
+local eq, neq, run, stop = t.eq, t.neq, n.run, n.stop
+local nvim_prog, command, fn = n.nvim_prog, n.command, n.fn
+local source, next_msg = n.source, n.next_msg
+local ok = t.ok
+local api = n.api
+local spawn, merge_args = n.spawn, n.merge_args
+local set_session = n.set_session
+local pcall_err = t.pcall_err
+local assert_alive = n.assert_alive
describe('server -> client', function()
local cid
@@ -91,19 +92,19 @@ describe('server -> client', function()
local function on_request(method, args)
eq('rcall', method)
- local n = unpack(args) * 2
- if n <= 16 then
+ local _n = unpack(args) * 2
+ if _n <= 16 then
local cmd
- if n == 4 then
- cmd = 'let g:result2 = rpcrequest(' .. cid .. ', "rcall", ' .. n .. ')'
- elseif n == 8 then
- cmd = 'let g:result3 = rpcrequest(' .. cid .. ', "rcall", ' .. n .. ')'
- elseif n == 16 then
- cmd = 'let g:result4 = rpcrequest(' .. cid .. ', "rcall", ' .. n .. ')'
+ if _n == 4 then
+ cmd = 'let g:result2 = rpcrequest(' .. cid .. ', "rcall", ' .. _n .. ')'
+ elseif _n == 8 then
+ cmd = 'let g:result3 = rpcrequest(' .. cid .. ', "rcall", ' .. _n .. ')'
+ elseif _n == 16 then
+ cmd = 'let g:result4 = rpcrequest(' .. cid .. ', "rcall", ' .. _n .. ')'
end
command(cmd)
end
- return n
+ return _n
end
run(on_request, nil, on_setup)
end)
@@ -259,7 +260,7 @@ describe('server -> client', function()
pcall(fn.jobstop, jobid)
end)
- if helpers.skip(helpers.is_os('win')) then
+ if t.skip(t.is_os('win')) then
return
end
@@ -280,7 +281,7 @@ describe('server -> client', function()
end)
describe('connecting to another (peer) nvim', function()
- local nvim_argv = merge_args(helpers.nvim_argv, { '--headless' })
+ local nvim_argv = merge_args(n.nvim_argv, { '--headless' })
local function connect_test(server, mode, address)
local serverpid = fn.getpid()
local client = spawn(nvim_argv, false, nil, true)
@@ -363,6 +364,24 @@ describe('server -> client', function()
server:close()
client:close()
end)
+
+ it('via stdio, with many small flushes does not crash #23781', function()
+ source([[
+ let chan = jobstart([v:progpath, '--embed', '--headless', '-n', '-u', 'NONE', '-i', 'NONE'], { 'rpc':v:false })
+ call chansend(chan, 0Z94)
+ sleep 50m
+ call chansend(chan, 0Z00)
+ call chansend(chan, 0Z01)
+ call chansend(chan, 0ZAC)
+ call chansend(chan, 0Z6E76696D5F636F6D6D616E64)
+ call chansend(chan, 0Z91)
+ call chansend(chan, 0ZA5)
+ call chansend(chan, 0Z71616C6C21)
+ let g:statuses = jobwait([chan])
+ ]])
+ eq(eval('g:statuses'), { 0 })
+ assert_alive()
+ end)
end)
describe('connecting to its own pipe address', function()
diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua
index 36955c4ace..74858475c8 100644
--- a/test/functional/api/tabpage_spec.lua
+++ b/test/functional/api/tabpage_spec.lua
@@ -1,11 +1,15 @@
-local helpers = require('test.functional.helpers')(after_each)
-local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
-local api = helpers.api
-local fn = helpers.fn
-local request = helpers.request
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+local clear, eq, ok = n.clear, t.eq, t.ok
+local exec = n.exec
+local feed = n.feed
+local api = n.api
+local fn = n.fn
+local request = n.request
local NIL = vim.NIL
-local pcall_err = helpers.pcall_err
-local command = helpers.command
+local pcall_err = t.pcall_err
+local command = n.command
describe('api/tabpage', function()
before_each(clear)
@@ -86,6 +90,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/ui_spec.lua b/test/functional/api/ui_spec.lua
index 3e1f1ec965..2145db7f8a 100644
--- a/test/functional/api/ui_spec.lua
+++ b/test/functional/api/ui_spec.lua
@@ -1,14 +1,16 @@
-local helpers = require('test.functional.helpers')(after_each)
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
-local clear = helpers.clear
-local command = helpers.command
-local eq = helpers.eq
-local eval = helpers.eval
-local exec = helpers.exec
-local feed = helpers.feed
-local api = helpers.api
-local request = helpers.request
-local pcall_err = helpers.pcall_err
+
+local clear = n.clear
+local command = n.command
+local eq = t.eq
+local eval = n.eval
+local exec = n.exec
+local feed = n.feed
+local api = n.api
+local request = n.request
+local pcall_err = t.pcall_err
describe('nvim_ui_attach()', function()
before_each(function()
diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua
index c304f1aa88..5dad9978b7 100644
--- a/test/functional/api/version_spec.lua
+++ b/test/functional/api/version_spec.lua
@@ -1,6 +1,8 @@
-local helpers = require('test.functional.helpers')(after_each)
-local clear, fn, eq = helpers.clear, helpers.fn, helpers.eq
-local api = helpers.api
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+local clear, fn, eq = n.clear, n.fn, t.eq
+local api = n.api
local function read_mpack_file(fname)
local fd = io.open(fname, 'rb')
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 9a4a457637..fd0535aa51 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -1,42 +1,43 @@
-local helpers = require('test.functional.helpers')(after_each)
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local uv = vim.uv
local fmt = string.format
-local dedent = helpers.dedent
-local assert_alive = helpers.assert_alive
+local dedent = t.dedent
+local assert_alive = n.assert_alive
local NIL = vim.NIL
-local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq
-local command = helpers.command
-local command_output = helpers.api.nvim_command_output
-local exec = helpers.exec
-local exec_capture = helpers.exec_capture
-local eval = helpers.eval
-local expect = helpers.expect
-local fn = helpers.fn
-local api = helpers.api
-local matches = helpers.matches
+local clear, eq, neq = n.clear, t.eq, t.neq
+local command = n.command
+local command_output = n.api.nvim_command_output
+local exec = n.exec
+local exec_capture = n.exec_capture
+local eval = n.eval
+local expect = n.expect
+local fn = n.fn
+local api = n.api
+local matches = t.matches
local pesc = vim.pesc
-local mkdir_p = helpers.mkdir_p
-local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed
-local async_meths = helpers.async_meths
-local is_os = helpers.is_os
-local parse_context = helpers.parse_context
-local request = helpers.request
-local rmdir = helpers.rmdir
-local source = helpers.source
-local next_msg = helpers.next_msg
-local tmpname = helpers.tmpname
-local write_file = helpers.write_file
-local exec_lua = helpers.exec_lua
-local exc_exec = helpers.exc_exec
-local insert = helpers.insert
-local skip = helpers.skip
-
-local pcall_err = helpers.pcall_err
+local mkdir_p = n.mkdir_p
+local ok, nvim_async, feed = t.ok, n.nvim_async, n.feed
+local async_meths = n.async_meths
+local is_os = t.is_os
+local parse_context = n.parse_context
+local request = n.request
+local rmdir = n.rmdir
+local source = n.source
+local next_msg = n.next_msg
+local tmpname = t.tmpname
+local write_file = t.write_file
+local exec_lua = n.exec_lua
+local exc_exec = n.exc_exec
+local insert = n.insert
+local skip = t.skip
+
+local pcall_err = t.pcall_err
local format_string = require('test.format_string').format_string
-local intchar2lua = helpers.intchar2lua
-local mergedicts_copy = helpers.mergedicts_copy
+local intchar2lua = t.intchar2lua
+local mergedicts_copy = t.mergedicts_copy
local endswith = vim.endswith
describe('API', function()
@@ -559,6 +560,16 @@ describe('API', function()
eq('Vim:E121: Undefined variable: bogus', pcall_err(request, 'nvim_eval', 'bogus expression'))
eq('', eval('v:errmsg')) -- v:errmsg was not updated.
end)
+
+ it('can return Lua function to Lua code', function()
+ eq(
+ [["a string with \"double quotes\" and 'single quotes'"]],
+ exec_lua([=[
+ local fun = vim.api.nvim_eval([[luaeval('string.format')]])
+ return fun('%q', [[a string with "double quotes" and 'single quotes']])
+ ]=])
+ )
+ end)
end)
describe('nvim_call_function', function()
@@ -624,6 +635,16 @@ describe('API', function()
pcall_err(request, 'nvim_call_function', 'Foo', too_many_args)
)
end)
+
+ it('can return Lua function to Lua code', function()
+ eq(
+ [["a string with \"double quotes\" and 'single quotes'"]],
+ exec_lua([=[
+ local fun = vim.api.nvim_call_function('luaeval', { 'string.format' })
+ return fun('%q', [[a string with "double quotes" and 'single quotes']])
+ ]=])
+ )
+ end)
end)
describe('nvim_call_dict_function', function()
@@ -702,18 +723,18 @@ describe('API', function()
end)
after_each(function()
- helpers.rmdir('Xtestdir')
+ n.rmdir('Xtestdir')
end)
it('works', function()
api.nvim_set_current_dir('Xtestdir')
- eq(fn.getcwd(), start_dir .. helpers.get_pathsep() .. 'Xtestdir')
+ eq(start_dir .. n.get_pathsep() .. 'Xtestdir', fn.getcwd())
end)
it('sets previous directory', function()
api.nvim_set_current_dir('Xtestdir')
command('cd -')
- eq(fn.getcwd(), start_dir)
+ eq(start_dir, fn.getcwd())
end)
end)
@@ -1269,7 +1290,7 @@ describe('API', function()
api.nvim_paste('', true, 3)
screen:expect([[
|
- ~ |*2
+ {1:~ }|*2
:Foo^ |
]])
end)
@@ -1280,8 +1301,8 @@ describe('API', function()
api.nvim_paste('normal! \023\022\006\027', true, -1)
screen:expect([[
|
- ~ |*2
- :normal! ^W^V^F^[^ |
+ {1:~ }|*2
+ :normal! {18:^W^V^F^[}^ |
]])
end)
it('crlf=false does not break lines at CR, CRLF', function()
@@ -1467,7 +1488,7 @@ describe('API', function()
eq(NIL, api.nvim_get_var('Unknown_script_func'))
-- Check if autoload works properly
- local pathsep = helpers.get_pathsep()
+ local pathsep = n.get_pathsep()
local xconfig = 'Xhome' .. pathsep .. 'Xconfig'
local xdata = 'Xhome' .. pathsep .. 'Xdata'
local autoload_folder = table.concat({ xconfig, 'nvim', 'autoload' }, pathsep)
@@ -1593,14 +1614,12 @@ describe('API', function()
api.nvim_set_option_value('equalalways', false, {})
local status, rv = pcall(command_output, 'verbose set equalalways?')
eq(true, status)
- ok(
- nil ~= string.find(rv, 'noequalalways\n' .. '\tLast set from API client %(channel id %d+%)')
- )
+ matches('noequalalways\n' .. '\tLast set from API client %(channel id %d+%)', rv)
api.nvim_exec_lua('vim.api.nvim_set_option_value("equalalways", true, {})', {})
status, rv = pcall(command_output, 'verbose set equalalways?')
eq(true, status)
- eq(' equalalways\n\tLast set from Lua', rv)
+ eq(' equalalways\n\tLast set from Lua (run Nvim with -V1 for more details)', rv)
end)
it('updates whether the option has ever been set #25025', function()
@@ -1953,7 +1972,7 @@ describe('API', function()
describe('RPC (K_EVENT)', function()
it('does not complete ("interrupt") normal-mode operator-pending #6166', function()
- helpers.insert([[
+ n.insert([[
FIRST LINE
SECOND LINE]])
api.nvim_input('gg')
@@ -1981,16 +2000,16 @@ describe('API', function()
-- Make any RPC request (can be non-async: op-pending does not block).
api.nvim_get_current_buf()
screen:expect([[
- ^a$ |
- b$ |
- c$ |
+ ^a{1:$} |
+ b{1:$} |
+ c{1:$} |
|
]])
end)
it('does not complete ("interrupt") normal-mode map-pending #6166', function()
command("nnoremap dd :let g:foo='it worked...'<CR>")
- helpers.insert([[
+ n.insert([[
FIRST LINE
SECOND LINE]])
api.nvim_input('gg')
@@ -2002,13 +2021,13 @@ describe('API', function()
expect([[
FIRST LINE
SECOND LINE]])
- eq('it worked...', helpers.eval('g:foo'))
+ eq('it worked...', n.eval('g:foo'))
end)
it('does not complete ("interrupt") insert-mode map-pending #6166', function()
command('inoremap xx foo')
command('set timeoutlen=9999')
- helpers.insert([[
+ n.insert([[
FIRST LINE
SECOND LINE]])
api.nvim_input('ix')
@@ -2155,35 +2174,32 @@ describe('API', function()
describe('nvim_replace_termcodes', function()
it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function()
- eq('\128\254X', helpers.api.nvim_replace_termcodes('\128', true, true, true))
+ eq('\128\254X', n.api.nvim_replace_termcodes('\128', true, true, true))
end)
it('leaves non-K_SPECIAL string unchanged', function()
- eq('abc', helpers.api.nvim_replace_termcodes('abc', true, true, true))
+ eq('abc', n.api.nvim_replace_termcodes('abc', true, true, true))
end)
it('converts <expressions>', function()
- eq('\\', helpers.api.nvim_replace_termcodes('<Leader>', true, true, true))
+ eq('\\', n.api.nvim_replace_termcodes('<Leader>', true, true, true))
end)
it('converts <LeftMouse> to K_SPECIAL KS_EXTRA KE_LEFTMOUSE', function()
-- K_SPECIAL KS_EXTRA KE_LEFTMOUSE
-- 0x80 0xfd 0x2c
-- 128 253 44
- eq('\128\253\44', helpers.api.nvim_replace_termcodes('<LeftMouse>', true, true, true))
+ eq('\128\253\44', n.api.nvim_replace_termcodes('<LeftMouse>', true, true, true))
end)
it('converts keycodes', function()
- eq(
- '\nx\27x\rx<x',
- helpers.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, true)
- )
+ eq('\nx\27x\rx<x', n.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, true))
end)
it('does not convert keycodes if special=false', function()
eq(
'<NL>x<Esc>x<CR>x<lt>x',
- helpers.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, false)
+ n.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, false)
)
end)
@@ -2212,18 +2228,18 @@ describe('API', function()
api.nvim_feedkeys(':let x1="…"\n', '', true)
-- Both nvim_replace_termcodes and nvim_feedkeys escape \x80
- local inp = helpers.api.nvim_replace_termcodes(':let x2="…"<CR>', true, true, true)
+ local inp = n.api.nvim_replace_termcodes(':let x2="…"<CR>', true, true, true)
api.nvim_feedkeys(inp, '', true) -- escape_ks=true
-- nvim_feedkeys with K_SPECIAL escaping disabled
- inp = helpers.api.nvim_replace_termcodes(':let x3="…"<CR>', true, true, true)
+ inp = n.api.nvim_replace_termcodes(':let x3="…"<CR>', true, true, true)
api.nvim_feedkeys(inp, '', false) -- escape_ks=false
- helpers.stop()
+ n.stop()
end
-- spin the loop a bit
- helpers.run(nil, nil, on_setup)
+ n.run(nil, nil, on_setup)
eq('…', api.nvim_get_var('x1'))
-- Because of the double escaping this is neq
@@ -2366,7 +2382,7 @@ describe('API', function()
{0:~ }|*6
{1:very fail} |
]])
- helpers.poke_eventloop()
+ n.poke_eventloop()
-- shows up to &cmdheight lines
async_meths.nvim_err_write('more fail\ntoo fail\n')
@@ -2678,7 +2694,7 @@ describe('API', function()
describe('nvim_list_runtime_paths', function()
setup(function()
- local pathsep = helpers.get_pathsep()
+ local pathsep = n.get_pathsep()
mkdir_p('Xtest' .. pathsep .. 'a')
mkdir_p('Xtest' .. pathsep .. 'b')
end)
@@ -2723,7 +2739,7 @@ describe('API', function()
it('can throw exceptions', function()
local status, err = pcall(api.nvim_get_option_value, 'invalid-option', {})
eq(false, status)
- ok(err:match("Unknown option 'invalid%-option'") ~= nil)
+ matches("Unknown option 'invalid%-option'", err)
end)
it('does not truncate error message <1 MB #5984', function()
@@ -2736,10 +2752,7 @@ describe('API', function()
it('does not leak memory on incorrect argument types', function()
local status, err = pcall(api.nvim_set_current_dir, { 'not', 'a', 'dir' })
eq(false, status)
- ok(
- err:match(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String')
- ~= nil
- )
+ matches(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String', err)
end)
describe('nvim_parse_expression', function()
@@ -3135,10 +3148,60 @@ describe('API', function()
-- nowadays this works because we don't execute any spurious autocmds at all #24824
assert_alive()
end)
+
+ it('no memory leak when autocommands load the buffer immediately', function()
+ exec([[
+ autocmd BufNew * ++once call bufload(expand("<abuf>")->str2nr())
+ \| let loaded = bufloaded(expand("<abuf>")->str2nr())
+ ]])
+ api.nvim_create_buf(false, true)
+ eq(1, eval('g:loaded'))
+ end)
+
+ it('creating scratch buffer where autocommands set &swapfile works', function()
+ exec([[
+ autocmd BufNew * ++once execute expand("<abuf>") "buffer"
+ \| file foobar
+ \| setlocal swapfile
+ ]])
+ local new_buf = api.nvim_create_buf(false, true)
+ neq('', fn.swapname(new_buf))
+ end)
+
+ it('fires expected autocommands', function()
+ exec([=[
+ " Append the &buftype to check autocommands trigger *after* the buffer was configured to be
+ " scratch, if applicable.
+ autocmd BufNew * let fired += [["BufNew", expand("<abuf>")->str2nr(),
+ \ getbufvar(expand("<abuf>")->str2nr(), "&buftype")]]
+ autocmd BufAdd * let fired += [["BufAdd", expand("<abuf>")->str2nr(),
+ \ getbufvar(expand("<abuf>")->str2nr(), "&buftype")]]
+
+ " Don't want to see OptionSet; buffer options set from passing true for "scratch", etc.
+ " should be configured invisibly, and before autocommands.
+ autocmd OptionSet * let fired += [["OptionSet", expand("<amatch>")]]
+
+ let fired = []
+ ]=])
+ local new_buf = api.nvim_create_buf(false, false)
+ eq({ { 'BufNew', new_buf, '' } }, eval('g:fired'))
+
+ command('let fired = []')
+ new_buf = api.nvim_create_buf(false, true)
+ eq({ { 'BufNew', new_buf, 'nofile' } }, eval('g:fired'))
+
+ command('let fired = []')
+ new_buf = api.nvim_create_buf(true, false)
+ eq({ { 'BufNew', new_buf, '' }, { 'BufAdd', new_buf, '' } }, eval('g:fired'))
+
+ command('let fired = []')
+ new_buf = api.nvim_create_buf(true, true)
+ eq({ { 'BufNew', new_buf, 'nofile' }, { 'BufAdd', new_buf, 'nofile' } }, eval('g:fired'))
+ end)
end)
describe('nvim_get_runtime_file', function()
- local p = helpers.alter_slashes
+ local p = n.alter_slashes
it('can find files', function()
eq({}, api.nvim_get_runtime_file('bork.borkbork', false))
eq({}, api.nvim_get_runtime_file('bork.borkbork', true))
@@ -3365,13 +3428,13 @@ describe('API', function()
{desc="(global option, fallback requested) points to global", linenr=9, sid=1, args={'completeopt', {}}},
}
- for _, t in pairs(tests) do
- it(t.desc, function()
+ for _, test in pairs(tests) do
+ it(test.desc, function()
-- Switch to the target buffer/window so that curbuf/curwin are used.
api.nvim_set_current_win(wins[2])
- local info = api.nvim_get_option_info2(unpack(t.args))
- eq(t.linenr, info.last_set_linenr)
- eq(t.sid, info.last_set_sid)
+ local info = api.nvim_get_option_info2(unpack(test.args))
+ eq(test.linenr, info.last_set_linenr)
+ eq(test.sid, info.last_set_sid)
end)
end
@@ -3492,9 +3555,9 @@ describe('API', function()
false,
{ width = 79, height = 31, row = 1, col = 1, relative = 'editor' }
)
- local t = api.nvim_open_term(b, {})
+ local term = api.nvim_open_term(b, {})
- api.nvim_chan_send(t, io.open('test/functional/fixtures/smile2.cat', 'r'):read('*a'))
+ api.nvim_chan_send(term, io.open('test/functional/fixtures/smile2.cat', 'r'):read('*a'))
screen:expect {
grid = [[
^ |
@@ -3620,7 +3683,7 @@ describe('API', function()
api.nvim_buf_set_name(buf, 'mybuf')
local mark = api.nvim_get_mark('F', {})
-- Compare the path tail only
- assert(string.find(mark[4], 'mybuf$'))
+ matches('mybuf$', mark[4])
eq({ 2, 2, buf, mark[4] }, mark)
end)
it('validation', function()
@@ -3855,13 +3918,13 @@ describe('API', function()
norm 4G
]])
eq({
- str = '││aabb 4 ',
+ str = '││bbaa 4 ',
width = 9,
highlights = {
{ group = 'CursorLineFold', start = 0 },
{ group = 'Normal', start = 6 },
- { group = 'IncSearch', start = 6 },
- { group = 'ErrorMsg', start = 8 },
+ { group = 'ErrorMsg', start = 6 },
+ { group = 'IncSearch', start = 8 },
{ group = 'Normal', start = 10 },
},
}, api.nvim_eval_statusline(
@@ -4953,4 +5016,218 @@ describe('API', function()
eq(false, exec_lua('return _G.success'))
end)
end)
+
+ it('nvim__redraw', function()
+ local screen = Screen.new(60, 5)
+ screen:attach()
+ local win = api.nvim_get_current_win()
+ eq('at least one action required', pcall_err(api.nvim__redraw, {}))
+ eq('at least one action required', pcall_err(api.nvim__redraw, { buf = 0 }))
+ eq('at least one action required', pcall_err(api.nvim__redraw, { win = 0 }))
+ eq("cannot use both 'buf' and 'win'", pcall_err(api.nvim__redraw, { buf = 0, win = 0 }))
+ feed(':echo getchar()<CR>')
+ fn.setline(1, 'foobar')
+ command('vnew')
+ fn.setline(1, 'foobaz')
+ -- Can flush pending screen updates
+ api.nvim__redraw({ flush = true })
+ screen:expect({
+ grid = [[
+ foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ ^:echo getchar() |
+ ]],
+ })
+ -- Can update the grid cursor position #20793
+ api.nvim__redraw({ cursor = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ :echo getchar() |
+ ]],
+ })
+ -- Also in non-current window
+ api.nvim__redraw({ cursor = true, win = win })
+ screen:expect({
+ grid = [[
+ foobaz │^foobar |
+ {1:~ }│{1:~ }|*2
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'statusline' in a single window
+ api.nvim_set_option_value('statusline', 'statusline1', { win = 0 })
+ api.nvim_set_option_value('statusline', 'statusline2', { win = win })
+ api.nvim__redraw({ cursor = true, win = 0, statusline = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline1 }{2:[No Name] [+] }|
+ :echo getchar() |
+ ]],
+ })
+ api.nvim__redraw({ win = win, statusline = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline1 }{2:statusline2 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'statusline' in all windows
+ api.nvim_set_option_value('statusline', '', { win = win })
+ api.nvim_set_option_value('statusline', 'statusline3', {})
+ api.nvim__redraw({ statusline = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'statuscolumn'
+ api.nvim_set_option_value('statuscolumn', 'statuscolumn', { win = win })
+ api.nvim__redraw({ statuscolumn = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │{8:statuscolumn}foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'winbar'
+ api.nvim_set_option_value('winbar', 'winbar', { win = 0 })
+ api.nvim__redraw({ win = 0, winbar = true })
+ screen:expect({
+ grid = [[
+ {5:^winbar }│{8:statuscolumn}foobar |
+ foobaz │{1:~ }|
+ {1:~ }│{1:~ }|
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'tabline'
+ api.nvim_set_option_value('showtabline', 2, {})
+ api.nvim_set_option_value('tabline', 'tabline', {})
+ api.nvim__redraw({ tabline = true })
+ screen:expect({
+ grid = [[
+ {2:^tabline }|
+ {5:winbar }│{8:statuscolumn}foobar |
+ foobaz │{1:~ }|
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update multiple status widgets
+ api.nvim_set_option_value('tabline', 'tabline2', {})
+ api.nvim_set_option_value('statusline', 'statusline4', {})
+ api.nvim__redraw({ statusline = true, tabline = true })
+ screen:expect({
+ grid = [[
+ {2:^tabline2 }|
+ {5:winbar }│{8:statuscolumn}foobar |
+ foobaz │{1:~ }|
+ {3:statusline4 }{2:statusline4 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update all status widgets
+ api.nvim_set_option_value('tabline', 'tabline3', {})
+ api.nvim_set_option_value('statusline', 'statusline5', {})
+ api.nvim_set_option_value('statuscolumn', 'statuscolumn2', {})
+ api.nvim_set_option_value('winbar', 'winbar2', {})
+ api.nvim__redraw({ statuscolumn = true, statusline = true, tabline = true, winbar = true })
+ screen:expect({
+ grid = [[
+ {2:^tabline3 }|
+ {5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}foobaz │{8:statuscolumn}foobar |
+ {3:statusline5 }{2:statusline5 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update status widget for a specific window
+ feed('<CR><CR>')
+ command('let g:status=0')
+ api.nvim_set_option_value('statusline', '%{%g:status%}', { win = 0 })
+ command('vsplit')
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
+ {3:0 }{2:0 statusline5 }|
+ 13 |
+ ]],
+ })
+ command('let g:status=1')
+ api.nvim__redraw({ win = 0, statusline = true })
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
+ {3:1 }{2:0 statusline5 }|
+ 13 |
+ ]],
+ })
+ -- Can update status widget for a specific buffer
+ command('let g:status=2')
+ api.nvim__redraw({ buf = 0, statusline = true })
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
+ {3:2 }{2:2 statusline5 }|
+ 13 |
+ ]],
+ })
+ -- valid = true does not draw any lines on its own
+ exec_lua([[
+ _G.lines = 0
+ ns = vim.api.nvim_create_namespace('')
+ vim.api.nvim_set_decoration_provider(ns, {
+ on_win = function()
+ if _G.do_win then
+ vim.api.nvim_buf_set_extmark(0, ns, 0, 0, { hl_group = 'IncSearch', end_col = 6 })
+ end
+ end,
+ on_line = function()
+ _G.lines = _G.lines + 1
+ end,
+ })
+ ]])
+ local lines = exec_lua('return lines')
+ api.nvim__redraw({ buf = 0, valid = true, flush = true })
+ eq(lines, exec_lua('return _G.lines'))
+ -- valid = false does
+ api.nvim__redraw({ buf = 0, valid = false, flush = true })
+ neq(lines, exec_lua('return _G.lines'))
+ -- valid = true does redraw lines if affected by on_win callback
+ exec_lua('_G.do_win = true')
+ api.nvim__redraw({ buf = 0, valid = true, flush = true })
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}{2:^foobaz} │{8:statuscolumn2}{2:foobaz}│{8:statuscolumn}foobar |
+ {3:2 }{2:2 statusline5 }|
+ 13 |
+ ]],
+ })
+ -- takes buffer line count from correct buffer with "win" and {0, -1} "range"
+ api.nvim__redraw({ win = 0, range = { 0, -1 } })
+ n.assert_alive()
+ end)
end)
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 097a546ef2..15b9b0945c 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -1,27 +1,29 @@
-local helpers = require('test.functional.helpers')(after_each)
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
+
local clear, curbuf, curbuf_contents, curwin, eq, neq, matches, ok, feed, insert, eval =
- helpers.clear,
- helpers.api.nvim_get_current_buf,
- helpers.curbuf_contents,
- helpers.api.nvim_get_current_win,
- helpers.eq,
- helpers.neq,
- helpers.matches,
- helpers.ok,
- helpers.feed,
- helpers.insert,
- helpers.eval
-local poke_eventloop = helpers.poke_eventloop
-local exec = helpers.exec
-local exec_lua = helpers.exec_lua
-local fn = helpers.fn
-local request = helpers.request
+ n.clear,
+ n.api.nvim_get_current_buf,
+ n.curbuf_contents,
+ n.api.nvim_get_current_win,
+ t.eq,
+ t.neq,
+ t.matches,
+ t.ok,
+ n.feed,
+ n.insert,
+ n.eval
+local poke_eventloop = n.poke_eventloop
+local exec = n.exec
+local exec_lua = n.exec_lua
+local fn = n.fn
+local request = n.request
local NIL = vim.NIL
-local api = helpers.api
-local command = helpers.command
-local pcall_err = helpers.pcall_err
-local assert_alive = helpers.assert_alive
+local api = n.api
+local command = n.command
+local pcall_err = t.pcall_err
+local assert_alive = n.assert_alive
describe('API/win', function()
before_each(clear)
@@ -111,6 +113,44 @@ describe('API/win', function()
api.nvim_win_set_buf(new_win, next_buf)
eq(next_buf, api.nvim_win_get_buf(new_win))
end)
+
+ describe("with 'autochdir'", function()
+ local topdir
+ local otherbuf
+ local oldwin
+ local newwin
+
+ before_each(function()
+ command('set shellslash')
+ topdir = fn.getcwd()
+ t.mkdir(topdir .. '/Xacd')
+ t.mkdir(topdir .. '/Xacd/foo')
+ otherbuf = api.nvim_create_buf(false, true)
+ api.nvim_buf_set_name(otherbuf, topdir .. '/Xacd/baz.txt')
+
+ command('set autochdir')
+ command('edit Xacd/foo/bar.txt')
+ eq(topdir .. '/Xacd/foo', fn.getcwd())
+
+ oldwin = api.nvim_get_current_win()
+ command('vsplit')
+ newwin = api.nvim_get_current_win()
+ end)
+
+ after_each(function()
+ n.rmdir(topdir .. '/Xacd')
+ end)
+
+ it('does not change cwd with non-current window', function()
+ api.nvim_win_set_buf(oldwin, otherbuf)
+ eq(topdir .. '/Xacd/foo', fn.getcwd())
+ end)
+
+ it('changes cwd with current window', function()
+ api.nvim_win_set_buf(newwin, otherbuf)
+ eq(topdir .. '/Xacd', fn.getcwd())
+ end)
+ end)
end)
describe('{get,set}_cursor', function()
@@ -1147,27 +1187,6 @@ describe('API/win', function()
end)
describe('open_win', function()
- it('noautocmd option works', function()
- command('autocmd BufEnter,BufLeave,BufWinEnter * let g:fired = 1')
- api.nvim_open_win(api.nvim_create_buf(true, true), true, {
- relative = 'win',
- row = 3,
- col = 3,
- width = 12,
- height = 3,
- noautocmd = true,
- })
- eq(0, fn.exists('g:fired'))
- api.nvim_open_win(api.nvim_create_buf(true, true), true, {
- relative = 'win',
- row = 3,
- col = 3,
- width = 12,
- height = 3,
- })
- eq(1, fn.exists('g:fired'))
- end)
-
it('disallowed in cmdwin if enter=true or buf=cmdwin_buf', function()
local new_buf = api.nvim_create_buf(true, true)
feed('q:')
@@ -1233,81 +1252,151 @@ describe('API/win', function()
eq(wins_before, api.nvim_list_wins())
end)
- it('creates a split window', function()
- local win = api.nvim_open_win(0, true, {
- vertical = false,
- })
- eq('', api.nvim_win_get_config(win).relative)
+ describe('creates a split window above', function()
+ local function test_open_win_split_above(key, val)
+ local initial_win = api.nvim_get_current_win()
+ local win = api.nvim_open_win(0, true, {
+ [key] = val,
+ height = 10,
+ })
+ eq('', api.nvim_win_get_config(win).relative)
+ eq(10, api.nvim_win_get_height(win))
+ local layout = fn.winlayout()
+ eq({
+ 'col',
+ {
+ { 'leaf', win },
+ { 'leaf', initial_win },
+ },
+ }, layout)
+ end
+
+ it("with split = 'above'", function()
+ test_open_win_split_above('split', 'above')
+ end)
+
+ it("with vertical = false and 'nosplitbelow'", function()
+ api.nvim_set_option_value('splitbelow', false, {})
+ test_open_win_split_above('vertical', false)
+ end)
end)
- it('creates split windows in the correct direction', function()
- local initial_win = api.nvim_get_current_win()
- local win = api.nvim_open_win(0, true, {
- vertical = true,
- })
- eq('', api.nvim_win_get_config(win).relative)
+ describe('creates a split window below', function()
+ local function test_open_win_split_below(key, val)
+ local initial_win = api.nvim_get_current_win()
+ local win = api.nvim_open_win(0, true, {
+ [key] = val,
+ height = 15,
+ })
+ eq('', api.nvim_win_get_config(win).relative)
+ eq(15, api.nvim_win_get_height(win))
+ local layout = fn.winlayout()
+ eq({
+ 'col',
+ {
+ { 'leaf', initial_win },
+ { 'leaf', win },
+ },
+ }, layout)
+ end
- local layout = fn.winlayout()
+ it("with split = 'below'", function()
+ test_open_win_split_below('split', 'below')
+ end)
- eq({
- 'row',
- {
- { 'leaf', win },
- { 'leaf', initial_win },
- },
- }, layout)
+ it("with vertical = false and 'splitbelow'", function()
+ api.nvim_set_option_value('splitbelow', true, {})
+ test_open_win_split_below('vertical', false)
+ end)
end)
- it("respects the 'split' option", function()
- local initial_win = api.nvim_get_current_win()
- local win = api.nvim_open_win(0, true, {
- split = 'below',
- })
- eq('', api.nvim_win_get_config(win).relative)
+ describe('creates a split window to the left', function()
+ local function test_open_win_split_left(key, val)
+ local initial_win = api.nvim_get_current_win()
+ local win = api.nvim_open_win(0, true, {
+ [key] = val,
+ width = 25,
+ })
+ eq('', api.nvim_win_get_config(win).relative)
+ eq(25, api.nvim_win_get_width(win))
+ local layout = fn.winlayout()
+ eq({
+ 'row',
+ {
+ { 'leaf', win },
+ { 'leaf', initial_win },
+ },
+ }, layout)
+ end
- local layout = fn.winlayout()
+ it("with split = 'left'", function()
+ test_open_win_split_left('split', 'left')
+ end)
- eq({
- 'col',
- {
- { 'leaf', initial_win },
- { 'leaf', win },
- },
- }, layout)
+ it("with vertical = true and 'nosplitright'", function()
+ api.nvim_set_option_value('splitright', false, {})
+ test_open_win_split_left('vertical', true)
+ end)
end)
- it(
- "doesn't change tp_curwin when splitting window in non-current tab with enter=false",
- function()
- local tab1 = api.nvim_get_current_tabpage()
- local tab1_win = api.nvim_get_current_win()
+ describe('creates a split window to the right', function()
+ local function test_open_win_split_right(key, val)
+ local initial_win = api.nvim_get_current_win()
+ local win = api.nvim_open_win(0, true, {
+ [key] = val,
+ width = 30,
+ })
+ eq('', api.nvim_win_get_config(win).relative)
+ eq(30, api.nvim_win_get_width(win))
+ local layout = fn.winlayout()
+ eq({
+ 'row',
+ {
+ { 'leaf', initial_win },
+ { 'leaf', win },
+ },
+ }, layout)
+ end
+
+ it("with split = 'right'", function()
+ test_open_win_split_right('split', 'right')
+ end)
- helpers.command('tabnew')
- local tab2 = api.nvim_get_current_tabpage()
- local tab2_win = api.nvim_get_current_win()
+ it("with vertical = true and 'splitright'", function()
+ api.nvim_set_option_value('splitright', true, {})
+ test_open_win_split_right('vertical', true)
+ end)
+ end)
- eq({ tab1_win, tab2_win }, api.nvim_list_wins())
- eq({ tab1, tab2 }, api.nvim_list_tabpages())
+ it("doesn't change tp_curwin when splitting window in another tab with enter=false", function()
+ local tab1 = api.nvim_get_current_tabpage()
+ local tab1_win = api.nvim_get_current_win()
- api.nvim_set_current_tabpage(tab1)
- eq(tab1_win, api.nvim_get_current_win())
+ n.command('tabnew')
+ local tab2 = api.nvim_get_current_tabpage()
+ local tab2_win = api.nvim_get_current_win()
- local tab2_prevwin = fn.tabpagewinnr(tab2, '#')
+ eq({ tab1_win, tab2_win }, api.nvim_list_wins())
+ eq({ tab1, tab2 }, api.nvim_list_tabpages())
- -- split in tab2 whine in tab2, with enter = false
- local tab2_win2 = api.nvim_open_win(api.nvim_create_buf(false, true), false, {
- win = tab2_win,
- split = 'right',
- })
- eq(tab1_win, api.nvim_get_current_win()) -- we should still be in the first tp
- eq(tab1_win, api.nvim_tabpage_get_win(tab1))
+ api.nvim_set_current_tabpage(tab1)
+ eq(tab1_win, api.nvim_get_current_win())
- eq(tab2_win, api.nvim_tabpage_get_win(tab2)) -- tab2's tp_curwin should not have changed
- eq(tab2_prevwin, fn.tabpagewinnr(tab2, '#')) -- tab2's tp_prevwin should not have changed
- eq({ tab1_win, tab2_win, tab2_win2 }, api.nvim_list_wins())
- eq({ tab2_win, tab2_win2 }, api.nvim_tabpage_list_wins(tab2))
- end
- )
+ local tab2_prevwin = fn.tabpagewinnr(tab2, '#')
+
+ -- split in tab2 whine in tab2, with enter = false
+ local tab2_win2 = api.nvim_open_win(api.nvim_create_buf(false, true), false, {
+ win = tab2_win,
+ split = 'right',
+ })
+ eq(tab1_win, api.nvim_get_current_win()) -- we should still be in the first tp
+ eq(tab1_win, api.nvim_tabpage_get_win(tab1))
+
+ eq(tab2_win, api.nvim_tabpage_get_win(tab2)) -- tab2's tp_curwin should not have changed
+ eq(tab2_prevwin, fn.tabpagewinnr(tab2, '#')) -- tab2's tp_prevwin should not have changed
+ eq({ tab1_win, tab2_win, tab2_win2 }, api.nvim_list_wins())
+ eq({ tab2_win, tab2_win2 }, api.nvim_tabpage_list_wins(tab2))
+ end)
it('creates splits in the correct location', function()
local first_win = api.nvim_get_current_win()
@@ -1364,6 +1453,400 @@ describe('API/win', function()
},
}, layout)
end)
+
+ it('opens floating windows in other tabpages', function()
+ local first_win = api.nvim_get_current_win()
+ local first_tab = api.nvim_get_current_tabpage()
+
+ command('tabnew')
+ local new_tab = api.nvim_get_current_tabpage()
+ local win = api.nvim_open_win(0, false, {
+ relative = 'win',
+ win = first_win,
+ width = 5,
+ height = 5,
+ row = 1,
+ col = 1,
+ })
+ eq(api.nvim_win_get_tabpage(win), first_tab)
+ eq(api.nvim_get_current_tabpage(), new_tab)
+ end)
+
+ it('switches to new windows in non-current tabpages when enter=true', function()
+ local first_win = api.nvim_get_current_win()
+ local first_tab = api.nvim_get_current_tabpage()
+ command('tabnew')
+ local win = api.nvim_open_win(0, true, {
+ relative = 'win',
+ win = first_win,
+ width = 5,
+ height = 5,
+ row = 1,
+ col = 1,
+ })
+ eq(api.nvim_win_get_tabpage(win), first_tab)
+ eq(api.nvim_get_current_tabpage(), first_tab)
+ 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('noautocmd option works', function()
+ local info = setup_tabbed_autocmd_test()
+
+ api.nvim_open_win(
+ info.other_buf,
+ true,
+ { split = 'left', win = info.tab2_curwin, noautocmd = true }
+ )
+ eq({}, eval('result'))
+
+ api.nvim_open_win(
+ info.orig_buf,
+ true,
+ { relative = 'editor', row = 0, col = 0, width = 10, height = 10, noautocmd = true }
+ )
+ eq({}, eval('result'))
+ 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)
+
+ describe("with 'autochdir'", function()
+ local topdir
+ local otherbuf
+
+ before_each(function()
+ command('set shellslash')
+ topdir = fn.getcwd()
+ t.mkdir(topdir .. '/Xacd')
+ t.mkdir(topdir .. '/Xacd/foo')
+ otherbuf = api.nvim_create_buf(false, true)
+ api.nvim_buf_set_name(otherbuf, topdir .. '/Xacd/baz.txt')
+
+ command('set autochdir')
+ command('edit Xacd/foo/bar.txt')
+ eq(topdir .. '/Xacd/foo', fn.getcwd())
+ end)
+
+ after_each(function()
+ n.rmdir(topdir .. '/Xacd')
+ end)
+
+ it('does not change cwd with enter=false #15280', function()
+ api.nvim_open_win(
+ otherbuf,
+ false,
+ { relative = 'editor', height = 5, width = 5, row = 5, col = 5 }
+ )
+ eq(topdir .. '/Xacd/foo', fn.getcwd())
+ end)
+
+ it('changes cwd with enter=true', function()
+ api.nvim_open_win(
+ otherbuf,
+ true,
+ { relative = 'editor', height = 5, width = 5, row = 5, col = 5 }
+ )
+ eq(topdir .. '/Xacd', fn.getcwd())
+ end)
+ end)
end)
describe('set_config', function()
@@ -1471,6 +1954,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 +2155,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()