diff options
Diffstat (limited to 'test/functional')
42 files changed, 2020 insertions, 318 deletions
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index 6f929ad1ca..d64d324a88 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -6,12 +6,18 @@ local command = helpers.command local curbufmeths = helpers.curbufmeths local eq = helpers.eq local meths = helpers.meths +local bufmeths = helpers.bufmeths +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 funcs = helpers.funcs describe('nvim_get_commands', function() - local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, } - local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, script_id=0, } + local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, keepscript=false, script_id=0, } + local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, keepscript=false, script_id=0, } before_each(clear) it('gets empty list if no commands were defined', function() @@ -53,11 +59,11 @@ describe('nvim_get_commands', function() end) it('gets various command attributes', function() - local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, script_id=0, } - local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, script_id=1, } - local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, } - local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, } - local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, script_id=4, } + local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, keepscript=false, script_id=0, } + local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, keepscript=false, script_id=1, } + local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, keepscript=false, script_id=2, } + local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, keepscript=false, script_id=3, } + local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, keepscript=false, script_id=4, } source([[ command -complete=custom,ListUsers -nargs=+ Finger !finger <args> ]]) @@ -78,3 +84,118 @@ describe('nvim_get_commands', function() eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false})) end) end) + +describe('nvim_add_user_command', function() + before_each(clear) + + it('works with strings', function() + meths.add_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1}) + meths.command('SomeCommand 42') + eq(42, meths.eval('g:command_fired')) + end) + + it('works with Lua functions', function() + exec_lua [[ + result = {} + vim.api.nvim_add_user_command('CommandWithLuaCallback', function(opts) + result = opts + end, { + nargs = "*", + bang = true, + count = 2, + }) + ]] + + eq({ + args = "hello", + bang = false, + line1 = 1, + line2 = 1, + mods = "", + range = 0, + count = 2, + reg = "", + }, exec_lua [[ + vim.api.nvim_command('CommandWithLuaCallback hello') + return result + ]]) + + eq({ + args = "", + bang = true, + line1 = 10, + line2 = 10, + mods = "botright", + range = 1, + count = 10, + reg = "", + }, exec_lua [[ + vim.api.nvim_command('botright 10CommandWithLuaCallback!') + return result + ]]) + + eq({ + args = "", + bang = false, + line1 = 1, + line2 = 42, + mods = "", + range = 1, + count = 42, + reg = "", + }, exec_lua [[ + vim.api.nvim_command('CommandWithLuaCallback 42') + return result + ]]) + end) + + it('can define buffer-local commands', function() + local bufnr = meths.create_buf(false, false) + bufmeths.add_user_command(bufnr, "Hello", "", {}) + matches("Not an editor command: Hello", pcall_err(meths.command, "Hello")) + meths.set_current_buf(bufnr) + meths.command("Hello") + assert_alive() + end) + + it('can use a Lua complete function', function() + exec_lua [[ + vim.api.nvim_add_user_command('Test', '', { + nargs = "*", + complete = function(arg, cmdline, pos) + local options = {"aaa", "bbb", "ccc"} + local t = {} + for _, v in ipairs(options) do + if string.find(v, "^" .. arg) then + table.insert(t, v) + end + end + return t + end, + }) + ]] + + feed(':Test a<Tab>') + eq('Test aaa', funcs.getcmdline()) + feed('<C-U>Test b<Tab>') + eq('Test bbb', funcs.getcmdline()) + end) +end) + +describe('nvim_del_user_command', function() + before_each(clear) + + it('can delete global commands', function() + meths.add_user_command('Hello', 'echo "Hi"', {}) + meths.command('Hello') + meths.del_user_command('Hello') + matches("Not an editor command: Hello", pcall_err(meths.command, "Hello")) + end) + + it('can delete buffer-local commands', function() + bufmeths.add_user_command(0, 'Hello', 'echo "Hi"', {}) + meths.command('Hello') + bufmeths.del_user_command(0, 'Hello') + matches("Not an editor command: Hello", pcall_err(meths.command, "Hello")) + end) +end) diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index a8f538b951..3f96efd4ef 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -110,6 +110,22 @@ describe('API/extmarks', function() pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 })) end) + it("can end extranges past final newline when strict mode is false", function() + set_extmark(ns, marks[1], 0, 0, { + end_col = 1, + end_row = 1, + strict = false, + }) + end) + + it("can end extranges past final column when strict mode is false", function() + set_extmark(ns, marks[1], 0, 0, { + end_col = 6, + end_row = 0, + strict = false, + }) + end) + it('adds, updates and deletes marks', function() local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2]) eq(marks[1], rv) @@ -1430,7 +1446,60 @@ describe('API/extmarks', function() end_col = 0, end_line = 1 }) - eq({ {1, 0, 0, { end_col = 0, end_row = 1 }} }, get_extmarks(ns, 0, -1, {details=true})) + eq({ {1, 0, 0, { + end_col = 0, + end_row = 1, + right_gravity = true, + end_right_gravity = false, + }} }, get_extmarks(ns, 0, -1, {details=true})) + end) + + it('can get details', function() + set_extmark(ns, marks[1], 0, 0, { + end_col = 0, + end_row = 1, + right_gravity = false, + end_right_gravity = true, + priority = 0, + hl_eol = true, + hl_mode = "blend", + hl_group = "String", + virt_text = { { "text", "Statement" } }, + virt_text_pos = "right_align", + virt_text_hide = true, + virt_lines = { { { "lines", "Statement" } }}, + virt_lines_above = true, + virt_lines_leftcol = true, + }) + set_extmark(ns, marks[2], 0, 0, { + priority = 0, + virt_text = { { "text", "Statement" } }, + virt_text_win_col = 1, + }) + eq({0, 0, { + end_col = 0, + end_row = 1, + right_gravity = false, + end_right_gravity = true, + priority = 0, + hl_eol = true, + hl_mode = "blend", + hl_group = "String", + virt_text = { { "text", "Statement" } }, + virt_text_pos = "right_align", + virt_text_hide = true, + virt_lines = { { { "lines", "Statement" } }}, + virt_lines_above = true, + virt_lines_leftcol = true, + } }, get_extmark_by_id(ns, marks[1], { details = true })) + eq({0, 0, { + right_gravity = true, + priority = 0, + virt_text = { { "text", "Statement" } }, + virt_text_hide = false, + virt_text_pos = "win_col", + virt_text_win_col = 1, + } }, get_extmark_by_id(ns, marks[2], { details = true })) end) end) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index dd8eef7ca0..450a76ddac 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -5,6 +5,7 @@ local clear = helpers.clear local command = helpers.command local curbufmeths = helpers.curbufmeths local eq, neq = helpers.eq, helpers.neq +local exec_lua = helpers.exec_lua local feed = helpers.feed local funcs = helpers.funcs local meths = helpers.meths @@ -316,6 +317,55 @@ describe('nvim_get_keymap', function() command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>') eq({space_table}, meths.get_keymap('n')) end) + + it('can handle lua keymaps', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]]) + + feed('asdf\n') + eq(1, exec_lua[[return GlobalCount]]) + + eq(2, exec_lua[[ + vim.api.nvim_get_keymap('n')[1].callback() + return GlobalCount + ]]) + local mapargs = meths.get_keymap('n') + assert.Truthy(type(mapargs[1].callback) == 'number', 'callback is not luaref number') + mapargs[1].callback = nil + eq({ + lhs='asdf', + script=0, + silent=0, + expr=0, + sid=0, + buffer=0, + nowait=0, + mode='n', + noremap=0, + lnum=0, + }, mapargs[1]) + end) + + it ('can handle map descriptions', function() + meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"}) + eq({ + lhs='lhs', + rhs='rhs', + script=0, + silent=0, + expr=0, + sid=0, + buffer=0, + nowait=0, + mode='n', + noremap=0, + lnum=0, + desc='map description' + }, meths.get_keymap('n')[1]) + end) end) describe('nvim_set_keymap, nvim_del_keymap', function() @@ -353,6 +403,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() to_return.sid = not opts.sid and 0 or opts.sid to_return.buffer = not opts.buffer and 0 or opts.buffer to_return.lnum = not opts.lnum and 0 or opts.lnum + to_return.desc = opts.desc return to_return end @@ -717,6 +768,115 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end) end end + + it('can make lua mappings', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + + end) + + it (':map command shows lua keymap correctly', function() + exec_lua [[ + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end }) + ]] + assert.truthy(string.match(exec_lua[[return vim.api.nvim_exec(':nmap asdf', true)]], + "^\nn asdf <Lua function %d+>")) + end) + + it ('mapcheck() returns lua keymap correctly', function() + exec_lua [[ + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end }) + ]] + assert.truthy(string.match(funcs.mapcheck('asdf', 'n'), + "^<Lua function %d+>")) + end) + + it ('maparg() returns lua keymap correctly', function() + exec_lua [[ + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end }) + ]] + assert.truthy(string.match(funcs.maparg('asdf', 'n'), + "^<Lua function %d+>")) + local mapargs = funcs.maparg('asdf', 'n', false, true) + assert.Truthy(type(mapargs.callback) == 'number', 'callback is not luaref number') + mapargs.callback = nil + eq(generate_mapargs('n', 'asdf', nil, {}), mapargs) + end) + + it('can make lua expr mappings', function() + exec_lua [[ + vim.api.nvim_set_keymap ('n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true }) + ]] + + feed('aa') + + eq(99, exec_lua[[return SomeValue]]) + end) + + it('does not reset pum in lua mapping', function() + eq(0, exec_lua [[ + VisibleCount = 0 + vim.api.nvim_set_keymap ('i', '<F2>', '', {callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end}) + return VisibleCount + ]]) + feed('i<C-X><C-V><F2><F2><esc>') + eq(2, exec_lua[[return VisibleCount]]) + end) + + it('can overwrite lua mappings', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + + exec_lua [[ + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end }) + ]] + + feed('asdf\n') + + eq(0, exec_lua[[return GlobalCount]]) + end) + + it('can unmap lua mappings', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + + 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')) + end) + + it('can set descriptions on keymaps', function() + meths.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")) + end) end) describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() @@ -814,4 +974,67 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() pcall_err(bufmeths.set_keymap, 100, '', 'lsh', 'irhs<Esc>', {}) helpers.assert_alive() end) + + it('can make lua mappings', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + end) + + it('can make lua expr mappings', function() + exec_lua [[ + vim.api.nvim_buf_set_keymap (0, 'n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true }) + ]] + + feed('aa') + + eq(99, exec_lua[[return SomeValue ]]) + end) + + it('can overwrite lua mappings', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + + exec_lua [[ + vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end }) + ]] + + feed('asdf\n') + + eq(0, exec_lua[[return GlobalCount]]) + end) + + it('can unmap lua mappings', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + + 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')) + end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 112adfaf2d..201ba45803 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1154,8 +1154,8 @@ describe('API', function() end) end) - describe('RPC (K_EVENT) #6166', function() - it('does not complete ("interrupt") normal-mode operator-pending', function() + describe('RPC (K_EVENT)', function() + it('does not complete ("interrupt") normal-mode operator-pending #6166', function() helpers.insert([[ FIRST LINE SECOND LINE]]) @@ -1191,7 +1191,7 @@ describe('API', function() ]]) end) - it('does not complete ("interrupt") normal-mode map-pending', function() + it('does not complete ("interrupt") normal-mode map-pending #6166', function() command("nnoremap dd :let g:foo='it worked...'<CR>") helpers.insert([[ FIRST LINE @@ -1207,7 +1207,8 @@ describe('API', function() SECOND LINE]]) eq('it worked...', helpers.eval('g:foo')) end) - it('does not complete ("interrupt") insert-mode map-pending', function() + + it('does not complete ("interrupt") insert-mode map-pending #6166', function() command('inoremap xx foo') command('set timeoutlen=9999') helpers.insert([[ @@ -1222,6 +1223,37 @@ describe('API', function() FIRST LINE SECOND LINfooE]]) end) + + it('does not interrupt Insert mode i_CTRL-O #10035', function() + feed('iHello World<c-o>') + eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event + eq(2, eval('1+1')) -- causes K_EVENT key + eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode + feed('dd') + eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode + expect('') -- executed the command + end) + + it('does not interrupt Select mode v_CTRL-O #15688', function() + feed('iHello World<esc>gh<c-o>') + eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event + eq({mode='vs', blocking=false}, meths.get_mode()) -- again #15288 + eq(2, eval('1+1')) -- causes K_EVENT key + eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode + feed('^') + eq({mode='s', blocking=false}, meths.get_mode()) -- left ctrl-o mode + feed('h') + eq({mode='i', blocking=false}, meths.get_mode()) -- entered insert mode + expect('h') -- selection is the whole line and is replaced + end) + + it('does not interrupt Insert mode i_0_CTRL-D #13997', function() + command('set timeoutlen=9999') + feed('i<Tab><Tab>a0') + eq(2, eval('1+1')) -- causes K_EVENT key + feed('<C-D>') + expect('a') -- recognized i_0_CTRL-D + end) end) describe('nvim_get_context', function() @@ -1335,18 +1367,18 @@ describe('API', function() end) describe('nvim_feedkeys', function() - it('CSI escaping', function() + it('K_SPECIAL escaping', function() local function on_setup() -- notice the special char(…) \xe2\80\xa6 nvim('feedkeys', ':let x1="…"\n', '', true) -- Both nvim_replace_termcodes and nvim_feedkeys escape \x80 local inp = helpers.nvim('replace_termcodes', ':let x2="…"<CR>', true, true, true) - nvim('feedkeys', inp, '', true) -- escape_csi=true + nvim('feedkeys', inp, '', true) -- escape_ks=true - -- nvim_feedkeys with CSI escaping disabled + -- nvim_feedkeys with K_SPECIAL escaping disabled inp = helpers.nvim('replace_termcodes', ':let x3="…"<CR>', true, true, true) - nvim('feedkeys', inp, '', false) -- escape_csi=false + nvim('feedkeys', inp, '', false) -- escape_ks=false helpers.stop() end diff --git a/test/functional/autocmd/recording_spec.lua b/test/functional/autocmd/recording_spec.lua index 81152758de..b9aec774f1 100644 --- a/test/functional/autocmd/recording_spec.lua +++ b/test/functional/autocmd/recording_spec.lua @@ -11,7 +11,7 @@ describe('RecordingEnter', function() source_vim [[ let g:recorded = 0 autocmd RecordingEnter * let g:recorded += 1 - execute "normal! qqyyq" + call feedkeys("qqyyq", 'xt') ]] eq(1, eval('g:recorded')) end) @@ -20,7 +20,7 @@ describe('RecordingEnter', function() source_vim [[ let g:recording = '' autocmd RecordingEnter * let g:recording = reg_recording() - execute "normal! qqyyq" + call feedkeys("qqyyq", 'xt') ]] eq('q', eval('g:recording')) end) @@ -32,7 +32,7 @@ describe('RecordingLeave', function() source_vim [[ let g:recorded = 0 autocmd RecordingLeave * let g:recorded += 1 - execute "normal! qqyyq" + call feedkeys("qqyyq", 'xt') ]] eq(1, eval('g:recorded')) end) @@ -43,10 +43,30 @@ describe('RecordingLeave', function() let g:recording = '' autocmd RecordingLeave * let g:recording = reg_recording() autocmd RecordingLeave * let g:recorded = reg_recorded() - execute "normal! qqyyq" + call feedkeys("qqyyq", 'xt') ]] eq('q', eval 'g:recording') eq('', eval 'g:recorded') eq('q', eval 'reg_recorded()') end) + + it('populates v:event', function() + source_vim [[ + let g:regname = '' + let g:regcontents = '' + autocmd RecordingLeave * let g:regname = v:event.regname + autocmd RecordingLeave * let g:regcontents = v:event.regcontents + call feedkeys("qqyyq", 'xt') + ]] + eq('q', eval 'g:regname') + eq('yy', eval 'g:regcontents') + end) + + it('resets v:event', function() + source_vim [[ + autocmd RecordingLeave * let g:event = v:event + call feedkeys("qqyyq", 'xt') + ]] + eq(0, eval 'len(v:event)') + end) end) diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 93dec9fb35..c28300f0f4 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -100,6 +100,38 @@ describe('channels', function() eq({"notification", "exit", {3,0}}, next_msg()) end) + it('can use stdio channel and on_print callback', function() + source([[ + let g:job_opts = { + \ 'on_stdout': function('OnEvent'), + \ 'on_stderr': function('OnEvent'), + \ 'on_exit': function('OnEvent'), + \ } + ]]) + meths.set_var("nvim_prog", nvim_prog) + meths.set_var("code", [[ + function! OnStdin(id, data, event) dict + echo string([a:id, a:data, a:event]) + if a:data == [''] + quit + endif + endfunction + function! OnPrint(text) dict + call chansend(g:x, ['OnPrint:' .. a:text]) + endfunction + let g:x = stdioopen({'on_stdin': funcref('OnStdin'), 'on_print':'OnPrint'}) + call chansend(x, "hello") + ]]) + command("let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)") + local id = eval("g:id") + ok(id > 0) + + eq({ "notification", "stdout", {id, { "hello" } } }, next_msg()) + + command("call chansend(id, 'howdy')") + eq({"notification", "stdout", {id, {"OnPrint:[1, ['howdy'], 'stdin']"}}}, next_msg()) + end) + local function expect_twoline(id, stream, line1, line2, nobr) local msg = next_msg() local joined = nobr and {line1..line2} or {line1, line2} diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 4220d68ee1..2fa84e8313 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -224,6 +224,23 @@ describe('startup', function() end end) + it('-e sets ex mode', function() + local screen = Screen.new(25, 3) + clear('-e') + screen:attach() + -- Verify we set the proper mode both before and after :vi. + feed("put =mode(1)<CR>vi<CR>:put =mode(1)<CR>") + screen:expect([[ + cv | + ^n | + :put =mode(1) | + ]]) + + eq('cv\n', + funcs.system({nvim_prog, '-n', '-es' }, + { 'put =mode(1)', 'print', '' })) + end) + it('fails on --embed with -es/-Es', function() matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es', funcs.system({nvim_prog, '--embed', '-es' })) @@ -651,11 +668,7 @@ describe('runtime:', function() mkdir_p(ftdetect_folder) write_file(ftdetect_file , [[vim.g.lua_ftdetect = 1]]) - -- TODO(shadmansaleh): Figure out why this test fails without - -- setting VIMRUNTIME - clear{ args_rm={'-u'}, env={XDG_CONFIG_HOME=xconfig, - XDG_DATA_HOME=xdata, - VIMRUNTIME='runtime/'}} + clear{ args_rm={'-u'}, env=xenv } eq(1, eval('g:lua_ftdetect')) rmdir(ftdetect_folder) diff --git a/test/functional/editor/K_spec.lua b/test/functional/editor/K_spec.lua index 40f36491e4..8ad81ac3d6 100644 --- a/test/functional/editor/K_spec.lua +++ b/test/functional/editor/K_spec.lua @@ -33,6 +33,29 @@ describe('K', function() feed('i'..test_file..'<ESC>K') retry(nil, nil, function() eq(1, eval('filereadable("'..test_file..'")')) end) eq({'fnord'}, eval("readfile('"..test_file.."')")) + -- Confirm that Neovim is still in terminal mode after K is pressed (#16692). + helpers.sleep(500) + eq('t', eval('mode()')) + feed('<space>') -- Any key, not just <space>, can be used here to escape. + eq('n', eval('mode()')) + end) + + it("<esc> kills the buffer for a running 'keywordprg' command", function() + helpers.source('set keywordprg=less') + eval('writefile(["hello", "world"], "' .. test_file .. '")') + feed('i' .. test_file .. '<esc>K') + eq('t', eval('mode()')) + -- Confirm that an arbitrary keypress doesn't escape (i.e., the process is + -- still running). If the process were no longer running, an arbitrary + -- keypress would escape. + helpers.sleep(500) + feed('<space>') + eq('t', eval('mode()')) + -- Confirm that <esc> kills the buffer for the running command. + local bufnr = eval('bufnr()') + feed('<esc>') + eq('n', eval('mode()')) + helpers.neq(bufnr, eval('bufnr()')) end) end) diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua index 2280f5bb24..8efcc81616 100644 --- a/test/functional/editor/meta_key_spec.lua +++ b/test/functional/editor/meta_key_spec.lua @@ -27,6 +27,14 @@ describe('meta-keys #8226 #13042', function() command('nnoremap <A-j> Aalt-j<Esc>') feed('<A-j><M-l>') expect('llo<ESC>;<ESC>;alt-jmeta-l') + -- Unmapped ALT-chord with characters containing K_SPECIAL bytes + command('nnoremap … A…<Esc>') + feed('<A-…><M-…>') + expect('llo<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…') + command("execute 'nnoremap' nr2char(0x40000000) 'AMAX<Esc>'") + command("call nvim_input('<A-'.nr2char(0x40000000).'>')") + command("call nvim_input('<M-'.nr2char(0x40000000).'>')") + expect('llo<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…<ESC>MAX<ESC>MAX') end) it('ALT/META, visual-mode', function() @@ -36,13 +44,20 @@ describe('meta-keys #8226 #13042', function() expect('peach') -- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869 command('vnoremap <ESC> A<lt>ESC>') - feed('viw<A-;><ESC>viw<M-;><ESC>') + feed('viw<A-;><Esc>viw<M-;><Esc>') expect('peach<ESC>;<ESC>;') -- Mapped ALT-chord behaves as mapped. command('vnoremap <M-l> Ameta-l<Esc>') command('vnoremap <A-j> Aalt-j<Esc>') feed('viw<A-j>viw<M-l>') expect('peach<ESC>;<ESC>;alt-jmeta-l') + -- Unmapped ALT-chord with characters containing K_SPECIAL bytes + feed('viw<A-…><Esc>viw<M-…><Esc>') + expect('peach<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…') + command("execute 'inoremap' nr2char(0x40000000) 'MAX'") + command("call nvim_input('viw<A-'.nr2char(0x40000000).'><Esc>')") + command("call nvim_input('viw<M-'.nr2char(0x40000000).'><Esc>')") + expect('peach<ESC>;<ESC>;alt-jmeta-l<ESC>…<ESC>…<ESC>MAX<ESC>MAX') end) it('ALT/META insert-mode', function() @@ -89,4 +104,20 @@ describe('meta-keys #8226 #13042', function() eq({ 0, 2, 1, 0, }, funcs.getpos('.')) eq('nt', eval('mode(1)')) end) + + it('ALT/META when recording a macro #13235', function() + feed('ifoo<CR>bar<CR>baz<Esc>gg0') + -- <M-"> is reinterpreted as <Esc>" + feed('qrviw"ayC// This is some text: <M-">apq') + expect([[ + // This is some text: foo + bar + baz]]) + -- Should not insert an extra double quote when replaying + feed('j0@rj0@@') + expect([[ + // This is some text: foo + // This is some text: bar + // This is some text: baz]]) + end) end) diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua index 46ab483036..528e228121 100644 --- a/test/functional/editor/mode_insert_spec.lua +++ b/test/functional/editor/mode_insert_spec.lua @@ -6,7 +6,6 @@ local expect = helpers.expect local command = helpers.command local eq = helpers.eq local eval = helpers.eval -local meths = helpers.meths describe('insert-mode', function() before_each(function() @@ -75,15 +74,58 @@ describe('insert-mode', function() feed('ooo') expect('hello oooworld') end) + end) + + describe('Ctrl-V', function() + it('supports entering the decimal value of a character', function() + feed('i<C-V>076<C-V>167') + expect('L§') + end) + + it('supports entering the octal value of a character with "o"', function() + feed('i<C-V>o114<C-V>o247<Esc>') + expect('L§') + end) + + it('supports entering the octal value of a character with "O"', function() + feed('i<C-V>O114<C-V>O247<Esc>') + expect('L§') + end) + + it('supports entering the hexadecimal value of a character with "x"', function() + feed('i<C-V>x4c<C-V>xA7<Esc>') + expect('L§') + end) + + it('supports entering the hexadecimal value of a character with "X"', function() + feed('i<C-V>X4c<C-V>XA7<Esc>') + expect('L§') + end) + + it('supports entering the hexadecimal value of a character with "u"', function() + feed('i<C-V>u25ba<C-V>u25C7<Esc>') + expect('►◇') + end) + + it('supports entering the hexadecimal value of a character with "U"', function() + feed('i<C-V>U0001f600<C-V>U0001F601<Esc>') + expect('😀😁') + end) + + it('entering character by value is interrupted by invalid character', function() + feed('i<C-V>76c<C-V>76<C-F2><C-V>u3c0j<C-V>u3c0<M-F3><C-V>U1f600j<C-V>U1f600<D-F4><Esc>') + expect('LcL<C-F2>πjπ<M-F3>😀j😀<D-F4>') + end) - it("doesn't cancel Ctrl-O mode when processing event", function() - feed('iHello World<c-o>') - eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event - eq(2, eval('1+1')) -- causes K_EVENT key - eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode - feed('dd') - eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode - expect('') -- executed the command + it('shows o, O, u, U, x, X, and digits with modifiers', function() + feed('i<C-V><M-o><C-V><D-o><C-V><M-O><C-V><D-O><Esc>') + expect('<M-o><D-o><M-O><D-O>') + feed('cc<C-V><M-u><C-V><D-u><C-V><M-U><C-V><D-U><Esc>') + expect('<M-u><D-u><M-U><D-U>') + feed('cc<C-V><M-x><C-V><D-x><C-V><M-X><C-V><D-X><Esc>') + expect('<M-x><D-x><M-X><D-X>') + feed('cc<C-V><M-1><C-V><D-2><C-V><M-7><C-V><D-8><Esc>') + expect('<M-1><D-2><M-7><D-8>') end) end) end) diff --git a/test/functional/editor/mode_visual_spec.lua b/test/functional/editor/mode_visual_spec.lua deleted file mode 100644 index 468ae00e01..0000000000 --- a/test/functional/editor/mode_visual_spec.lua +++ /dev/null @@ -1,27 +0,0 @@ --- Visual-mode tests. - -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local expect = helpers.expect -local feed = helpers.feed -local meths = helpers.meths - -describe('visual-mode', function() - before_each(clear) - - it("select-mode Ctrl-O doesn't cancel Ctrl-O mode when processing event #15688", function() - feed('iHello World<esc>gh<c-o>') - eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event - eq({mode='vs', blocking=false}, meths.get_mode()) -- again #15288 - eq(2, eval('1+1')) -- causes K_EVENT key - eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode - feed('^') - eq({mode='s', blocking=false}, meths.get_mode()) -- left ctrl-o mode - feed('h') - eq({mode='i', blocking=false}, meths.get_mode()) -- entered insert mode - expect('h') -- selection is the whole line and is replaced - end) -end) - diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua index 26967ecbba..fdda2be131 100644 --- a/test/functional/editor/put_spec.lua +++ b/test/functional/editor/put_spec.lua @@ -64,7 +64,7 @@ describe('put command', function() -- one place to the right (unless we were at the end of the -- line when we pasted). if not (exception_table.undo_position and after_undo) then - eq(funcs.getcurpos(), init_cursorpos) + eq(init_cursorpos, funcs.getcurpos()) end end @@ -86,7 +86,7 @@ describe('put command', function() -- If we paste the ". register with a count we can't avoid -- changing this register, hence avoid this check. if not test.exception_table.dot_reg_changed then - eq(funcs.getreg('.'), orig_dotstr) + eq(orig_dotstr, funcs.getreg('.')) end -- Doing something, undoing it, and then redoing it should @@ -507,9 +507,9 @@ describe('put command', function() return function(exception_table, after_redo) test_expect(exception_table, after_redo) if selection_string then - eq(getreg('"'), selection_string) + eq(selection_string, getreg('"')) else - eq(getreg('"'), 'test_string"') + eq('test_string"', getreg('"')) end end end @@ -714,7 +714,7 @@ describe('put command', function() expect_base, conversion_table) return function(exception_table, after_redo) test_expect(exception_table, after_redo) - eq(getreg('"'), 'Line of words 1\n') + eq('Line of words 1\n', getreg('"')) end end local base_expect_string = [[ @@ -748,7 +748,7 @@ describe('put command', function() end, expect_base, conversion_table) return function(e,c) test_expect(e,c) - eq(getreg('"'), 'Lin\nLin') + eq('Lin\nLin', getreg('"')) end end @@ -800,9 +800,9 @@ describe('put command', function() feed('u') -- Have to use feed('u') here to set curswant, because -- ex_undo() doesn't do that. - eq(funcs.getcurpos(), {0, 1, 1, 0, 1}) + eq({0, 1, 1, 0, 1}, funcs.getcurpos()) feed('<C-r>') - eq(funcs.getcurpos(), {0, 1, 1, 0, 1}) + eq({0, 1, 1, 0, 1}, funcs.getcurpos()) end end diff --git a/test/functional/ex_cmds/append_spec.lua b/test/functional/ex_cmds/append_spec.lua index 0a4d701794..fadb5c9b42 100644 --- a/test/functional/ex_cmds/append_spec.lua +++ b/test/functional/ex_cmds/append_spec.lua @@ -1,23 +1,26 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq +local dedent = helpers.dedent +local exec = helpers.exec local feed = helpers.feed local clear = helpers.clear local funcs = helpers.funcs local command = helpers.command local curbufmeths = helpers.curbufmeths - -before_each(function() - clear() - curbufmeths.set_lines(0, 1, true, { 'foo', 'bar', 'baz' }) -end) - -local buffer_contents = function() - return curbufmeths.get_lines(0, -1, false) -end +local Screen = require('test.functional.ui.screen') local cmdtest = function(cmd, prep, ret1) describe(':' .. cmd, function() + before_each(function() + clear() + curbufmeths.set_lines(0, 1, true, { 'foo', 'bar', 'baz' }) + end) + + local buffer_contents = function() + return curbufmeths.get_lines(0, -1, false) + end + it(cmd .. 's' .. prep .. ' the current line by default', function() command(cmd .. '\nabc\ndef\n') eq(ret1, buffer_contents()) @@ -52,3 +55,52 @@ end cmdtest('insert', ' before', { 'abc', 'def', 'foo', 'bar', 'baz' }) cmdtest('append', ' after', { 'foo', 'abc', 'def', 'bar', 'baz' }) cmdtest('change', '', { 'abc', 'def', 'bar', 'baz' }) + +describe('the first line is redrawn correctly after inserting text in an empty buffer', function() + local screen + before_each(function() + clear() + screen = Screen.new(20, 8) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue}, + [2] = {bold = true, reverse = true}, + }) + screen:attach() + end) + + it('using :append', function() + exec(dedent([[ + append + aaaaa + bbbbb + .]])) + screen:expect([[ + aaaaa | + ^bbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) + + it('using :insert', function() + exec(dedent([[ + insert + aaaaa + bbbbb + .]])) + screen:expect([[ + aaaaa | + ^bbbbb | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) +end) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 09eaa36686..aefcc53cab 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -5,6 +5,7 @@ local clear = helpers.clear local command = helpers.command local get_pathsep = helpers.get_pathsep local eq = helpers.eq +local neq = helpers.neq local funcs = helpers.funcs local matches = helpers.matches local pesc = helpers.pesc @@ -30,7 +31,8 @@ describe(':mksession', function() -- If the same :terminal is displayed in multiple windows, :mksession -- should restore it as such. - -- Create two windows showing the same :terminal buffer. + -- Create three windows: first two from top show same terminal, third - + -- another one (created earlier). command('terminal') command('split') command('terminal') @@ -43,8 +45,8 @@ describe(':mksession', function() -- Restore session. command('source '..session_file) - eq({2,2,4}, - {funcs.winbufnr(1), funcs.winbufnr(2), funcs.winbufnr(3)}) + eq(funcs.winbufnr(1), funcs.winbufnr(2)) + neq(funcs.winbufnr(1), funcs.winbufnr(3)) end) it('restores tab-local working directories', function() @@ -91,12 +93,7 @@ describe(':mksession', function() command('tabnext 1') eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '1', funcs.expand('%:p')) command('tabnext 2') - -- :mksession stores paths using unix slashes, but Nvim doesn't adjust these - -- for absolute paths in all cases yet. Absolute paths are used in the - -- session file after :tcd, so we need to expect unix slashes here for now - -- eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p')) - eq(cwd_dir:gsub([[\]], '/') .. '/' .. tmpfile_base .. '2', - funcs.expand('%:p')) + eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p')) end) it('restores CWD for :terminal buffers #11288', function() diff --git a/test/functional/ex_cmds/script_spec.lua b/test/functional/ex_cmds/script_spec.lua index 0a772c559b..f249b9ba7c 100644 --- a/test/functional/ex_cmds/script_spec.lua +++ b/test/functional/ex_cmds/script_spec.lua @@ -62,10 +62,10 @@ describe('script_get-based command', function() -- Provider-based scripts test_garbage_exec('ruby', not missing_provider('ruby')) - test_garbage_exec('python', not missing_provider('python')) test_garbage_exec('python3', not missing_provider('python3')) -- Missing scripts + test_garbage_exec('python', false) test_garbage_exec('tcl', false) test_garbage_exec('mzscheme', false) test_garbage_exec('perl', false) diff --git a/test/functional/fixtures/pack/foo/start/bar/lua/baz-quux.lua b/test/functional/fixtures/pack/foo/start/bar/lua/baz-quux.lua new file mode 100644 index 0000000000..c1c33d787e --- /dev/null +++ b/test/functional/fixtures/pack/foo/start/bar/lua/baz-quux.lua @@ -0,0 +1 @@ +return {doit=function() return 9004 end} diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index f152a487af..1845786c4b 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -451,6 +451,7 @@ function module.new_argv(...) 'GCOV_ERROR_FILE', 'XDG_DATA_DIRS', 'TMPDIR', + 'VIMRUNTIME', }) do if not env_tbl[k] then env_tbl[k] = os.getenv(k) diff --git a/test/functional/legacy/listchars_spec.lua b/test/functional/legacy/listchars_spec.lua index dc6ccd3628..7a1afa1fd6 100644 --- a/test/functional/legacy/listchars_spec.lua +++ b/test/functional/legacy/listchars_spec.lua @@ -1,7 +1,8 @@ -- Tests for 'listchars' display with 'list' and :list. local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local Screen = require('test.functional.ui.screen') +local feed, insert, exec = helpers.feed, helpers.insert, helpers.exec local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -- luacheck: ignore 621 (Indentation) @@ -13,7 +14,7 @@ describe("'listchars'", function() -- luacheck: ignore 613 (Trailing whitespace in a string) it("works with 'list'", function() - source([[ + exec([[ function GetScreenCharsForLine(lnum) return join(map(range(1, virtcol('$')), 'nr2char(screenchar(a:lnum, v:val))'), '') endfunction @@ -98,4 +99,80 @@ describe("'listchars'", function() .....h>-$ iii<<<<><<$]]) end) + + it('"exceeds" character does not appear in foldcolumn vim-patch:8.2.3121', function() + local screen = Screen.new(60, 10) + screen:attach() + exec([[ + call setline(1, ['aaa', '', 'a', 'aaaaaa']) + vsplit + vsplit + windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:< + ]]) + feed('13<C-W>>') + screen:expect([[ + aaa │ a>│ ^aaa | + │ │ | + a │ a │ a | + aaaaaa │ a>│ aaaaaa | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + [No Name] [+] <[+] [No Name] [+] | + | + ]]) + feed('<C-W>>') + screen:expect([[ + aaa │ >│ ^aaa | + │ │ | + a │ a│ a | + aaaaaa │ >│ aaaaaa | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + [No Name] [+] <+] [No Name] [+] | + | + ]]) + feed('<C-W>>') + screen:expect([[ + aaa │ │ ^aaa | + │ │ | + a │ │ a | + aaaaaa │ │ aaaaaa | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + [No Name] [+] <] [No Name] [+] | + | + ]]) + feed('<C-W>>') + screen:expect([[ + aaa │ │ ^aaa | + │ │ | + a │ │ a | + aaaaaa │ │ aaaaaa | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + ~ │~ │~ | + [No Name] [+] < [No Name] [+] | + | + ]]) + feed('<C-W>>') + screen:expect([[ + aaa │ │ ^aaa | + │ │ | + a │ │ a | + aaaaaa │ │ aaaaaa | + ~ │~│~ | + ~ │~│~ | + ~ │~│~ | + ~ │~│~ | + [No Name] [+] < [No Name] [+] | + | + ]]) + end) end) diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index d86caca0e9..8d25b9d927 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -195,10 +195,10 @@ describe('memory usage', function() local after = monitor_memory_usage(pid) source('bwipe!') poke_eventloop() - -- Allow for an increase of 5% in memory usage, which accommodates minor fluctuation, + -- Allow for an increase of 10% in memory usage, which accommodates minor fluctuation, -- but is small enough that if memory were not released (prior to PR #14884), the test -- would fail. - local upper = before.last * 1.05 + local upper = before.last * 1.10 check_result({before=before, after=after}, pcall(ok, after.last <= upper)) end) end) diff --git a/test/functional/legacy/options_spec.lua b/test/functional/legacy/options_spec.lua index 023cdd4ae1..bd14f3bc53 100644 --- a/test/functional/legacy/options_spec.lua +++ b/test/functional/legacy/options_spec.lua @@ -83,4 +83,9 @@ describe('set', function() Press ENTER or type command to continue^ | ]]) end) + + it('foldcolumn and signcolumn to empty string is disallowed', function() + matches('E474: Invalid argument: fdc=', exc_exec('set fdc=')) + matches('E474: Invalid argument: scl=', exc_exec('set scl=')) + end) end) diff --git a/test/functional/legacy/visual_mode_spec.lua b/test/functional/legacy/visual_mode_spec.lua index c8e83ed649..8b5dd0c2dc 100644 --- a/test/functional/legacy/visual_mode_spec.lua +++ b/test/functional/legacy/visual_mode_spec.lua @@ -1,5 +1,3 @@ --- Test visual line mode selection redraw after scrolling - local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') @@ -10,6 +8,7 @@ local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths local eq = helpers.eq +local exec = helpers.exec describe('visual line mode', function() local screen @@ -40,3 +39,44 @@ describe('visual line mode', function() ]]) end) end) + +describe('visual block mode', function() + it('shows selection correctly with virtualedit=block', function() + clear() + local screen = Screen.new(30, 7) + screen:set_default_attr_ids({ + [1] = {bold = true}, -- ModeMsg + [2] = {background = Screen.colors.LightGrey}, -- Visual + [3] = {foreground = Screen.colors.Blue, bold = true} -- NonText + }) + screen:attach() + + exec([[ + call setline(1, ['aaaaaa', 'bbbb', 'cc']) + set virtualedit=block + normal G + ]]) + + feed('<C-V>gg$') + screen:expect([[ + {2:aaaaaa}^ | + {2:bbbb } | + {2:cc } | + {3:~ }| + {3:~ }| + {3:~ }| + {1:-- VISUAL BLOCK --} | + ]]) + + feed('<Esc>gg<C-V>G$') + screen:expect([[ + {2:aaaaaa } | + {2:bbbb } | + {2:cc}^ {2: } | + {3:~ }| + {3:~ }| + {3:~ }| + {1:-- VISUAL BLOCK --} | + ]]) + end) +end) diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua index 81e00bba6d..cb37fb9a1c 100644 --- a/test/functional/lua/api_spec.lua +++ b/test/functional/lua/api_spec.lua @@ -8,6 +8,7 @@ local clear = helpers.clear local eval = helpers.eval local NIL = helpers.NIL local eq = helpers.eq +local exec_lua = helpers.exec_lua before_each(clear) @@ -111,6 +112,12 @@ describe('luaeval(vim.api.…)', function() eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]])) eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})')) + + eq(true, funcs.luaeval('vim.api.nvim__id(vim.api.nvim__id)(true)')) + eq(42, exec_lua [[ + local f = vim.api.nvim__id({42, vim.api.nvim__id}) + return f[2](f[1]) + ]]) end) it('correctly converts container objects with type_idx to API objects', function() @@ -159,12 +166,8 @@ describe('luaeval(vim.api.…)', function() it('errors out correctly when working with API', function() -- Conversion errors - eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type', - remove_trace(exc_exec([[call luaeval("vim.api.nvim__id(vim.api.nvim__id)")]]))) eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table', remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]]))) - eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type', - remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({42, vim.api.nvim__id})")]]))) -- Errors in number of arguments eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument', remove_trace(exc_exec([[call luaeval("vim.api.nvim__id()")]]))) @@ -180,6 +183,8 @@ describe('luaeval(vim.api.…)', function() remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]]))) eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Number is not integral', remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]]))) + eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected Lua number', + remove_trace(exc_exec([[call luaeval("vim.api.nvim_win_is_valid(nil)")]]))) eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table', remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]]))) diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index 9b9ba531b0..b8346df290 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -141,6 +141,24 @@ describe(':lua command', function() {4:Press ENTER or type command to continue}^ | ]]} end) + + it('Can print results of =expr', function() + helpers.exec_lua("x = 5") + eq("5", helpers.exec_capture(':lua =x')) + helpers.exec_lua("function x() return 'hello' end") + eq([["hello"]], helpers.exec_capture(':lua = x()')) + helpers.exec_lua("x = {a = 1, b = 2}") + eq("{\n a = 1,\n b = 2\n}", helpers.exec_capture(':lua =x')) + helpers.exec_lua([[function x(success) + if success then + return true, "Return value" + else + return false, nil, "Error message" + end + end]]) + eq([[true "Return value"]], helpers.exec_capture(':lua =x(true)')) + eq([[false nil "Error message"]], helpers.exec_capture(':lua =x(false)')) + end) end) describe(':luado command', function() diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index a88da63e90..b58fad1cab 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -208,10 +208,10 @@ describe('vim.diagnostic', function() eq(all_highlights, exec_lua [[ local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), - make_warning("Warning on Server 1", 2, 1, 2, 5), + make_warning("Warning on Server 1", 2, 1, 2, 3), } local ns_2_diags = { - make_warning("Warning 1", 2, 1, 2, 5), + make_warning("Warning 1", 2, 1, 2, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) @@ -255,10 +255,10 @@ describe('vim.diagnostic', function() eq({0, 2}, exec_lua [[ local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), - make_warning("Warning on Server 1", 2, 1, 2, 5), + make_warning("Warning on Server 1", 2, 1, 2, 3), } local ns_2_diags = { - make_warning("Warning 1", 2, 1, 2, 5), + make_warning("Warning 1", 2, 1, 2, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) @@ -599,10 +599,10 @@ describe('vim.diagnostic', function() eq(all_highlights, exec_lua [[ local ns_1_diags = { make_error("Error 1", 1, 1, 1, 5), - make_warning("Warning on Server 1", 2, 1, 2, 5), + make_warning("Warning on Server 1", 2, 1, 2, 3), } local ns_2_diags = { - make_warning("Warning 1", 2, 1, 2, 5), + make_warning("Warning 1", 2, 1, 2, 3), } vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) @@ -787,7 +787,7 @@ describe('vim.diagnostic', function() eq(2, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), - make_warning("Warning on Server 1", 1, 1, 2, 5), + make_warning("Warning on Server 1", 1, 1, 2, 3), }) return #vim.diagnostic.get(diagnostic_bufnr) @@ -798,9 +798,9 @@ describe('vim.diagnostic', function() eq({2, 3, 2}, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), - make_warning("Warning on Server 1", 1, 1, 2, 5), - make_info("Ignored information", 1, 1, 2, 5), - make_hint("Here's a hint", 1, 1, 2, 5), + make_warning("Warning on Server 1", 1, 1, 2, 3), + make_info("Ignored information", 1, 1, 2, 3), + make_hint("Here's a hint", 1, 1, 2, 3), }) return { @@ -820,8 +820,8 @@ describe('vim.diagnostic', function() eq(1, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), - make_warning("Warning on Server 1", 1, 1, 2, 5), - make_info("Ignored information", 1, 1, 2, 5), + make_warning("Warning on Server 1", 1, 1, 2, 3), + make_info("Ignored information", 1, 1, 2, 3), make_error("Error On Other Line", 2, 1, 1, 5), }) @@ -1119,6 +1119,11 @@ describe('vim.diagnostic', function() end) describe('set()', function() + it('validates its arguments', function() + matches("expected a list of diagnostics", + pcall_err(exec_lua, [[vim.diagnostic.set(1, 0, {lnum = 1, col = 2})]])) + end) + it('can perform updates after insert_leave', function() exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] nvim("input", "o") diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua new file mode 100644 index 0000000000..729ebc601b --- /dev/null +++ b/test/functional/lua/filetype_spec.lua @@ -0,0 +1,98 @@ +local helpers = require('test.functional.helpers')(after_each) +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local clear = helpers.clear +local pathroot = helpers.pathroot + +local root = pathroot() + +describe('vim.filetype', function() + before_each(function() + clear() + + exec_lua [[ + local bufnr = vim.api.nvim_create_buf(true, false) + vim.api.nvim_set_current_buf(bufnr) + ]] + end) + + it('works with extensions', function() + eq('radicalscript', exec_lua [[ + vim.filetype.add({ + extension = { + rs = 'radicalscript', + }, + }) + vim.filetype.match('main.rs') + return vim.bo.filetype + ]]) + end) + + it('prioritizes filenames over extensions', function() + eq('somethingelse', exec_lua [[ + vim.filetype.add({ + extension = { + rs = 'radicalscript', + }, + filename = { + ['main.rs'] = 'somethingelse', + }, + }) + vim.filetype.match('main.rs') + return vim.bo.filetype + ]]) + end) + + it('works with filenames', function() + eq('nim', exec_lua [[ + vim.filetype.add({ + filename = { + ['s_O_m_e_F_i_l_e'] = 'nim', + }, + }) + vim.filetype.match('s_O_m_e_F_i_l_e') + return vim.bo.filetype + ]]) + + eq('dosini', exec_lua([[ + local root = ... + vim.filetype.add({ + filename = { + ['config'] = 'toml', + [root .. '/.config/fun/config'] = 'dosini', + }, + }) + vim.filetype.match(root .. '/.config/fun/config') + return vim.bo.filetype + ]], root)) + end) + + it('works with patterns', function() + eq('markdown', exec_lua([[ + local root = ... + vim.filetype.add({ + pattern = { + ['~/blog/.*%.txt'] = 'markdown', + } + }) + vim.filetype.match('~/blog/why_neovim_is_awesome.txt') + return vim.bo.filetype + ]], root)) + end) + + it('works with functions', function() + eq('foss', exec_lua [[ + vim.filetype.add({ + pattern = { + ["relevant_to_(%a+)"] = function(path, bufnr, capture) + if capture == "me" then + return "foss" + end + end, + } + }) + vim.filetype.match('relevant_to_me') + return vim.bo.filetype + ]]) + end) +end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index c543dd1995..1322155595 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -532,6 +532,7 @@ describe('v:lua', function() command('set pp+=test/functional/fixtures') eq('\tbadval', eval("v:lua.require'leftpad'('badval')")) eq(9003, eval("v:lua.require'bar'.doit()")) + eq(9004, eval("v:lua.require'baz-quux'.doit()")) end) it('throw errors for invalid use', function() diff --git a/test/functional/lua/spell_spec.lua b/test/functional/lua/spell_spec.lua new file mode 100644 index 0000000000..7e831f16a7 --- /dev/null +++ b/test/functional/lua/spell_spec.lua @@ -0,0 +1,53 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local pcall_err = helpers.pcall_err + +describe('vim.spell', function() + before_each(function() + clear() + end) + + describe('.check', function() + local check = function(x, exp) + return eq(exp, exec_lua("return vim.spell.check(...)", x)) + end + + it('can handle nil', function() + eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'check' (expected string)]], + pcall_err(exec_lua, [[vim.spell.check(nil)]])) + end) + + it('can check spellings', function() + check('hello', {}) + + check( + 'helloi', + {{"helloi", "bad", 1}} + ) + + check( + 'hello therei', + {{"therei", "bad", 7}} + ) + + check( + 'hello. there', + {{"there", "caps", 8}} + ) + + check( + 'neovim cna chkc spellins. okay?', + { + {"neovim" , "bad" , 1}, + {"cna" , "bad" , 8}, + {"chkc" , "bad" , 12}, + {"spellins", "bad" , 17}, + {"okay" , "caps", 27} + } + ) + end) + + end) +end) diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index dbfbe2dbfe..4635f17557 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -101,7 +101,7 @@ describe('URI methods', function() eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case)) end) - it('file path includes only ascii charactors with encoded colon character', function() + it('file path includes only ascii characters with encoded colon character', function() local test_case = [[ local uri = 'file:///C%3A/Foo/Bar/Baz.txt' return vim.uri_to_fname(uri) @@ -155,6 +155,12 @@ describe('URI methods', function() return pcall(vim.uri_to_fname, 'not_an_uri.txt') ]]) end) + + it('uri_to_fname should not treat comma as a scheme character', function() + eq(false, exec_lua [[ + return pcall(vim.uri_to_fname, 'foo,://bar') + ]]) + end) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 317f92fcdc..ddcf7687f5 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -906,6 +906,7 @@ describe('lua stdlib', function() exec_lua("vim.validate{arg1={nil, 'thread', true }}") 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'} }}") matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) @@ -935,6 +936,8 @@ describe('lua stdlib', function() 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, {'n', 's'} }}")) -- Pass an additional message back. matches('arg1: expected %?, got 3. Info: TEST_MSG', @@ -984,6 +987,38 @@ describe('lua stdlib', function() matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.g[0].testing')) + + exec_lua [[ + local counter = 0 + vim.g.AddCounter = function() counter = counter + 1 end + vim.g.GetCounter = function() return counter end + ]] + + eq(0, eval('g:GetCounter()')) + eval('g:AddCounter()') + eq(1, eval('g:GetCounter()')) + eval('g:AddCounter()') + eq(2, eval('g:GetCounter()')) + exec_lua([[vim.g.AddCounter()]]) + eq(3, exec_lua([[return vim.g.GetCounter()]])) + exec_lua([[vim.api.nvim_get_var('AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_get_var('GetCounter')()]])) + + exec_lua [[ + local counter = 0 + vim.api.nvim_set_var('AddCounter', function() counter = counter + 1 end) + vim.api.nvim_set_var('GetCounter', function() return counter end) + ]] + + eq(0, eval('g:GetCounter()')) + eval('g:AddCounter()') + eq(1, eval('g:GetCounter()')) + eval('g:AddCounter()') + eq(2, eval('g:GetCounter()')) + exec_lua([[vim.g.AddCounter()]]) + eq(3, exec_lua([[return vim.g.GetCounter()]])) + exec_lua([[vim.api.nvim_get_var('AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_get_var('GetCounter')()]])) end) it('vim.b', function() @@ -1019,6 +1054,38 @@ describe('lua stdlib', function() eq(NIL, funcs.luaeval "vim.b.to_delete") exec_lua [[ + local counter = 0 + vim.b.AddCounter = function() counter = counter + 1 end + vim.b.GetCounter = function() return counter end + ]] + + eq(0, eval('b:GetCounter()')) + eval('b:AddCounter()') + eq(1, eval('b:GetCounter()')) + eval('b:AddCounter()') + eq(2, eval('b:GetCounter()')) + exec_lua([[vim.b.AddCounter()]]) + eq(3, exec_lua([[return vim.b.GetCounter()]])) + exec_lua([[vim.api.nvim_buf_get_var(0, 'AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_buf_get_var(0, 'GetCounter')()]])) + + exec_lua [[ + local counter = 0 + vim.api.nvim_buf_set_var(0, 'AddCounter', function() counter = counter + 1 end) + vim.api.nvim_buf_set_var(0, 'GetCounter', function() return counter end) + ]] + + eq(0, eval('b:GetCounter()')) + eval('b:AddCounter()') + eq(1, eval('b:GetCounter()')) + eval('b:AddCounter()') + eq(2, eval('b:GetCounter()')) + exec_lua([[vim.b.AddCounter()]]) + eq(3, exec_lua([[return vim.b.GetCounter()]])) + exec_lua([[vim.api.nvim_buf_get_var(0, 'AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_buf_get_var(0, 'GetCounter')()]])) + + exec_lua [[ vim.cmd "vnew" ]] @@ -1056,6 +1123,38 @@ describe('lua stdlib', function() eq(NIL, funcs.luaeval "vim.w.to_delete") exec_lua [[ + local counter = 0 + vim.w.AddCounter = function() counter = counter + 1 end + vim.w.GetCounter = function() return counter end + ]] + + eq(0, eval('w:GetCounter()')) + eval('w:AddCounter()') + eq(1, eval('w:GetCounter()')) + eval('w:AddCounter()') + eq(2, eval('w:GetCounter()')) + exec_lua([[vim.w.AddCounter()]]) + eq(3, exec_lua([[return vim.w.GetCounter()]])) + exec_lua([[vim.api.nvim_win_get_var(0, 'AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_win_get_var(0, 'GetCounter')()]])) + + exec_lua [[ + local counter = 0 + vim.api.nvim_win_set_var(0, 'AddCounter', function() counter = counter + 1 end) + vim.api.nvim_win_set_var(0, 'GetCounter', function() return counter end) + ]] + + eq(0, eval('w:GetCounter()')) + eval('w:AddCounter()') + eq(1, eval('w:GetCounter()')) + eval('w:AddCounter()') + eq(2, eval('w:GetCounter()')) + exec_lua([[vim.w.AddCounter()]]) + eq(3, exec_lua([[return vim.w.GetCounter()]])) + exec_lua([[vim.api.nvim_win_get_var(0, 'AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_win_get_var(0, 'GetCounter')()]])) + + exec_lua [[ vim.cmd "vnew" ]] @@ -1088,6 +1187,38 @@ describe('lua stdlib', function() eq(NIL, funcs.luaeval "vim.t.to_delete") exec_lua [[ + local counter = 0 + vim.t.AddCounter = function() counter = counter + 1 end + vim.t.GetCounter = function() return counter end + ]] + + eq(0, eval('t:GetCounter()')) + eval('t:AddCounter()') + eq(1, eval('t:GetCounter()')) + eval('t:AddCounter()') + eq(2, eval('t:GetCounter()')) + exec_lua([[vim.t.AddCounter()]]) + eq(3, exec_lua([[return vim.t.GetCounter()]])) + exec_lua([[vim.api.nvim_tabpage_get_var(0, 'AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'GetCounter')()]])) + + exec_lua [[ + local counter = 0 + vim.api.nvim_tabpage_set_var(0, 'AddCounter', function() counter = counter + 1 end) + vim.api.nvim_tabpage_set_var(0, 'GetCounter', function() return counter end) + ]] + + eq(0, eval('t:GetCounter()')) + eval('t:AddCounter()') + eq(1, eval('t:GetCounter()')) + eval('t:AddCounter()') + eq(2, eval('t:GetCounter()')) + exec_lua([[vim.t.AddCounter()]]) + eq(3, exec_lua([[return vim.t.GetCounter()]])) + exec_lua([[vim.api.nvim_tabpage_get_var(0, 'AddCounter')()]]) + eq(4, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'GetCounter')()]])) + + exec_lua [[ vim.cmd "tabnew" ]] @@ -2200,6 +2331,40 @@ describe('lua stdlib', function() end) end) + it('vim.notify_once', function() + local screen = Screen.new(60,5) + screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {foreground=Screen.colors.Red}, + }) + screen:attach() + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + exec_lua [[vim.notify_once("I'll only tell you this once...", vim.log.levels.WARN)]] + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {1:I'll only tell you this once...} | + ]]} + feed('<C-l>') + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + exec_lua [[vim.notify_once("I'll only tell you this once...")]] + screen:expect_unchanged() + end) + describe('vim.schedule_wrap', function() it('preserves argument lists', function() exec_lua [[ @@ -2295,3 +2460,82 @@ describe('lua: require("mod") from packages', function() eq('I am fancy_z.lua', exec_lua [[ return require'fancy_z' ]]) end) end) + +describe('vim.keymap', function() + it('can make a mapping', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + end) + + it('can make an expr mapping', function() + exec_lua [[ + vim.keymap.set('n', 'aa', function() return ':lua SomeValue = 99<cr>' end, {expr = true}) + ]] + + feed('aa') + + eq(99, exec_lua[[return SomeValue]]) + end) + + it('can overwrite a mapping', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + + exec_lua [[ + vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount - 1 end) + ]] + + feed('asdf\n') + + eq(0, exec_lua[[return GlobalCount]]) + end) + + it('can unmap a mapping', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.keymap.set('n', 'asdf', function() GlobalCount = GlobalCount + 1 end) + return GlobalCount + ]]) + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + + exec_lua [[ + vim.keymap.del('n', 'asdf') + ]] + + feed('asdf\n') + + eq(1, exec_lua[[return GlobalCount]]) + eq('\nNo mapping found', helpers.exec_capture('nmap asdf')) + end) + + it('can do <Plug> mappings', function() + eq(0, exec_lua [[ + GlobalCount = 0 + vim.keymap.set('n', '<plug>(asdf)', function() GlobalCount = GlobalCount + 1 end) + vim.keymap.set('n', 'ww', '<plug>(asdf)') + return GlobalCount + ]]) + + feed('ww\n') + + eq(1, exec_lua[[return GlobalCount]]) + end) + +end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 1af31c38f8..eab520948f 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -73,8 +73,11 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options) on_init = function(client, result) TEST_RPC_CLIENT = client vim.rpcrequest(1, "init", result) - client.config.flags.allow_incremental_sync = options.allow_incremental_sync or false end; + flags = { + allow_incremental_sync = options.allow_incremental_sync or false; + debounce_text_changes = options.debounce_text_changes or 0; + }; on_exit = function(...) vim.rpcnotify(1, "exit", ...) end; @@ -926,7 +929,60 @@ describe('LSP', function() local client test_rpc_server { test_name = "basic_check_buffer_open_and_change_incremental"; - options = { allow_incremental_sync = true }; + options = { + allow_incremental_sync = true, + }; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental") + eq(sync_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().text_document_open_close) + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_handler = function(err, result, ctx) + if ctx.method == 'start' then + exec_lua [[ + vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { + "123boop"; + }) + ]] + client.notify('finish') + end + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then + client.stop() + end + end; + } + end) + it('should check the body and didChange incremental with debounce', function() + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; + } + local client + test_rpc_server { + test_name = "basic_check_buffer_open_and_change_incremental"; + options = { + allow_incremental_sync = true, + debounce_text_changes = 5 + }; on_setup = function() exec_lua [[ BUFFER = vim.api.nvim_create_buf(false, true) @@ -1235,7 +1291,7 @@ describe('LSP', function() make_edit(2, 0, 2, 2, {"3"}); make_edit(3, 2, 3, 4, {""}); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ '123First line of text'; '2econd line of text'; @@ -1255,7 +1311,7 @@ describe('LSP', function() make_edit(3, #'', 3, #"Fourth", {"another line of text", "before this"}); make_edit(3, #'Fourth', 3, #"Fourth line of text", {"!"}); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ ''; '123'; @@ -1279,7 +1335,7 @@ describe('LSP', function() make_edit(3, #"Fourth", 3, #'', {"another line of text", "before this"}); make_edit(3, #"Fourth line of text", 3, #'Fourth', {"!"}); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ ''; '123'; @@ -1296,7 +1352,7 @@ describe('LSP', function() local edits = { make_edit(4, 3, 4, 4, {"ä"}); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; 'Second line of text'; @@ -1309,7 +1365,7 @@ describe('LSP', function() local edits = { make_edit(5, 0, 5, 0, "foobar"); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; 'Second line of text'; @@ -1319,6 +1375,20 @@ describe('LSP', function() 'foobar'; }, buf_lines(1)) end) + it('applies multiple text edits at the end of the document', function() + local edits = { + make_edit(4, 0, 5, 0, ""); + make_edit(5, 0, 5, 0, "foobar"); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") + eq({ + 'First line of text'; + 'Second line of text'; + 'Third line of text'; + 'Fourth line of text'; + 'foobar'; + }, buf_lines(1)) + end) describe('cursor position', function() it('don\'t fix the cursor if the range contains the cursor', function() @@ -1326,7 +1396,7 @@ describe('LSP', function() local edits = { make_edit(1, 0, 1, 19, 'Second line of text') } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; 'Second line of text'; @@ -1343,7 +1413,7 @@ describe('LSP', function() make_edit(1, 0, 1, 6, ''), make_edit(1, 6, 1, 19, '') } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; ''; @@ -1360,7 +1430,7 @@ describe('LSP', function() make_edit(1, 0, 1, 6, ''), make_edit(0, 18, 5, 0, '') } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; }, buf_lines(1)) @@ -1372,7 +1442,7 @@ describe('LSP', function() local edits = { make_edit(1, 0, 2, 0, '') } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; 'Third line of text'; @@ -1387,7 +1457,7 @@ describe('LSP', function() local edits = { make_edit(1, 7, 1, 11, '') } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; 'Second of text'; @@ -1403,7 +1473,7 @@ describe('LSP', function() local edits = { make_edit(0, 11, 1, 12, '') } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({ 'First line of text'; 'Third line of text'; @@ -1419,21 +1489,21 @@ describe('LSP', function() local edits = { make_edit(0, 0, 5, 0, {"All replaced"}); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({'All replaced'}, buf_lines(1)) end) it('applies edits when the end line is 2 larger than vim\'s', function() local edits = { make_edit(0, 0, 6, 0, {"All replaced"}); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({'All replaced'}, buf_lines(1)) end) it('applies edits with a column offset', function() local edits = { make_edit(0, 0, 5, 2, {"All replaced"}); } - exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-16") eq({'All replaced'}, buf_lines(1)) end) end) @@ -1461,7 +1531,7 @@ describe('LSP', function() ]] end) it('correctly goes ahead with the edit if all is normal', function() - exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(5)) + exec_lua("vim.lsp.util.apply_text_document_edit(..., nil, 'utf-16')", text_document_edit(5)) eq({ 'First ↥ 🤦 🦄 line of text'; '2nd line of 语text'; @@ -1473,7 +1543,7 @@ describe('LSP', function() local bufnr = select(1, ...) local text_edit = select(2, ...) vim.lsp.util.buf_versions[bufnr] = 10 - vim.lsp.util.apply_text_document_edit(text_edit) + vim.lsp.util.apply_text_document_edit(text_edit, nil, 'utf-16') ]], target_bufnr, text_document_edit(0)) eq({ 'First ↥ 🤦 🦄 line of text'; @@ -1486,7 +1556,7 @@ describe('LSP', function() local args = {...} local versionedBuf = args[2] vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion - vim.lsp.util.apply_text_document_edit(args[1]) + vim.lsp.util.apply_text_document_edit(args[1], nil, 'utf-16') ]], edit, versionedBuf) end @@ -1512,17 +1582,36 @@ describe('LSP', function() describe('workspace_apply_edit', function() it('workspace/applyEdit returns ApplyWorkspaceEditResponse', function() - local expected = { - applied = true; - failureReason = nil; + local expected_handlers = { + {NIL, {}, {method="test", client_id=1}}; + } + test_rpc_server { + test_name = "basic_init"; + on_init = function(client, _) + client.stop() + end; + -- If the program timed out, then code will be nil. + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + -- Note that NIL must be used here. + -- on_handler(err, method, result, client_id) + on_handler = function(...) + local expected = { + applied = true; + failureReason = nil; + } + eq(expected, exec_lua [[ + local apply_edit = { + label = nil; + edit = {}; + } + return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit, {client_id = TEST_RPC_CLIENT_ID}) + ]]) + eq(table.remove(expected_handlers), {...}) + end; } - eq(expected, exec_lua [[ - local apply_edit = { - label = nil; - edit = {}; - } - return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit) - ]]) end) end) @@ -1596,7 +1685,7 @@ describe('LSP', function() local workspace_edits = args[1] local target_bufnr = args[2] - vim.lsp.util.apply_workspace_edit(workspace_edits) + vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16') return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false) ]], make_workspace_edit(edits), target_bufnr)) @@ -1618,7 +1707,7 @@ describe('LSP', function() local workspace_edits = args[1] local target_bufnr = args[2] - vim.lsp.util.apply_workspace_edit(workspace_edits) + vim.lsp.util.apply_workspace_edit(workspace_edits, 'utf-16') return vim.api.nvim_buf_get_lines(target_bufnr, 0, -1, false) ]], make_workspace_edit(edits), target_bufnr)) @@ -1635,7 +1724,7 @@ describe('LSP', function() }, } } - exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit) + exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile)) end) it('createFile does not touch file if it exists and ignoreIfExists is set', function() @@ -1653,7 +1742,7 @@ describe('LSP', function() }, } } - exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit) + exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile)) eq('Dummy content', read_file(tmpfile)) end) @@ -1673,7 +1762,7 @@ describe('LSP', function() }, } } - exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit) + exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile)) eq('', read_file(tmpfile)) end) @@ -1694,7 +1783,7 @@ describe('LSP', function() } } } - eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit)) + eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')) eq(false, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile)) eq(false, exec_lua('return vim.api.nvim_buf_is_loaded(vim.fn.bufadd(...))', tmpfile)) end) @@ -1855,7 +1944,7 @@ describe('LSP', function() } }, } - return vim.lsp.util.locations_to_items(locations) + return vim.lsp.util.locations_to_items(locations, 'utf-16') ]] eq(expected, actual) end) @@ -1885,7 +1974,7 @@ describe('LSP', function() } }, } - return vim.lsp.util.locations_to_items(locations) + return vim.lsp.util.locations_to_items(locations, 'utf-16') ]] eq(expected, actual) end) @@ -2189,7 +2278,7 @@ describe('LSP', function() end local jump = function(msg) - eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg)) + eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg, "utf-16")) eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]]) return { line = exec_lua[[return vim.fn.line('.')]], @@ -2263,6 +2352,27 @@ describe('LSP', function() end) end) + describe('lsp.util.convert_signature_help_to_markdown_lines', function() + it('can handle negative activeSignature', function() + local result = exec_lua[[ + local signature_help = { + activeParameter = 0, + activeSignature = -1, + signatures = { + { + documentation = "", + label = "TestEntity.TestEntity()", + parameters = {} + }, + } + } + return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'cs', {','}) + ]] + local expected = {'```cs', 'TestEntity.TestEntity()', '```', ''} + eq(expected, result) + end) + end) + describe('lsp.util.get_effective_tabstop', function() local function test_tabstop(tabsize, softtabstop) exec_lua(string.format([[ diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index d100db8de2..603f4d91b9 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -8,6 +8,7 @@ local source = helpers.source local missing_provider = helpers.missing_provider local matches = helpers.matches local pcall_err = helpers.pcall_err +local funcs = helpers.funcs do clear() @@ -93,16 +94,40 @@ describe('python3 provider', function() ghi]]) end) - it('py3eval', function() - eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]])) + describe('py3eval()', function() + it('works', function() + eq({1, 2, {['key'] = 'val'}}, funcs.py3eval('[1, 2, {"key": "val"}]')) + end) + + it('errors out when given non-string', function() + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(10)')) + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:_null_dict)')) + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:_null_list)')) + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(0.0)')) + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(function("tr"))')) + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:true)')) + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:false)')) + eq('Vim:E474: Invalid argument', pcall_err(eval, 'py3eval(v:null)')) + end) + + it('accepts NULL string', function() + matches('.*SyntaxError.*', pcall_err(eval, 'py3eval($XXX_NONEXISTENT_VAR_XXX)')) + end) end) it('pyxeval #10758', function() - eq(0, eval([[&pyxversion]])) + eq(3, eval([[&pyxversion]])) eq(3, eval([[pyxeval('sys.version_info[:3][0]')]])) eq(3, eval([[&pyxversion]])) end) + it("setting 'pyxversion'", function() + command 'set pyxversion=3' -- no error + eq('Vim(set):E474: Invalid argument: pyxversion=2', pcall_err(command, 'set pyxversion=2')) + command 'set pyxversion=0' -- allowed, but equivalent to pyxversion=3 + eq(3, eval'&pyxversion') + end) + it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function() helpers.add_builddir_to_rtp() source([=[ @@ -120,3 +145,15 @@ describe('python3 provider', function() assert_alive() end) end) + +describe('python2 feature test', function() + -- python2 is not supported, so correct behaviour is to return 0 + it('works', function() + eq(0, funcs.has('python2')) + eq(0, funcs.has('python')) + eq(0, funcs.has('python_compiled')) + eq(0, funcs.has('python_dynamic')) + eq(0, funcs.has('python_dynamic_')) + eq(0, funcs.has('python_')) + end) +end) diff --git a/test/functional/provider/python_spec.lua b/test/functional/provider/python_spec.lua deleted file mode 100644 index d60d8d1001..0000000000 --- a/test/functional/provider/python_spec.lua +++ /dev/null @@ -1,123 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local neq = helpers.neq -local feed = helpers.feed -local clear = helpers.clear -local funcs = helpers.funcs -local meths = helpers.meths -local insert = helpers.insert -local expect = helpers.expect -local command = helpers.command -local exc_exec = helpers.exc_exec -local write_file = helpers.write_file -local curbufmeths = helpers.curbufmeths -local missing_provider = helpers.missing_provider -local matches = helpers.matches -local pcall_err = helpers.pcall_err - -do - clear() - local reason = missing_provider('python') - if reason then - it(':python reports E319 if provider is missing', function() - local expected = [[Vim%(py.*%):E319: No "python" provider found.*]] - matches(expected, pcall_err(command, 'py print("foo")')) - matches(expected, pcall_err(command, 'pyfile foo')) - end) - pending(string.format('Python 2 (or the pynvim module) is broken/missing (%s)', reason), function() end) - return - end -end - -before_each(function() - clear() - command('python import vim') -end) - -describe('python feature test', function() - it('works', function() - eq(1, funcs.has('python')) - eq(1, funcs.has('python_compiled')) - eq(1, funcs.has('python_dynamic')) - eq(0, funcs.has('python_dynamic_')) - eq(0, funcs.has('python_')) - end) -end) - -describe(':python command', function() - it('works with a line', function() - command('python vim.vars["set_by_python"] = [100, 0]') - eq({100, 0}, meths.get_var('set_by_python')) - end) - - -- TODO(ZyX-I): works with << EOF - -- TODO(ZyX-I): works with execute 'python' line1."\n".line2."\n"… - - it('supports nesting', function() - command([[python vim.command('python vim.command("python vim.command(\'let set_by_nested_python = 555\')")')]]) - eq(555, meths.get_var('set_by_nested_python')) - end) - - it('supports range', function() - insert([[ - line1 - line2 - line3 - line4]]) - feed('ggjvj:python vim.vars["range"] = vim.current.range[:]<CR>') - eq({'line2', 'line3'}, meths.get_var('range')) - end) -end) - -describe(':pyfile command', function() - it('works', function() - local fname = 'pyfile.py' - write_file(fname, 'vim.command("let set_by_pyfile = 123")') - command('pyfile pyfile.py') - eq(123, meths.get_var('set_by_pyfile')) - os.remove(fname) - end) -end) - -describe(':pydo command', function() - it('works', function() - -- :pydo 42 returns None for all lines, - -- the buffer should not be changed - command('normal :pydo 42') - eq(false, curbufmeths.get_option('modified')) - -- insert some text - insert('abc\ndef\nghi') - expect([[ - abc - def - ghi]]) - -- go to top and select and replace the first two lines - feed('ggvj:pydo return str(linenr)<CR>') - expect([[ - 1 - 2 - ghi]]) - end) -end) - -describe('pyeval()', function() - it('works', function() - eq({1, 2, {['key'] = 'val'}}, funcs.pyeval('[1, 2, {"key": "val"}]')) - end) - - it('errors out when given non-string', function() - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(10)')) - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:_null_dict)')) - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:_null_list)')) - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(0.0)')) - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(function("tr"))')) - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:true)')) - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:false)')) - eq('Vim(call):E474: Invalid argument', exc_exec('call pyeval(v:null)')) - end) - - it('accepts NULL string', function() - neq(0, exc_exec('call pyeval($XXX_NONEXISTENT_VAR_XXX)')) - end) -end) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index f25cfa2039..beb43e0271 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -292,10 +292,9 @@ describe(':terminal buffer', function() command('quit') end) - it('does not segfault when pasting empty buffer #13955', function() - feed_command('terminal') + it('does not segfault when pasting empty register #13955', function() feed('<c-\\><c-n>') - feed_command('put a') -- buffer a is empty + feed_command('put a') -- register a is empty helpers.assert_alive() end) end) @@ -350,7 +349,7 @@ describe('on_lines does not emit out-of-bounds line indexes when', function() end) it('creating a terminal buffer #16394', function() - feed_command([[autocmd TermOpen * ++once call v:lua.register_callback(expand("<abuf>"))]]) + feed_command('autocmd TermOpen * ++once call v:lua.register_callback(str2nr(expand("<abuf>")))') feed_command('terminal') sleep(500) eq('', exec_lua([[return _G.cb_error]])) diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index 3d8441b93c..6e2c851df7 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval -local feed, nvim = helpers.feed, helpers.nvim +local feed, nvim, command = helpers.feed, helpers.nvim, helpers.command local feed_data = thelpers.feed_data describe(':terminal mouse', function() @@ -10,9 +10,9 @@ describe(':terminal mouse', function() before_each(function() clear() nvim('set_option', 'statusline', '==========') - nvim('command', 'highlight StatusLine cterm=NONE') - nvim('command', 'highlight StatusLineNC cterm=NONE') - nvim('command', 'highlight VertSplit cterm=NONE') + command('highlight StatusLine cterm=NONE') + command('highlight StatusLineNC cterm=NONE') + command('highlight VertSplit cterm=NONE') screen = thelpers.screen_setup() local lines = {} for i = 1, 30 do @@ -38,6 +38,26 @@ describe(':terminal mouse', function() eq('nt', eval('mode(1)')) end) + it('will exit focus and trigger Normal mode mapping on mouse click', function() + command('let g:got_leftmouse = 0') + command('nnoremap <LeftMouse> <Cmd>let g:got_leftmouse = 1<CR>') + eq('t', eval('mode(1)')) + eq(0, eval('g:got_leftmouse')) + feed('<LeftMouse>') + eq('nt', eval('mode(1)')) + eq(1, eval('g:got_leftmouse')) + end) + + it('will exit focus and trigger Normal mode mapping on mouse click with modifier', function() + command('let g:got_ctrl_leftmouse = 0') + command('nnoremap <C-LeftMouse> <Cmd>let g:got_ctrl_leftmouse = 1<CR>') + eq('t', eval('mode(1)')) + eq(0, eval('g:got_ctrl_leftmouse')) + feed('<C-LeftMouse>') + eq('nt', eval('mode(1)')) + eq(1, eval('g:got_ctrl_leftmouse')) + end) + it('will exit focus on <C-\\> + mouse-scroll', function() eq('t', eval('mode(1)')) feed('<C-\\>') @@ -45,6 +65,12 @@ describe(':terminal mouse', function() eq('nt', eval('mode(1)')) end) + it('does not leave terminal mode on left-release', function() + if helpers.pending_win32(pending) then return end + feed('<LeftRelease>') + eq('t', eval('mode(1)')) + end) + describe('with mouse events enabled by the program', function() before_each(function() thelpers.enable_mouse() @@ -60,7 +86,7 @@ describe(':terminal mouse', function() ]]) end) - it('will forward mouse clicks to the program', function() + it('will forward mouse press, drag and release to the program', function() if helpers.pending_win32(pending) then return end feed('<LeftMouse><1,2>') screen:expect([[ @@ -72,6 +98,36 @@ describe(':terminal mouse', function() "#{1: } | {3:-- TERMINAL --} | ]]) + feed('<LeftDrag><2,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + @##{1: } | + {3:-- TERMINAL --} | + ]]) + feed('<LeftDrag><3,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + @$#{1: } | + {3:-- TERMINAL --} | + ]]) + feed('<LeftRelease><3,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + #$#{1: } | + {3:-- TERMINAL --} | + ]]) end) it('will forward mouse scroll to the program', function() @@ -88,9 +144,63 @@ describe(':terminal mouse', function() ]]) end) + it('dragging and scrolling do not interfere with each other', function() + if helpers.pending_win32(pending) then return end + feed('<LeftMouse><1,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + "#{1: } | + {3:-- TERMINAL --} | + ]]) + feed('<ScrollWheelUp><1,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + `"#{1: } | + {3:-- TERMINAL --} | + ]]) + feed('<LeftDrag><2,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + @##{1: } | + {3:-- TERMINAL --} | + ]]) + feed('<ScrollWheelUp><2,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + `##{1: } | + {3:-- TERMINAL --} | + ]]) + feed('<LeftRelease><2,2>') + screen:expect([[ + line27 | + line28 | + line29 | + line30 | + mouse enabled | + ###{1: } | + {3:-- TERMINAL --} | + ]]) + end) + it('will forward mouse clicks to the program with the correct even if set nu', function() if helpers.pending_win32(pending) then return end - nvim('command', 'set number') + command('set number') -- When the display area such as a number is clicked, it returns to the -- normal mode. feed('<LeftMouse><3,0>') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index bf57b135cb..faf44fa01d 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -20,7 +20,6 @@ local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set local ok = helpers.ok local read_file = helpers.read_file -local exec_lua = helpers.exec_lua if helpers.pending_win32(pending) then return end @@ -581,34 +580,21 @@ describe('TUI', function() end) it("paste: 'nomodifiable' buffer", function() - local has_luajit = exec_lua('return jit ~= nil') child_session:request('nvim_command', 'set nomodifiable') child_session:request('nvim_exec_lua', [[ - -- Stack traces for this test are non-deterministic, so disable them - _G.debug.traceback = function(msg) return msg end + -- Truncate the error message to hide the line number + _G.debug.traceback = function(msg) return msg:sub(-49) end ]], {}) feed_data('\027[200~fail 1\nfail 2\n\027[201~') - if has_luajit then - screen:expect{grid=[[ - | - {4:~ }| - {5: }| - {8:paste: Error executing lua: vim.lua:0: Vim:E21: Ca}| - {8:nnot make changes, 'modifiable' is off} | - {10:Press ENTER or type command to continue}{1: } | - {3:-- TERMINAL --} | - ]]} - else - screen:expect{grid=[[ - | - {4:~ }| - {5: }| - {8:paste: Error executing lua: Vim:E21: Cannot make c}| - {8:hanges, 'modifiable' is off} | - {10:Press ENTER or type command to continue}{1: } | - {3:-- TERMINAL --} | - ]]} - end + screen:expect{grid=[[ + | + {4:~ }| + {5: }| + {8:paste: Error executing lua: Vim:E21: Cannot make c}| + {8:hanges, 'modifiable' is off} | + {10:Press ENTER or type command to continue}{1: } | + {3:-- TERMINAL --} | + ]]} feed_data('\n') -- <Enter> child_session:request('nvim_command', 'set modifiable') feed_data('\027[200~success 1\nsuccess 2\n\027[201~') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 911fa017ab..396fe5feab 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -167,6 +167,27 @@ void ui_refresh(void) eq('variable', ret) end) + it("supports caching queries", function() + local long_query = query:rep(100) + local first_run = exec_lua ([[ + local before = vim.loop.hrtime() + cquery = vim.treesitter.parse_query("c", ...) + local after = vim.loop.hrtime() + return after - before + ]], long_query) + + local subsequent_runs = exec_lua ([[ + local before = vim.loop.hrtime() + for i=1,100,1 do + cquery = vim.treesitter.parse_query("c", ...) + end + local after = vim.loop.hrtime() + return after - before + ]], long_query) + + assert.True(1000 * subsequent_runs < first_run) + end) + it('support query and iter by capture', function() insert(test_text) @@ -645,6 +666,21 @@ int x = INT_MAX; -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) }, get_ranges()) end) + + it("should not inject bad languages", function() + if helpers.pending_win32(pending) then return end + exec_lua([=[ + vim.treesitter.add_directive("inject-bad!", function(match, _, _, pred, metadata) + metadata.language = "{" + metadata.combined = true + metadata.content = pred[2] + end) + + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = "(preproc_function_def value: ((preproc_arg) @_a (#inject-bad! @_a)))"}}) + ]=]) + end) end) describe("when using the offset directive", function() diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index 16ed3b9486..9f84b71ece 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -755,12 +755,26 @@ describe('Buffer highlighting', function() -- TODO: only a virtual text from the same ns curretly overrides -- an existing virtual text. We might add a prioritation system. set_virtual_text(id1, 0, s1, {}) - eq({{1, 0, 0, { priority = 0, virt_text = s1}}}, get_extmarks(id1, {0,0}, {0, -1}, {details=true})) + eq({{1, 0, 0, { + priority = 0, + virt_text = s1, + -- other details + right_gravity = true, + virt_text_pos = 'eol', + virt_text_hide = false, + }}}, get_extmarks(id1, {0,0}, {0, -1}, {details=true})) -- TODO: is this really valid? shouldn't the max be line_count()-1? local lastline = line_count() set_virtual_text(id1, line_count(), s2, {}) - eq({{3, lastline, 0, { priority = 0, virt_text = s2}}}, get_extmarks(id1, {lastline,0}, {lastline, -1}, {details=true})) + eq({{3, lastline, 0, { + priority = 0, + virt_text = s2, + -- other details + right_gravity = true, + virt_text_pos = 'eol', + virt_text_hide = false, + }}}, get_extmarks(id1, {lastline,0}, {lastline, -1}, {details=true})) eq({}, get_extmarks(id1, {lastline+9000,0}, {lastline+9000, -1}, {})) end) diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index ea8968a653..11718a6e18 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -114,11 +114,30 @@ describe('mappings', function() end) end) -describe('input utf sequences that contain CSI/K_SPECIAL', function() +describe('input utf sequences that contain K_SPECIAL (0x80)', function() it('ok', function() feed('i…<esc>') expect('…') end) + + it('can be mapped', function() + command('inoremap … E280A6') + feed('i…<esc>') + expect('E280A6') + end) +end) + +describe('input utf sequences that contain CSI (0x9B)', function() + it('ok', function() + feed('iě<esc>') + expect('ě') + end) + + it('can be mapped', function() + command('inoremap ě C49B') + feed('iě<esc>') + expect('C49B') + end) end) describe('input non-printable chars', function() diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index aeba049557..c44e59cfd3 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -10,6 +10,7 @@ local funcs = helpers.funcs local get_pathsep = helpers.get_pathsep local eq = helpers.eq local pcall_err = helpers.pcall_err +local exec_lua = helpers.exec_lua describe('ui/ext_popupmenu', function() local screen @@ -25,6 +26,7 @@ describe('ui/ext_popupmenu', function() [5] = {bold = true, foreground = Screen.colors.SeaGreen}, [6] = {background = Screen.colors.WebGray}, [7] = {background = Screen.colors.LightMagenta}, + [8] = {foreground = Screen.colors.Red}, }) source([[ function! TestComplete() abort @@ -369,6 +371,111 @@ describe('ui/ext_popupmenu', function() {1:~ }| {2:-- INSERT --} | ]]) + + command('iunmap <f1>') + command('iunmap <f2>') + command('iunmap <f3>') + exec_lua([[ + vim.keymap.set('i', '<f1>', function() vim.api.nvim_select_popupmenu_item(2, true, false, {}) end) + vim.keymap.set('i', '<f2>', function() vim.api.nvim_select_popupmenu_item(-1, false, false, {}) end) + vim.keymap.set('i', '<f3>', function() vim.api.nvim_select_popupmenu_item(1, false, true, {}) end) + ]]) + feed('<C-r>=TestComplete()<CR>') + screen:expect([[ + | + foo^ | + {6:fo x the foo }{1: }| + {7:bar }{1: }| + {7:spam }{1: }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + feed('<f1>') + screen:expect([[ + | + spam^ | + {7:fo x the foo }{1: }| + {7:bar }{1: }| + {6:spam }{1: }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + feed('<f2>') + screen:expect([[ + | + spam^ | + {7:fo x the foo }{1: }| + {7:bar }{1: }| + {7:spam }{1: }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + feed('<f3>') + screen:expect([[ + | + bar^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + + feed('<esc>ddiaa bb cc<cr>') + feed('<c-x><c-n>') + screen:expect([[ + aa bb cc | + aa^ | + {6:aa }{1: }| + {7:bb }{1: }| + {7:cc }{1: }| + {1:~ }| + {1:~ }| + {2:-- Keyword Local completion (^N^P) }{5:match 1 of 3} | + ]]) + + feed('<f1>') + screen:expect([[ + aa bb cc | + cc^ | + {7:aa }{1: }| + {7:bb }{1: }| + {6:cc }{1: }| + {1:~ }| + {1:~ }| + {2:-- Keyword Local completion (^N^P) }{5:match 3 of 3} | + ]]) + + feed('<f2>') + screen:expect([[ + aa bb cc | + cc^ | + {7:aa }{1: }| + {7:bb }{1: }| + {7:cc }{1: }| + {1:~ }| + {1:~ }| + {2:-- Keyword Local completion (^N^P) }{8:Back at original} | + ]]) + + feed('<f3>') + screen:expect([[ + aa bb cc | + bb^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) end) local function source_complete_month() @@ -2213,4 +2320,175 @@ describe('builtin popupmenu', function() feed('<c-y>') assert_alive() end) + + it('is closed by :stopinsert from timer #12976', function() + screen:try_resize(32,14) + command([[call setline(1, ['hello', 'hullo', 'heeee', ''])]]) + feed('Gah<C-N>') + screen:expect([[ + hello | + hullo | + heeee | + hello^ | + {s:hello }{1: }| + {n:hullo }{1: }| + {n:heeee }{1: }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- }{5:match 1 of 3} | + ]]) + command([[call timer_start(100, { -> execute('stopinsert') })]]) + helpers.sleep(200) + feed('k') -- cursor should move up in Normal mode + screen:expect([[ + hello | + hullo | + heee^e | + hello | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) + + it('truncates double-width character correctly when there is no scrollbar', function() + screen:try_resize(32,8) + command('set completeopt+=menuone,noselect') + feed('i' .. string.rep(' ', 13)) + funcs.complete(14, {'哦哦哦哦哦哦哦哦哦哦'}) + screen:expect([[ + ^ | + {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + end) + + it('truncates double-width character correctly when there is scrollbar', function() + screen:try_resize(32,8) + command('set completeopt+=noselect') + command('set pumheight=4') + feed('i' .. string.rep(' ', 12)) + local items = {} + for _ = 1, 8 do + table.insert(items, {word = '哦哦哦哦哦哦哦哦哦哦', equal = 1, dup = 1}) + end + funcs.complete(13, items) + screen:expect([[ + ^ | + {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{c: }| + {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{c: }| + {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{s: }| + {1:~ }{n: 哦哦哦哦哦哦哦哦哦>}{s: }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]) + end) +end) + +describe('builtin popupmenu with ui/ext_multigrid', function() + local screen + before_each(function() + clear() + screen = Screen.new(32, 20) + screen:attach({ext_multigrid=true}) + screen:set_default_attr_ids({ + -- popup selected item / scrollbar track + ['s'] = {background = Screen.colors.WebGray}, + -- popup non-selected item + ['n'] = {background = Screen.colors.LightMagenta}, + -- popup scrollbar knob + ['c'] = {background = Screen.colors.Grey0}, + [1] = {bold = true, foreground = Screen.colors.Blue}, + [2] = {bold = true}, + [3] = {reverse = true}, + [4] = {bold = true, reverse = true}, + [5] = {bold = true, foreground = Screen.colors.SeaGreen}, + [6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + }) + end) + + it('truncates double-width character correctly when there is no scrollbar', function() + screen:try_resize(32,8) + command('set completeopt+=menuone,noselect') + feed('i' .. string.rep(' ', 13)) + funcs.complete(14, {'哦哦哦哦哦哦哦哦哦哦'}) + screen:expect({grid=[[ + ## grid 1 + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [3:--------------------------------]| + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + {n: 哦哦哦哦哦哦哦哦哦>}| + ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 12, false, 100}}}) + end) + + it('truncates double-width character correctly when there is scrollbar', function() + screen:try_resize(32,8) + command('set completeopt+=noselect') + command('set pumheight=4') + feed('i' .. string.rep(' ', 12)) + local items = {} + for _ = 1, 8 do + table.insert(items, {word = '哦哦哦哦哦哦哦哦哦哦', equal = 1, dup = 1}) + end + funcs.complete(13, items) + screen:expect({grid=[[ + ## grid 1 + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [3:--------------------------------]| + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + {n: 哦哦哦哦哦哦哦哦哦>}{c: }| + {n: 哦哦哦哦哦哦哦哦哦>}{c: }| + {n: 哦哦哦哦哦哦哦哦哦>}{s: }| + {n: 哦哦哦哦哦哦哦哦哦>}{s: }| + ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 11, false, 100}}}) + end) end) diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 958e137f65..09bcbc2bbd 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -6,6 +6,7 @@ local insert = helpers.insert local eq = helpers.eq local eval = helpers.eval local iswin = helpers.iswin +local funcs, meths, exec_lua = helpers.funcs, helpers.meths, helpers.exec_lua describe('screen', function() local screen @@ -127,14 +128,67 @@ local function screen_tests(linegrid) end) it('has correct default title with named file', function() - local expected = (iswin() and 'myfile (C:\\mydir) - NVIM' - or 'myfile (/mydir) - NVIM') + local expected = (iswin() and 'myfile (C:\\mydir) - NVIM' or 'myfile (/mydir) - NVIM') command('set title') command(iswin() and 'file C:\\mydir\\myfile' or 'file /mydir/myfile') screen:expect(function() eq(expected, screen.title) end) end) + + describe('is not changed by', function() + local file1 = iswin() and 'C:\\mydir\\myfile1' or '/mydir/myfile1' + local file2 = iswin() and 'C:\\mydir\\myfile2' or '/mydir/myfile2' + local expected = (iswin() and 'myfile1 (C:\\mydir) - NVIM' or 'myfile1 (/mydir) - NVIM') + local buf2 + + before_each(function() + command('edit '..file1) + buf2 = funcs.bufadd(file2) + command('set title') + end) + + it('calling setbufvar() to set an option in a hidden buffer from i_CTRL-R', function() + command([[inoremap <F2> <C-R>=setbufvar(]]..buf2..[[, '&autoindent', 1) ? '' : ''<CR>]]) + feed('i<F2><Esc>') + command('redraw!') + screen:expect(function() + eq(expected, screen.title) + end) + end) + + it('an RPC call to nvim_buf_set_option in a hidden buffer', function() + meths.buf_set_option(buf2, 'autoindent', true) + command('redraw!') + screen:expect(function() + eq(expected, screen.title) + end) + end) + + it('a Lua callback calling nvim_buf_set_option in a hidden buffer', function() + exec_lua(string.format([[ + vim.schedule(function() + vim.api.nvim_buf_set_option(%d, 'autoindent', true) + end) + ]], buf2)) + command('redraw!') + screen:expect(function() + eq(expected, screen.title) + end) + end) + + it('a Lua callback calling nvim_buf_call in a hidden buffer', function() + exec_lua(string.format([[ + vim.schedule(function() + vim.api.nvim_buf_call(%d, function() end) + end) + ]], buf2)) + command('redraw!') + screen:expect(function() + eq(expected, screen.title) + end) + end) + end) end) describe(':set icon', function() diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua index e1459ab5b8..0c2ca8de78 100644 --- a/test/functional/vimscript/eval_spec.lua +++ b/test/functional/vimscript/eval_spec.lua @@ -5,7 +5,7 @@ -- null_spec.lua -- operators_spec.lua -- --- Tests for the Vimscript |functions| library should live in: +-- Tests for the Vimscript |builtin-functions| library should live in: -- test/functional/vimscript/<funcname>_spec.lua -- test/functional/vimscript/functions_spec.lua diff --git a/test/functional/vimscript/functions_spec.lua b/test/functional/vimscript/functions_spec.lua index 0ad7fd8010..20c1400030 100644 --- a/test/functional/vimscript/functions_spec.lua +++ b/test/functional/vimscript/functions_spec.lua @@ -1,4 +1,4 @@ --- Tests for misc Vimscript |functions|. +-- Tests for misc Vimscript |builtin-functions|. -- -- If a function is non-trivial, consider moving its spec to: -- test/functional/vimscript/<funcname>_spec.lua |