aboutsummaryrefslogtreecommitdiff
path: root/test/functional/lua/with_spec.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/lua/with_spec.lua')
-rw-r--r--test/functional/lua/with_spec.lua1044
1 files changed, 814 insertions, 230 deletions
diff --git a/test/functional/lua/with_spec.lua b/test/functional/lua/with_spec.lua
index 36dee9630a..f93ef6f26b 100644
--- a/test/functional/lua/with_spec.lua
+++ b/test/functional/lua/with_spec.lua
@@ -7,286 +7,870 @@ local api = n.api
local command = n.command
local eq = t.eq
local exec_lua = n.exec_lua
+local exec_capture = n.exec_capture
local matches = t.matches
local pcall_err = t.pcall_err
-before_each(function()
- n.clear()
-end)
+describe('vim._with', function()
+ before_each(function()
+ n.clear()
+ exec_lua([[
+ _G.fn = vim.fn
+ _G.api = vim.api
-describe('vim._with {buf = }', function()
- it('does not trigger autocmd', function()
- exec_lua [[
- local new = vim.api.nvim_create_buf(false, true)
- vim.api.nvim_create_autocmd( { 'BufEnter', 'BufLeave', 'BufWinEnter', 'BufWinLeave' }, {
- callback = function() _G.n = (_G.n or 0) + 1 end
- })
- vim._with({buf = new}, function()
- end)
- assert(_G.n == nil)
- ]]
+ _G.setup_buffers = function()
+ return api.nvim_create_buf(false, true), api.nvim_get_current_buf()
+ end
+
+ _G.setup_windows = function()
+ local other_win = api.nvim_get_current_win()
+ vim.cmd.new()
+ return other_win, api.nvim_get_current_win()
+ end
+ ]])
end)
- it('trigger autocmd if changed within context', function()
- exec_lua [[
- local new = vim.api.nvim_create_buf(false, true)
- vim.api.nvim_create_autocmd( { 'BufEnter', 'BufLeave', 'BufWinEnter', 'BufWinLeave' }, {
- callback = function() _G.n = (_G.n or 0) + 1 end
- })
- vim._with({}, function()
- vim.api.nvim_set_current_buf(new)
- assert(_G.n ~= nil)
+ local validate_events_trigger = function()
+ local out = exec_lua [[
+ -- Needs three global values defined:
+ -- - `test_events` - array of events which are tested.
+ -- - `test_context` - context to be tested.
+ -- - `test_trig_event` - callable triggering at least one tested event.
+ _G.n_events = 0
+ local opts = { callback = function() _G.n_events = _G.n_events + 1 end }
+ api.nvim_create_autocmd(_G.test_events, opts)
+
+ local context = { bo = { commentstring = '-- %s' } }
+
+ -- Should not trigger events on its own
+ vim._with(_G.test_context, function() end)
+ local is_no_events = _G.n_events == 0
+
+ -- Should trigger events if specifically asked inside callback
+ local is_events = vim._with(_G.test_context, function()
+ _G.test_trig_event()
+ return _G.n_events > 0
end)
+ return { is_no_events, is_events }
]]
- end)
+ eq({ true, true }, out)
+ end
- it('can access buf options', function()
- local buf1 = api.nvim_get_current_buf()
- local buf2 = exec_lua [[
- buf2 = vim.api.nvim_create_buf(false, true)
- return buf2
- ]]
+ describe('`buf` context', function()
+ it('works', function()
+ local out = exec_lua [[
+ local other_buf, cur_buf = setup_buffers()
+ local inner = vim._with({ buf = other_buf }, function()
+ return api.nvim_get_current_buf()
+ end)
+ return { inner == other_buf, api.nvim_get_current_buf() == cur_buf }
+ ]]
+ eq({ true, true }, out)
+ end)
+
+ it('does not trigger events', function()
+ exec_lua [[
+ _G.test_events = { 'BufEnter', 'BufLeave', 'BufWinEnter', 'BufWinLeave' }
+ _G.test_context = { buf = other_buf }
+ _G.test_trig_event = function() vim.cmd.new() end
+ ]]
+ validate_events_trigger()
+ end)
- eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 }))
- eq(false, api.nvim_get_option_value('autoindent', { buf = buf2 }))
+ it('can access buffer options', function()
+ local out = exec_lua [[
+ other_buf, cur_buf = setup_buffers()
+ vim.bo[other_buf].commentstring = '## %s'
+ vim.bo[cur_buf].commentstring = '// %s'
- local val = exec_lua [[
- return vim._with({buf = buf2}, function()
- vim.cmd "set autoindent"
- return vim.api.nvim_get_current_buf()
+ vim._with({ buf = other_buf }, function()
+ vim.cmd.set('commentstring=--\\ %s')
+ end)
+
+ return vim.bo[other_buf].commentstring == '-- %s' and
+ vim.bo[cur_buf].commentstring == '// %s'
+ ]]
+ eq(true, out)
end)
- ]]
- eq(false, api.nvim_get_option_value('autoindent', { buf = buf1 }))
- eq(true, api.nvim_get_option_value('autoindent', { buf = buf2 }))
- eq(buf1, api.nvim_get_current_buf())
- eq(buf2, val)
+ it('works with different kinds of buffers', function()
+ exec_lua [[
+ local validate = function(buf)
+ vim._with({ buf = buf }, function()
+ assert(api.nvim_get_current_buf() == buf)
+ end)
+ end
+
+ -- Current
+ validate(api.nvim_get_current_buf())
+
+ -- Hidden listed
+ local listed = api.nvim_create_buf(true, true)
+ validate(listed)
+
+ -- Visible
+ local other_win, cur_win = setup_windows()
+ api.nvim_win_set_buf(other_win, listed)
+ validate(listed)
+
+ -- Shown but not visible
+ vim.cmd.tabnew()
+ validate(listed)
+
+ -- Shown in several windows
+ api.nvim_win_set_buf(0, listed)
+ validate(listed)
+
+ -- Shown in floating window
+ local float_buf = api.nvim_create_buf(false, true)
+ local config = { relative = 'editor', row = 1, col = 1, width = 5, height = 5 }
+ api.nvim_open_win(float_buf, false, config)
+ validate(float_buf)
+ ]]
+ end)
+
+ it('does not cause ml_get errors with invalid visual selection', function()
+ exec_lua [[
+ api.nvim_buf_set_lines(0, 0, -1, true, { 'a', 'b', 'c' })
+ api.nvim_feedkeys(vim.keycode('G<C-V>'), 'txn', false)
+ local other_buf, _ = setup_buffers()
+ vim._with({ buf = buf }, function() vim.cmd.redraw() end)
+ ]]
+ end)
+
+ it('can be nested', function()
+ exec_lua [[
+ local other_buf, cur_buf = setup_buffers()
+ vim._with({ buf = other_buf }, function()
+ assert(api.nvim_get_current_buf() == other_buf)
+ inner = vim._with({ buf = cur_buf }, function()
+ assert(api.nvim_get_current_buf() == cur_buf)
+ end)
+ assert(api.nvim_get_current_buf() == other_buf)
+ end)
+ assert(api.nvim_get_current_buf() == cur_buf)
+ ]]
+ end)
+
+ it('can be nested crazily with hidden buffers', function()
+ local out = exec_lua([[
+ local n = 0
+ local function with_recursive_nested_bufs()
+ n = n + 1
+ if n > 20 then return true end
+
+ local other_buf, _ = setup_buffers()
+ vim.bo[other_buf].commentstring = '## %s'
+ local callback = function()
+ return api.nvim_get_current_buf() == other_buf
+ and vim.bo[other_buf].commentstring == '## %s'
+ and with_recursive_nested_bufs()
+ end
+ return vim._with({ buf = other_buf }, callback) and
+ api.nvim_buf_delete(other_buf, {}) == nil
+ end
+
+ return with_recursive_nested_bufs()
+ ]])
+ eq(true, out)
+ end)
end)
- it('does not cause ml_get errors with invalid visual selection', function()
- exec_lua [[
- local api = vim.api
- local t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
- api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
- api.nvim_feedkeys(t "G<C-V>", "txn", false)
- vim._with({buf = api.nvim_create_buf(false, true)}, function() vim.cmd "redraw" end)
- ]]
+ describe('`emsg_silent` context', function()
+ pending('works', function()
+ local ok = pcall(
+ exec_lua,
+ [[
+ _G.f = function()
+ error('This error should not interfer with execution', 0)
+ end
+ -- Should not produce error same as `vim.cmd('silent! lua _G.f()')`
+ vim._with({ emsg_silent = true }, f)
+ ]]
+ )
+ eq(true, ok)
+
+ -- Should properly report errors afterwards
+ ok = pcall(exec_lua, 'lua _G.f()')
+ eq(false, ok)
+ end)
+
+ it('can be nested', function()
+ local ok = pcall(
+ exec_lua,
+ [[
+ _G.f = function()
+ error('This error should not interfer with execution', 0)
+ end
+ -- Should produce error same as `_G.f()`
+ vim._with({ emsg_silent = true }, function()
+ vim._with( { emsg_silent = false }, f)
+ end)
+ ]]
+ )
+ eq(false, ok)
+ end)
end)
- it('can be nested crazily with hidden buffers', function()
- eq(
- true,
- exec_lua([[
- local function scratch_buf_call(fn)
- local buf = vim.api.nvim_create_buf(false, true)
- vim.api.nvim_set_option_value('cindent', true, {buf = buf})
- return vim._with({buf = buf}, function()
- return vim.api.nvim_get_current_buf() == buf
- and vim.api.nvim_get_option_value('cindent', {buf = buf})
- and fn()
- end) and vim.api.nvim_buf_delete(buf, {}) == nil
- end
+ describe('`hide` context', function()
+ pending('works', function()
+ local ok = pcall(
+ exec_lua,
+ [[
+ vim.o.hidden = false
+ vim.bo.modified = true
+ local init_buf = api.nvim_get_current_buf()
+ -- Should not produce error same as `vim.cmd('hide enew')`
+ vim._with({ hide = true }, function()
+ vim.cmd.enew()
+ end)
+ assert(api.nvim_get_current_buf() ~= init_buf)
+ ]]
+ )
+ eq(true, ok)
+ end)
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return scratch_buf_call(function()
- return true
- end)
- end)
- end)
- end)
- end)
- end)
- end)
+ it('can be nested', function()
+ local ok = pcall(
+ exec_lua,
+ [[
+ vim.o.hidden = false
+ vim.bo.modified = true
+ -- Should produce error same as `vim.cmd.enew()`
+ vim._with({ hide = true }, function()
+ vim._with({ hide = false }, function()
+ vim.cmd.enew()
end)
end)
- end)
- end)
+ ]]
+ )
+ eq(false, ok)
end)
- ]])
- )
end)
- it('can return values by reference', function()
- eq(
- { 4, 7 },
+ describe('`horizontal` context', function()
+ local is_approx_eq = function(dim, id_1, id_2)
+ local f = dim == 'height' and api.nvim_win_get_height or api.nvim_win_get_width
+ return math.abs(f(id_1) - f(id_2)) <= 1
+ end
+
+ local win_id_1, win_id_2, win_id_3
+ before_each(function()
+ win_id_1 = api.nvim_get_current_win()
+ command('wincmd v | wincmd 5>')
+ win_id_2 = api.nvim_get_current_win()
+ command('wincmd s | wincmd 5+')
+ win_id_3 = api.nvim_get_current_win()
+
+ eq(is_approx_eq('width', win_id_1, win_id_2), false)
+ eq(is_approx_eq('height', win_id_3, win_id_2), false)
+ end)
+
+ pending('works', function()
exec_lua [[
- local val = {4, 10}
- local ref = vim._with({ buf = 0}, function() return val end)
- ref[2] = 7
- return val
- ]]
- )
+ -- Should be same as `vim.cmd('horizontal wincmd =')`
+ vim._with({ horizontal = true }, function()
+ vim.cmd.wincmd('=')
+ end)
+ ]]
+ eq(is_approx_eq('width', win_id_1, win_id_2), true)
+ eq(is_approx_eq('height', win_id_3, win_id_2), false)
+ end)
+
+ pending('can be nested', function()
+ exec_lua [[
+ -- Should be same as `vim.cmd.wincmd('=')`
+ vim._with({ horizontal = true }, function()
+ vim._with({ horizontal = false }, function()
+ vim.cmd.wincmd('=')
+ end)
+ end)
+ ]]
+ eq(is_approx_eq('width', win_id_1, win_id_2), true)
+ eq(is_approx_eq('height', win_id_3, win_id_2), true)
+ end)
end)
-end)
-describe('vim._with {win = }', function()
- it('does not trigger autocmd', function()
- exec_lua [[
- local old = vim.api.nvim_get_current_win()
- vim.cmd("new")
- local new = vim.api.nvim_get_current_win()
- vim.api.nvim_create_autocmd( { 'WinEnter', 'WinLeave' }, {
- callback = function() _G.n = (_G.n or 0) + 1 end
- })
- vim._with({win = old}, function()
- end)
- assert(_G.n == nil)
- ]]
+ describe('`keepalt` context', function()
+ pending('works', function()
+ local out = exec_lua [[
+ vim.cmd('edit alt')
+ vim.cmd('edit new')
+ assert(fn.bufname('#') == 'alt')
+
+ -- Should work as `vim.cmd('keepalt edit very-new')`
+ vim._with({ keepalt = true }, function()
+ vim.cmd.edit('very-new')
+ end)
+ return fn.bufname('#') == 'alt'
+ ]]
+ eq(true, out)
+ end)
+
+ it('can be nested', function()
+ local out = exec_lua [[
+ vim.cmd('edit alt')
+ vim.cmd('edit new')
+ assert(fn.bufname('#') == 'alt')
+
+ -- Should work as `vim.cmd.edit('very-new')`
+ vim._with({ keepalt = true }, function()
+ vim._with({ keepalt = false }, function()
+ vim.cmd.edit('very-new')
+ end)
+ end)
+ return fn.bufname('#') == 'alt'
+ ]]
+ eq(false, out)
+ end)
end)
- it('trigger autocmd if changed within context', function()
- exec_lua [[
- local old = vim.api.nvim_get_current_win()
- vim.cmd("new")
- local new = vim.api.nvim_get_current_win()
- vim.api.nvim_create_autocmd( { 'WinEnter', 'WinLeave' }, {
- callback = function() _G.n = (_G.n or 0) + 1 end
- })
- vim._with({}, function()
- vim.api.nvim_set_current_win(old)
- assert(_G.n ~= nil)
- end)
- ]]
+ describe('`keepjumps` context', function()
+ pending('works', function()
+ local out = exec_lua [[
+ api.nvim_buf_set_lines(0, 0, -1, false, { 'aaa', 'bbb', 'ccc' })
+ local jumplist_before = fn.getjumplist()
+ -- Should work as `vim.cmd('keepjumps normal! Ggg')`
+ vim._with({ keepjumps = true }, function()
+ vim.cmd('normal! Ggg')
+ end)
+ return vim.deep_equal(jumplist_before, fn.getjumplist())
+ ]]
+ eq(true, out)
+ end)
+
+ it('can be nested', function()
+ local out = exec_lua [[
+ api.nvim_buf_set_lines(0, 0, -1, false, { 'aaa', 'bbb', 'ccc' })
+ local jumplist_before = fn.getjumplist()
+ vim._with({ keepjumps = true }, function()
+ vim._with({ keepjumps = false }, function()
+ vim.cmd('normal! Ggg')
+ end)
+ end)
+ return vim.deep_equal(jumplist_before, fn.getjumplist())
+ ]]
+ eq(false, out)
+ end)
end)
- it('can access window options', function()
- command('vsplit')
- local win1 = api.nvim_get_current_win()
- command('wincmd w')
- local win2 = exec_lua [[
- win2 = vim.api.nvim_get_current_win()
- return win2
- ]]
- command('wincmd p')
+ describe('`keepmarks` context', function()
+ pending('works', function()
+ local out = exec_lua [[
+ vim.cmd('set cpoptions+=R')
+ api.nvim_buf_set_lines(0, 0, -1, false, { 'bbb', 'ccc', 'aaa' })
+ api.nvim_buf_set_mark(0, 'm', 2, 2, {})
- eq('', api.nvim_get_option_value('winhighlight', { win = win1 }))
- eq('', api.nvim_get_option_value('winhighlight', { win = win2 }))
+ -- Should be the same as `vim.cmd('keepmarks %!sort')`
+ vim._with({ keepmarks = true }, function()
+ vim.cmd('%!sort')
+ end)
+ return api.nvim_buf_get_mark(0, 'm')
+ ]]
+ eq({ 2, 2 }, out)
+ end)
- local val = exec_lua [[
- return vim._with({win = win2}, function()
- vim.cmd "setlocal winhighlight=Normal:Normal"
- return vim.api.nvim_get_current_win()
- end)
- ]]
+ it('can be nested', function()
+ local out = exec_lua [[
+ vim.cmd('set cpoptions+=R')
+ api.nvim_buf_set_lines(0, 0, -1, false, { 'bbb', 'ccc', 'aaa' })
+ api.nvim_buf_set_mark(0, 'm', 2, 2, {})
- eq('', api.nvim_get_option_value('winhighlight', { win = win1 }))
- eq('Normal:Normal', api.nvim_get_option_value('winhighlight', { win = win2 }))
- eq(win1, api.nvim_get_current_win())
- eq(win2, val)
+ vim._with({ keepmarks = true }, function()
+ vim._with({ keepmarks = false }, function()
+ vim.cmd('%!sort')
+ end)
+ end)
+ return api.nvim_buf_get_mark(0, 'm')
+ ]]
+ eq({ 0, 2 }, out)
+ end)
end)
- it('does not cause ml_get errors with invalid visual selection', function()
- -- Add lines to the current buffer and make another window looking into an empty buffer.
- exec_lua [[
- _G.api = vim.api
- _G.t = function(s) return api.nvim_replace_termcodes(s, true, true, true) end
- _G.win_lines = api.nvim_get_current_win()
- vim.cmd "new"
- _G.win_empty = api.nvim_get_current_win()
- api.nvim_set_current_win(win_lines)
- api.nvim_buf_set_lines(0, 0, -1, true, {"a", "b", "c"})
- ]]
+ describe('`keepatterns` context', function()
+ pending('works', function()
+ local out = exec_lua [[
+ api.nvim_buf_set_lines(0, 0, -1, false, { 'aaa', 'bbb' })
+ vim.cmd('/aaa')
+ -- Should be the same as `vim.cmd('keeppatterns /bbb')`
+ vim._with({ keeppatterns = true }, function()
+ vim.cmd('/bbb')
+ end)
+ return fn.getreg('/')
+ ]]
+ eq('aaa', out)
+ end)
- -- Start Visual in current window, redraw in other window with fewer lines.
- exec_lua [[
- api.nvim_feedkeys(t "G<C-V>", "txn", false)
- vim._with({win = win_empty}, function() vim.cmd "redraw" end)
- ]]
+ it('can be nested', function()
+ local out = exec_lua [[
+ api.nvim_buf_set_lines(0, 0, -1, false, { 'aaa', 'bbb' })
+ vim.cmd('/aaa')
+ vim._with({ keeppatterns = true }, function()
+ vim._with({ keeppatterns = false }, function()
+ vim.cmd('/bbb')
+ end)
+ end)
+ return fn.getreg('/')
+ ]]
+ eq('bbb', out)
+ end)
+ end)
- -- Start Visual in current window, extend it in other window with more lines.
- exec_lua [[
- api.nvim_feedkeys(t "<Esc>gg", "txn", false)
- api.nvim_set_current_win(win_empty)
- api.nvim_feedkeys(t "gg<C-V>", "txn", false)
- vim._with({win = win_lines}, function() api.nvim_feedkeys(t "G<C-V>", "txn", false) end)
- vim.cmd "redraw"
- ]]
+ describe('`lockmarks` context', function()
+ it('works', function()
+ local mark = exec_lua [[
+ api.nvim_buf_set_lines(0, 0, 0, false, { 'aaa', 'bbb', 'ccc' })
+ api.nvim_buf_set_mark(0, 'm', 2, 2, {})
+ -- Should be same as `:lockmarks lua api.nvim_buf_set_lines(...)`
+ vim._with({ lockmarks = true }, function()
+ api.nvim_buf_set_lines(0, 0, 2, false, { 'uuu', 'vvv', 'www' })
+ end)
+ return api.nvim_buf_get_mark(0, 'm')
+ ]]
+ eq({ 2, 2 }, mark)
+ end)
+
+ it('can be nested', function()
+ local mark = exec_lua [[
+ api.nvim_buf_set_lines(0, 0, 0, false, { 'aaa', 'bbb', 'ccc' })
+ api.nvim_buf_set_mark(0, 'm', 2, 2, {})
+ vim._with({ lockmarks = true }, function()
+ vim._with({ lockmarks = false }, function()
+ api.nvim_buf_set_lines(0, 0, 2, false, { 'uuu', 'vvv', 'www' })
+ end)
+ end)
+ return api.nvim_buf_get_mark(0, 'm')
+ ]]
+ eq({ 0, 2 }, mark)
+ end)
end)
- it('updates ruler if cursor moved', function()
- local screen = Screen.new(30, 5)
- screen:set_default_attr_ids {
- [1] = { reverse = true },
- [2] = { bold = true, reverse = true },
- }
- screen:attach()
- exec_lua [[
- _G.api = vim.api
- vim.opt.ruler = true
- local lines = {}
- for i = 0, 499 do lines[#lines + 1] = tostring(i) end
- api.nvim_buf_set_lines(0, 0, -1, true, lines)
- api.nvim_win_set_cursor(0, {20, 0})
- vim.cmd "split"
- _G.win = api.nvim_get_current_win()
- vim.cmd "wincmd w | redraw"
- ]]
- screen:expect [[
- 19 |
- {1:[No Name] [+] 20,1 3%}|
- ^19 |
- {2:[No Name] [+] 20,1 3%}|
- |
- ]]
- exec_lua [[
- vim._with({win = win}, function() api.nvim_win_set_cursor(0, {100, 0}) end)
- vim.cmd "redraw"
- ]]
- screen:expect [[
- 99 |
- {1:[No Name] [+] 100,1 19%}|
- ^19 |
- {2:[No Name] [+] 20,1 3%}|
- |
+ describe('`noautocmd` context', function()
+ it('works', function()
+ local out = exec_lua [[
+ _G.n_events = 0
+ vim.cmd('au ModeChanged * lua _G.n_events = _G.n_events + 1')
+ -- Should be the same as `vim.cmd('noautocmd normal! vv')`
+ vim._with({ noautocmd = true }, function()
+ vim.cmd('normal! vv')
+ end)
+ return _G.n_events
+ ]]
+ eq(0, out)
+ end)
+
+ it('works with User events', function()
+ local out = exec_lua [[
+ _G.n_events = 0
+ vim.cmd('au User MyEvent lua _G.n_events = _G.n_events + 1')
+ -- Should be the same as `vim.cmd('noautocmd doautocmd User MyEvent')`
+ vim._with({ noautocmd = true }, function()
+ api.nvim_exec_autocmds('User', { pattern = 'MyEvent' })
+ end)
+ return _G.n_events
+ ]]
+ eq(0, out)
+ end)
+
+ pending('can be nested', function()
+ local out = exec_lua [[
+ _G.n_events = 0
+ vim.cmd('au ModeChanged * lua _G.n_events = _G.n_events + 1')
+ vim._with({ noautocmd = true }, function()
+ vim._with({ noautocmd = false }, function()
+ vim.cmd('normal! vv')
+ end)
+ end)
+ return _G.n_events
+ ]]
+ eq(2, out)
+ end)
+ end)
+
+ describe('`sandbox` context', function()
+ it('works', function()
+ local ok, err = pcall(
+ exec_lua,
+ [[
+ -- Should work as `vim.cmd('sandbox call append(0, "aaa")')`
+ vim._with({ sandbox = true }, function()
+ fn.append(0, 'aaa')
+ end)
+ ]]
+ )
+ eq(false, ok)
+ matches('Not allowed in sandbox', err)
+ end)
+
+ it('can NOT be nested', function()
+ -- This behavior is intentionally different from other flags as allowing
+ -- disabling `sandbox` from nested function seems to be against the point
+ -- of using `sandbox` context in the first place
+ local ok, err = pcall(
+ exec_lua,
+ [[
+ vim._with({ sandbox = true }, function()
+ vim._with({ sandbox = false }, function()
+ fn.append(0, 'aaa')
+ end)
+ end)
+ ]]
+ )
+ eq(false, ok)
+ matches('Not allowed in sandbox', err)
+ end)
+ end)
+
+ describe('`silent` context', function()
+ it('works', function()
+ exec_lua [[
+ -- Should be same as `vim.cmd('silent lua print("aaa")')`
+ vim._with({ silent = true }, function() print('aaa') end)
+ ]]
+ eq('', exec_capture('messages'))
+
+ exec_lua [[ vim._with({ silent = true }, function() vim.cmd.echomsg('"bbb"') end) ]]
+ eq('', exec_capture('messages'))
+
+ local screen = Screen.new(20, 5)
+ screen:set_default_attr_ids {
+ [1] = { bold = true, reverse = true },
+ [2] = { bold = true, foreground = Screen.colors.Blue },
+ }
+ screen:attach()
+ exec_lua [[ vim._with({ silent = true }, function() vim.cmd.echo('"ccc"') end) ]]
+ screen:expect [[
+ ^ |
+ {2:~ }|*3
+ |
+ ]]
+ end)
+
+ pending('can be nested', function()
+ exec_lua [[ vim._with({ silent = true }, function()
+ vim._with({ silent = false }, function()
+ print('aaa')
+ end)
+ end)]]
+ eq('aaa', exec_capture('messages'))
+ end)
+ end)
+
+ describe('`unsilent` context', function()
+ it('works', function()
+ exec_lua [[
+ _G.f = function()
+ -- Should be same as `vim.cmd('unsilent lua print("aaa")')`
+ vim._with({ unsilent = true }, function() print('aaa') end)
+ end
+ ]]
+ command('silent lua f()')
+ eq('aaa', exec_capture('messages'))
+ end)
+
+ pending('can be nested', function()
+ exec_lua [[
+ _G.f = function()
+ vim._with({ unsilent = true }, function()
+ vim._with({ unsilent = false }, function() print('aaa') end)
+ end)
+ end
+ ]]
+ command('silent lua f()')
+ eq('', exec_capture('messages'))
+ end)
+ end)
+
+ describe('`win` context', function()
+ it('works', function()
+ local out = exec_lua [[
+ local other_win, cur_win = setup_windows()
+ local inner = vim._with({ win = other_win }, function()
+ return api.nvim_get_current_win()
+ end)
+ return { inner == other_win, api.nvim_get_current_win() == cur_win }
+ ]]
+ eq({ true, true }, out)
+ end)
+
+ it('does not trigger events', function()
+ exec_lua [[
+ _G.test_events = { 'WinEnter', 'WinLeave', 'BufWinEnter', 'BufWinLeave' }
+ _G.test_context = { win = other_win }
+ _G.test_trig_event = function() vim.cmd.new() end
+ ]]
+ validate_events_trigger()
+ end)
+
+ it('can access window options', function()
+ local out = exec_lua [[
+ local other_win, cur_win = setup_windows()
+ vim.wo[other_win].winblend = 10
+ vim.wo[cur_win].winblend = 25
+
+ vim._with({ win = other_win }, function()
+ vim.cmd.setlocal('winblend=0')
+ end)
+
+ return vim.wo[other_win].winblend == 0 and vim.wo[cur_win].winblend == 25
+ ]]
+ eq(true, out)
+ end)
+
+ it('works with different kinds of windows', function()
+ exec_lua [[
+ local validate = function(win)
+ vim._with({ win = win }, function()
+ assert(api.nvim_get_current_win() == win)
+ end)
+ end
+
+ -- Current
+ validate(api.nvim_get_current_win())
+
+ -- Not visible
+ local other_win, cur_win = setup_windows()
+ vim.cmd.tabnew()
+ validate(other_win)
+
+ -- Floating
+ local float_win = api.nvim_open_win(
+ api.nvim_create_buf(false, true),
+ false,
+ { relative = 'editor', row = 1, col = 1, height = 5, width = 5}
+ )
+ validate(float_win)
+ ]]
+ end)
+
+ it('does not cause ml_get errors with invalid visual selection', function()
+ exec_lua [[
+ local feedkeys = function(keys) api.nvim_feedkeys(vim.keycode(keys), 'txn', false) end
+
+ -- Add lines to the current buffer and make another window looking into an empty buffer.
+ local win_empty, win_lines = setup_windows()
+ api.nvim_buf_set_lines(0, 0, -1, true, { 'a', 'b', 'c' })
+
+ -- Start Visual in current window, redraw in other window with fewer lines.
+ -- Should be fixed by vim-patch:8.2.4018.
+ feedkeys('G<C-V>')
+ vim._with({ win = win_empty }, function() vim.cmd.redraw() end)
+
+ -- Start Visual in current window, extend it in other window with more lines.
+ -- Fixed for win_execute by vim-patch:8.2.4026, but nvim_win_call should also not be affected.
+ feedkeys('<Esc>gg')
+ api.nvim_set_current_win(win_empty)
+ feedkeys('gg<C-V>')
+ vim._with({ win = win_lines }, function() feedkeys('G<C-V>') end)
+ vim.cmd.redraw()
+ ]]
+ end)
+
+ it('can be nested', function()
+ exec_lua [[
+ local other_win, cur_win = setup_windows()
+ vim._with({ win = other_win }, function()
+ assert(api.nvim_get_current_win() == other_win)
+ inner = vim._with({ win = cur_win }, function()
+ assert(api.nvim_get_current_win() == cur_win)
+ end)
+ assert(api.nvim_get_current_win() == other_win)
+ end)
+ assert(api.nvim_get_current_win() == cur_win)
+ ]]
+ end)
+
+ it('updates ruler if cursor moved', function()
+ local screen = Screen.new(30, 5)
+ screen:set_default_attr_ids {
+ [1] = { reverse = true },
+ [2] = { bold = true, reverse = true },
+ }
+ screen:attach()
+ exec_lua [[
+ vim.opt.ruler = true
+ local lines = {}
+ for i = 0, 499 do lines[#lines + 1] = tostring(i) end
+ api.nvim_buf_set_lines(0, 0, -1, true, lines)
+ api.nvim_win_set_cursor(0, { 20, 0 })
+ vim.cmd 'split'
+ _G.win = api.nvim_get_current_win()
+ vim.cmd "wincmd w | redraw"
+ ]]
+ screen:expect [[
+ 19 |
+ {1:[No Name] [+] 20,1 3%}|
+ ^19 |
+ {2:[No Name] [+] 20,1 3%}|
+ |
+ ]]
+ exec_lua [[
+ vim._with({ win = win }, function() api.nvim_win_set_cursor(0, { 100, 0 }) end)
+ vim.cmd "redraw"
+ ]]
+ screen:expect [[
+ 99 |
+ {1:[No Name] [+] 100,1 19%}|
+ ^19 |
+ {2:[No Name] [+] 20,1 3%}|
+ |
+ ]]
+ end)
+
+ it('layout in current tabpage does not affect windows in others', function()
+ command('tab split')
+ local t2_move_win = api.nvim_get_current_win()
+ command('vsplit')
+ local t2_other_win = api.nvim_get_current_win()
+ command('tabprevious')
+ matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
+ command('vsplit')
+
+ exec_lua('vim._with({ win = ... }, function() vim.cmd.wincmd "J" end)', t2_move_win)
+ eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2))
+ end)
+ end)
+
+ it('returns what callback returns', function()
+ local out_verify = exec_lua [[
+ out = { vim._with({}, function()
+ return 'a', 2, nil, { 4 }, function() end
+ end) }
+ return {
+ out[1] == 'a', out[2] == 2, out[3] == nil,
+ vim.deep_equal(out[4], { 4 }),
+ type(out[5]) == 'function',
+ vim.tbl_count(out),
+ }
]]
+ eq({ true, true, true, true, true, 4 }, out_verify)
end)
it('can return values by reference', function()
- eq(
- { 7, 10 },
- exec_lua [[
- local val = {4, 10}
- local ref = vim._with({win = 0}, function() return val end)
+ local out = exec_lua [[
+ local val = { 4, 10 }
+ local ref = vim._with({}, function() return val end)
ref[1] = 7
return val
]]
- )
+ eq({ 7, 10 }, out)
end)
- it('layout in current tabpage does not affect windows in others', function()
- command('tab split')
- local t2_move_win = api.nvim_get_current_win()
- command('vsplit')
- local t2_other_win = api.nvim_get_current_win()
- command('tabprevious')
- matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)'))
- command('vsplit')
-
- exec_lua('vim._with({win = ...}, function() vim.cmd.wincmd "J" end)', t2_move_win)
- eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2))
+ it('can not work with conflicting `buf` and `win`', function()
+ local out = exec_lua [[
+ local other_buf, cur_buf = setup_buffers()
+ local other_win, cur_win = setup_windows()
+ assert(api.nvim_win_get_buf(other_win) ~= other_buf)
+ local _, err = pcall(vim._with, { buf = other_buf, win = other_win }, function() end)
+ return err
+ ]]
+ matches('Can not set both `buf` and `win`', out)
end)
-end)
-describe('vim._with {lockmarks = true}', function()
- it('is reset', function()
- local mark = exec_lua [[
- vim.api.nvim_buf_set_lines(0, 0, 0, false, {"marky", "snarky", "malarkey"})
- vim.api.nvim_buf_set_mark(0,"m",1,0, {})
- vim._with({lockmarks = true}, function()
- vim.api.nvim_buf_set_lines(0, 0, 2, false, {"mass", "mess", "moss"})
- end)
- return vim.api.nvim_buf_get_mark(0,"m")
+ pending('can forward command modifiers to user command', function()
+ local out = exec_lua [[
+ local test_flags = {
+ 'emsg_silent',
+ 'hide',
+ 'keepalt',
+ 'keepjumps',
+ 'keepmarks',
+ 'keeppatterns',
+ 'lockmarks',
+ 'noautocmd',
+ 'silent',
+ 'unsilent',
+ }
+
+ local used_smods
+ local command = function(data)
+ used_smods = data.smods
+ end
+ api.nvim_create_user_command('DummyLog', command, {})
+
+ local res = {}
+ for _, flag in ipairs(test_flags) do
+ used_smods = nil
+ vim._with({ [flag] = true }, function() vim.cmd('DummyLog') end)
+ res[flag] = used_smods[flag]
+ end
+ return res
]]
- t.eq(mark, { 1, 0 })
+ for k, v in pairs(out) do
+ eq({ k, true }, { k, v })
+ end
+ end)
+
+ it('handles error in callback', function()
+ -- Should still restore initial context
+ local out_buf = exec_lua [[
+ local other_buf, cur_buf = setup_buffers()
+
+ local context = { buf = other_buf }
+ local ok, err = pcall(vim._with, context, function() error('Oops buf', 0) end)
+
+ return {
+ ok,
+ err,
+ api.nvim_get_current_buf() == cur_buf,
+ }
+ ]]
+ eq({ false, 'Oops buf', true }, out_buf)
+
+ local out_win = exec_lua [[
+ local other_win, cur_win = setup_windows()
+ vim.wo[other_win].winblend = 25
+
+ local context = { win = other_win, wo = { winblend = 50 } }
+ local ok, err = pcall(vim._with, context, function() error('Oops win', 0) end)
+
+ return {
+ ok,
+ err,
+ api.nvim_get_current_win() == cur_win,
+ vim.wo[other_win].winblend,
+ }
+ ]]
+ eq({ false, 'Oops win', true, 25 }, out_win)
+ end)
+
+ it('validates arguments', function()
+ exec_lua [[
+ _G.get_error = function(...)
+ local _, err = pcall(vim._with, ...)
+ return err or ''
+ end
+ ]]
+ local get_error = function(string_args)
+ return exec_lua('return get_error(' .. string_args .. ')')
+ end
+
+ matches('context.*table', get_error("'a', function() end"))
+ matches('f.*function', get_error('{}, 1'))
+
+ local validate_context = function(bad_context, expected_type)
+ local bad_field = vim.tbl_keys(bad_context)[1]
+ matches(
+ 'context%.' .. bad_field .. '.*' .. expected_type,
+ get_error(vim.inspect(bad_context) .. ', function() end')
+ )
+ end
+
+ validate_context({ buf = 'a' }, 'number')
+ validate_context({ emsg_silent = 1 }, 'boolean')
+ validate_context({ hide = 1 }, 'boolean')
+ validate_context({ keepalt = 1 }, 'boolean')
+ validate_context({ keepjumps = 1 }, 'boolean')
+ validate_context({ keepmarks = 1 }, 'boolean')
+ validate_context({ keeppatterns = 1 }, 'boolean')
+ validate_context({ lockmarks = 1 }, 'boolean')
+ validate_context({ noautocmd = 1 }, 'boolean')
+ validate_context({ sandbox = 1 }, 'boolean')
+ validate_context({ silent = 1 }, 'boolean')
+ validate_context({ unsilent = 1 }, 'boolean')
+ validate_context({ win = 'a' }, 'number')
+
+ matches('Invalid buffer', get_error('{ buf = -1 }, function() end'))
+ matches('Invalid window', get_error('{ win = -1 }, function() end'))
end)
end)