diff options
Diffstat (limited to 'test/functional/lua/vim_spec.lua')
-rw-r--r-- | test/functional/lua/vim_spec.lua | 376 |
1 files changed, 326 insertions, 50 deletions
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 3c65ec664e..3cfbfe167a 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -28,6 +28,7 @@ local rmdir = n.rmdir local write_file = t.write_file local poke_eventloop = n.poke_eventloop local assert_alive = n.assert_alive +local expect = n.expect describe('lua stdlib', function() before_each(clear) @@ -155,10 +156,10 @@ describe('lua stdlib', function() end) it('plugin=nil, no error if soft-deprecated', function() - eq( - vim.NIL, - exec_lua('return vim.deprecate(...)', 'foo.baz()', 'foo.better_baz()', '0.99.0') - ) + eq(vim.NIL, exec_lua [[return vim.deprecate('old1', 'new1', '0.99.0')]]) + -- Major version > current Nvim major is always "soft-deprecated". + -- XXX: This is also a reminder to update the hardcoded `nvim_major`, when Nvim reaches 1.0. + eq(vim.NIL, exec_lua [[return vim.deprecate('old2', 'new2', '1.0.0')]]) end) it('plugin=nil, show error if hard-deprecated', function() @@ -175,13 +176,6 @@ describe('lua stdlib', function() ) end) - it('plugin=nil, to be deleted in the next major version (1.0)', function() - eq( - [[foo.baz() is deprecated. Run ":checkhealth vim.deprecated" for more information]], - exec_lua [[ return vim.deprecate('foo.baz()', nil, '1.0') ]] - ) - end) - it('plugin specified', function() -- When `plugin` is specified, don't show ":help deprecated". #22235 eq( @@ -319,21 +313,106 @@ describe('lua stdlib', function() 49, 51, } + local indices8 = { + [0] = 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + } for i, k in pairs(indices32) do eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ...)', i), i) + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., false)', i), i) + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", ...)', i), i) end for i, k in pairs(indices16) do eq(k, exec_lua('return vim.str_byteindex(_G.test_text, ..., true)', i), i) + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", ...)', i), i) end - eq( + for i, k in pairs(indices8) do + eq(k, exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", ...)', i), i) + end + matches( 'index out of range', pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ...)', #indices32 + 1) ) - eq( + matches( 'index out of range', pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, ..., true)', #indices16 + 1) ) - local i32, i16 = 0, 0 + matches( + 'index out of range', + pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-16", ...)', #indices16 + 1) + ) + matches( + 'index out of range', + pcall_err(exec_lua, 'return vim.str_byteindex(_G.test_text, "utf-32", ...)', #indices32 + 1) + ) + matches( + 'invalid encoding', + pcall_err(exec_lua, 'return vim.str_byteindex("hello", "madeupencoding", 1)') + ) + eq( + indices32[#indices32], + exec_lua('return vim.str_byteindex(_G.test_text, "utf-32", 99999, false)') + ) + eq( + indices16[#indices16], + exec_lua('return vim.str_byteindex(_G.test_text, "utf-16", 99999, false)') + ) + eq( + indices8[#indices8], + exec_lua('return vim.str_byteindex(_G.test_text, "utf-8", 99999, false)') + ) + eq(2, exec_lua('return vim.str_byteindex("é", "utf-16", 2, false)')) + local i32, i16, i8 = 0, 0, 0 local len = 51 for k = 0, len do if indices32[i32] < k then @@ -345,9 +424,29 @@ describe('lua stdlib', function() i16 = i16 + 1 end end + if indices8[i8] < k then + i8 = i8 + 1 + end eq({ i32, i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, ...)}', k), k) + eq({ i32 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-32", ...)}', k), k) + eq({ i16 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-16", ...)}', k), k) + eq({ i8 }, exec_lua('return {vim.str_utfindex(_G.test_text, "utf-8", ...)}', k), k) end - eq( + + eq({ #indices32, #indices16 }, exec_lua('return {vim.str_utfindex(_G.test_text)}')) + + eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32", math.huge, false)')) + eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16", math.huge, false)')) + eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8", math.huge, false)')) + + eq(#indices32, exec_lua('return vim.str_utfindex(_G.test_text, "utf-32")')) + eq(#indices16, exec_lua('return vim.str_utfindex(_G.test_text, "utf-16")')) + eq(#indices8, exec_lua('return vim.str_utfindex(_G.test_text, "utf-8")')) + matches( + 'invalid encoding', + pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, "madeupencoding", ...)', 1) + ) + matches( 'index out of range', pcall_err(exec_lua, 'return vim.str_utfindex(_G.test_text, ...)', len + 1) ) @@ -538,7 +637,6 @@ describe('lua stdlib', function() matches('big failure\nvery async', remove_trace(eval('v:errmsg'))) local screen = Screen.new(60, 5) - screen:attach() screen:expect { grid = [[ ^ | @@ -1304,7 +1402,6 @@ describe('lua stdlib', function() end) local screen = Screen.new(50, 7) - screen:attach() exec_lua([[ timer = vim.uv.new_timer() timer:start(20, 0, function () @@ -1317,7 +1414,7 @@ describe('lua stdlib', function() screen:expect { grid = [[ {9:[string "<nvim>"]:6: E5560: rpcrequest must not be}| - {9: called in a lua loop callback} | + {9: called in a fast event context} | {9:stack traceback:} | {9: [C]: in function 'rpcrequest'} | {9: [string "<nvim>"]:6: in function <[string }| @@ -1326,7 +1423,9 @@ describe('lua stdlib', function() ]], } feed('<cr>') - eq({ 3, NIL }, api.nvim_get_var('yy')) + retry(10, nil, function() + eq({ 3, NIL }, api.nvim_get_var('yy')) + end) exec_lua([[timer:close()]]) end) @@ -1363,7 +1462,79 @@ describe('lua stdlib', function() eq('{"a": {}, "b": []}', exec_lua([[ return vim.fn.json_encode({a=vim.empty_dict(), b={}}) ]])) end) - it('vim.validate', function() + it('vim.validate (fast form)', function() + exec_lua("vim.validate('arg1', {}, 'table')") + exec_lua("vim.validate('arg1', nil, 'table', true)") + exec_lua("vim.validate('arg1', { foo='foo' }, 'table')") + exec_lua("vim.validate('arg1', { 'foo' }, 'table')") + exec_lua("vim.validate('arg1', 'foo', 'string')") + exec_lua("vim.validate('arg1', nil, 'string', true)") + exec_lua("vim.validate('arg1', 1, 'number')") + exec_lua("vim.validate('arg1', 0, 'number')") + exec_lua("vim.validate('arg1', 0.1, 'number')") + exec_lua("vim.validate('arg1', nil, 'number', true)") + exec_lua("vim.validate('arg1', true, 'boolean')") + exec_lua("vim.validate('arg1', false, 'boolean')") + exec_lua("vim.validate('arg1', nil, 'boolean', true)") + exec_lua("vim.validate('arg1', function()end, 'function')") + exec_lua("vim.validate('arg1', nil, 'function', true)") + exec_lua("vim.validate('arg1', nil, 'nil')") + exec_lua("vim.validate('arg1', nil, 'nil', true)") + exec_lua("vim.validate('arg1', coroutine.create(function()end), 'thread')") + exec_lua("vim.validate('arg1', nil, 'thread', true)") + exec_lua("vim.validate('arg1', 2, function(a) return (a % 2) == 0 end, 'even number')") + exec_lua("vim.validate('arg1', 5, {'number', 'string'})") + exec_lua("vim.validate('arg2', 'foo', {'number', 'string'})") + + matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number')) + matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string')) + matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table')) + matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function')) + matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string')) + matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table')) + matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function')) + matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number')) + matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate('arg1', 1, 'x')")) + matches('invalid validator: 1', pcall_err(exec_lua, "vim.validate('arg1', 1, 1)")) + matches('invalid arguments', pcall_err(exec_lua, "vim.validate('arg1', { 1 })")) + + -- Validated parameters are required by default. + matches( + 'arg1: expected string, got nil', + pcall_err(exec_lua, "vim.validate('arg1', nil, 'string')") + ) + -- Explicitly required. + matches( + 'arg1: expected string, got nil', + pcall_err(exec_lua, "vim.validate('arg1', nil, 'string', false)") + ) + + matches( + 'arg1: expected table, got number', + pcall_err(exec_lua, "vim.validate('arg1', 1, 'table')") + ) + + matches( + 'arg1: expected even number, got 3', + pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end, 'even number')") + ) + matches( + 'arg1: expected %?, got 3', + pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end)") + ) + matches( + 'arg1: expected number|string, got nil', + pcall_err(exec_lua, "vim.validate('arg1', nil, {'number', 'string'})") + ) + + -- Pass an additional message back. + matches( + 'arg1: expected %?, got 3. Info: TEST_MSG', + pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1, 'TEST_MSG' end)") + ) + end) + + it('vim.validate (spec form)', function() exec_lua("vim.validate{arg1={{}, 'table' }}") exec_lua("vim.validate{arg1={{}, 't' }}") exec_lua("vim.validate{arg1={nil, 't', true }}") @@ -1392,29 +1563,11 @@ describe('lua stdlib', function() exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}") - vim.validate('arg1', 5, 'number') - vim.validate('arg1', '5', 'string') - vim.validate('arg1', { 5 }, 'table') - vim.validate('arg1', function() - return 5 - end, 'function') - vim.validate('arg1', nil, 'number', true) - vim.validate('arg1', nil, 'string', true) - vim.validate('arg1', nil, 'table', true) - vim.validate('arg1', nil, 'function', true) - matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number')) - matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string')) - matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table')) - matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function')) - matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string')) - matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table')) - matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function')) - matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number')) matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) - matches('invalid type name: x', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) - matches('invalid type name: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}')) - matches('invalid type name: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}')) + matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) + matches('invalid validator: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}')) + matches('invalid validator: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}')) -- Validated parameters are required by default. matches( @@ -1975,7 +2128,6 @@ describe('lua stdlib', function() eq({ 1, 5 }, api.nvim_win_get_cursor(0)) local screen = Screen.new(60, 3) - screen:attach() eq(1, eval('v:hlsearch')) screen:expect { grid = [[ @@ -3157,10 +3309,17 @@ describe('lua stdlib', function() eq('inext lines<ESC>', exec_lua [[return table.concat(keys, '')]]) end) - it('skips any function that caused an error', function() + it('skips any function that caused an error and shows stacktrace', function() insert([[hello world]]) exec_lua [[ + local function ErrF2() + error("Dumb Error") + end + local function ErrF1() + ErrF2() + end + keys = {} return vim.on_key(function(buf) @@ -3171,7 +3330,7 @@ describe('lua stdlib', function() table.insert(keys, buf) if buf == 'l' then - error("Dumb Error") + ErrF1() end end) ]] @@ -3181,6 +3340,19 @@ describe('lua stdlib', function() -- Only the first letter gets added. After that we remove the callback eq('inext l', exec_lua [[ return table.concat(keys, '') ]]) + + local errmsg = api.nvim_get_vvar('errmsg') + matches( + [[ +^Error executing vim%.on%_key%(%) callbacks:.* +With ns%_id %d+: .*: Dumb Error +stack traceback: +.*: in function 'error' +.*: in function 'ErrF2' +.*: in function 'ErrF1' +.*]], + errmsg + ) end) it('argument 1 is keys after mapping, argument 2 is typed keys', function() @@ -3223,6 +3395,109 @@ describe('lua stdlib', function() feed('<C-C>') eq('/', exec_lua([[return _G.ctrl_c_cmdtype]])) end) + + it('callback is not invoked recursively #30752', function() + local screen = Screen.new(60, 10) + exec_lua([[ + vim.on_key(function(key, typed) + vim.api.nvim_echo({ + { 'key_cb\n' }, + { ("KEYCB: key '%s', typed '%s'\n"):format(key, typed) }, + }, false, {}) + end) + ]]) + feed('^') + screen:expect([[ + | + {1:~ }|*5 + {3: }| + key_cb | + KEYCB: key '^', typed '^' | + {6:Press ENTER or type command to continue}^ | + ]]) + feed('<C-C>') + screen:expect([[ + | + {1:~ }|*3 + {3: }| + key_cb | + KEYCB: key '^', typed '^' | + key_cb | + KEYCB: key '{18:^C}', typed '{18:^C}' | + {6:Press ENTER or type command to continue}^ | + ]]) + feed('<C-C>') + screen:expect([[ + ^ | + {1:~ }|*8 + | + ]]) + end) + + it('can discard input', function() + clear() + -- discard every other normal 'x' command + exec_lua [[ + n_key = 0 + + vim.on_key(function(buf, typed_buf) + if typed_buf == 'x' then + n_key = n_key + 1 + end + return (n_key % 2 == 0) and "" or nil + end) + ]] + + api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) + + feed('x') + expect('4321') + feed('x') + expect('4321') + feed('x') + expect('321') + feed('x') + expect('321') + end) + + it('callback invalid return', function() + clear() + -- second key produces an error which removes the callback + exec_lua [[ + n_call = 0 + + vim.on_key(function(buf, typed_buf) + if typed_buf == 'x' then + n_call = n_call + 1 + end + return n_call >= 2 and '!' or nil + end) + ]] + + api.nvim_buf_set_lines(0, 0, -1, true, { '54321' }) + + local function cleanup_msg(msg) + return msg:gsub('^Error .*\nWith ns%_id %d+: ', '') + end + + feed('x') + eq(1, exec_lua [[ return n_call ]]) + + eq(1, exec_lua [[ return vim.on_key(nil, nil) ]]) + + eq('', cleanup_msg(eval('v:errmsg'))) + feed('x') + eq(2, exec_lua [[ return n_call ]]) + eq('return string must be empty', cleanup_msg(eval('v:errmsg'))) + command('let v:errmsg = ""') + + eq(0, exec_lua [[ return vim.on_key(nil, nil) ]]) + + feed('x') + eq(2, exec_lua [[ return n_call ]]) + expect('21') + eq('', cleanup_msg(eval('v:errmsg'))) + end) end) describe('vim.wait', function() @@ -3500,7 +3775,6 @@ describe('lua stdlib', function() it('fails in fast callbacks #26122', function() local screen = Screen.new(80, 10) - screen:attach() exec_lua([[ local timer = vim.uv.new_timer() timer:start(0, 0, function() @@ -3509,7 +3783,7 @@ describe('lua stdlib', function() end) ]]) screen:expect({ - any = pesc('E5560: vim.wait must not be called in a lua loop callback'), + any = pesc('E5560: vim.wait must not be called in a fast event context'), }) feed('<CR>') assert_alive() @@ -3518,7 +3792,6 @@ describe('lua stdlib', function() it('vim.notify_once', function() local screen = Screen.new(60, 5) - screen:attach() screen:expect { grid = [[ ^ | @@ -3715,7 +3988,6 @@ describe('lua stdlib', function() it('updates ruler if cursor moved', function() -- Fixed for win_execute in vim-patch:8.1.2124, but should've applied to nvim_win_call too! local screen = Screen.new(30, 5) - screen:attach() exec_lua [[ _G.api = vim.api vim.opt.ruler = true @@ -3858,7 +4130,6 @@ describe('lua stdlib', function() it('vim.lua_omnifunc', function() local screen = Screen.new(60, 5) - screen:attach() command [[ set omnifunc=v:lua.vim.lua_omnifunc ]] -- Note: the implementation is shared with lua command line completion. @@ -4060,11 +4331,16 @@ describe('vim.keymap', function() ) matches( - 'opts: expected table, got function', + 'rhs: expected string|function, got number', pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 42, function() end)]]) ) matches( + 'opts: expected table, got function', + pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 'x', function() end)]]) + ) + + matches( 'rhs: expected string|function, got number', pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 42)]]) ) |