diff options
Diffstat (limited to 'test')
71 files changed, 2063 insertions, 574 deletions
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index da7515f012..8ed642b43e 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -534,6 +534,26 @@ describe('api/buf', function() end) end) + describe('nvim_buf_delete', function() + it('allows for just deleting', function() + nvim('command', 'new') + local b = nvim('get_current_buf') + ok(buffer('is_valid', b)) + nvim('buf_delete', b, {}) + ok(not buffer('is_loaded', b)) + ok(not buffer('is_valid', b)) + end) + + it('allows for just unloading', function() + nvim('command', 'new') + local b = nvim('get_current_buf') + ok(buffer('is_valid', b)) + nvim('buf_delete', b, { unload = true }) + ok(not buffer('is_loaded', b)) + ok(buffer('is_valid', b)) + end) + end) + describe('nvim_buf_get_mark', function() it('works', function() curbuf('set_lines', -1, -1, true, {'a', 'bit of', 'text'}) diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 9ea35e50a2..ab913ba4a4 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -17,22 +17,14 @@ local function expect(contents) return eq(contents, helpers.curbuf_contents()) end -local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end - local rv = curbufmeths.get_extmark_by_id(ns, mark) - eq({er, ec}, rv) - feed("u") - rv = curbufmeths.get_extmark_by_id(ns, mark) - eq({sr, sc}, rv) - feed("<c-r>") - rv = curbufmeths.get_extmark_by_id(ns, mark) - eq({er, ec}, rv) -end - local function set_extmark(ns_id, id, line, col, opts) if opts == nil then opts = {} end - return curbufmeths.set_extmark(ns_id, id, line, col, opts) + if id ~= nil and id ~= 0 then + opts.id = id + end + return curbufmeths.set_extmark(ns_id, line, col, opts) end local function get_extmarks(ns_id, start, end_, opts) @@ -42,6 +34,24 @@ local function get_extmarks(ns_id, start, end_, opts) return curbufmeths.get_extmarks(ns_id, start, end_, opts) end +local function get_extmark_by_id(ns_id, id, opts) + if opts == nil then + opts = {} + end + return curbufmeths.get_extmark_by_id(ns_id, id, opts) +end + +local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end + local rv = get_extmark_by_id(ns, mark) + eq({er, ec}, rv) + feed("u") + rv = get_extmark_by_id(ns, mark) + eq({sr, sc}, rv) + feed("<c-r>") + rv = get_extmark_by_id(ns, mark) + eq({er, ec}, rv) +end + local function batch_set(ns_id, positions) local ids = {} for _, pos in ipairs(positions) do @@ -90,10 +100,19 @@ describe('API/extmarks', function() ns2 = request('nvim_create_namespace', "my-fancy-plugin2") end) + it("can end extranges past final newline using end_col = 0", function() + set_extmark(ns, marks[1], 0, 0, { + end_col = 0, + end_line = 1 + }) + eq("end_col value outside range", + pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_line = 1 })) + 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) - rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + rv = get_extmark_by_id(ns, marks[1]) eq({positions[1][1], positions[1][2]}, rv) -- Test adding a second mark on same row works rv = set_extmark(ns, marks[2], positions[2][1], positions[2][2]) @@ -102,14 +121,14 @@ describe('API/extmarks', function() -- Test an update, (same pos) rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2]) eq(marks[1], rv) - rv = curbufmeths.get_extmark_by_id(ns, marks[2]) + rv = get_extmark_by_id(ns, marks[2]) eq({positions[2][1], positions[2][2]}, rv) -- Test an update, (new pos) row = positions[1][1] col = positions[1][2] + 1 rv = set_extmark(ns, marks[1], row, col) eq(marks[1], rv) - rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + rv = get_extmark_by_id(ns, marks[1]) eq({row, col}, rv) -- remove the test marks @@ -432,7 +451,7 @@ describe('API/extmarks', function() ~ | | ]]) - local rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + local rv = get_extmark_by_id(ns, marks[1]) eq({0, 6}, rv) check_undo_redo(ns, marks[1], 0, 3, 0, 6) end) @@ -906,9 +925,9 @@ describe('API/extmarks', function() -- Set the mark before the cursor, should stay there set_extmark(ns, marks[2], 0, 10) feed("i<cr><esc>") - local rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + local rv = get_extmark_by_id(ns, marks[1]) eq({1, 3}, rv) - rv = curbufmeths.get_extmark_by_id(ns, marks[2]) + rv = get_extmark_by_id(ns, marks[2]) eq({0, 10}, rv) check_undo_redo(ns, marks[1], 0, 12, 1, 3) end) @@ -921,12 +940,12 @@ describe('API/extmarks', function() feed("0iint <esc>A {<cr><esc>0i1M1<esc>") set_extmark(ns, marks[1], 1, 1) feed("0i<c-f><esc>") - local rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + local rv = get_extmark_by_id(ns, marks[1]) eq({1, 3}, rv) check_undo_redo(ns, marks[1], 1, 1, 1, 3) -- now check when cursor at eol feed("uA<c-f><esc>") - rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + rv = get_extmark_by_id(ns, marks[1]) eq({1, 3}, rv) end) @@ -937,12 +956,12 @@ describe('API/extmarks', function() feed("0i<tab><esc>") set_extmark(ns, marks[1], 0, 3) feed("bi<c-d><esc>") - local rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + local rv = get_extmark_by_id(ns, marks[1]) eq({0, 1}, rv) check_undo_redo(ns, marks[1], 0, 3, 0, 1) -- check when cursor at eol feed("uA<c-d><esc>") - rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + rv = get_extmark_by_id(ns, marks[1]) eq({0, 1}, rv) end) @@ -1072,7 +1091,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[5], 2, 0, 3, 0) feed('u') feed([[:1,2s:3:\rxx<cr>]]) - eq({1, 3}, curbufmeths.get_extmark_by_id(ns, marks[3])) + eq({1, 3}, get_extmark_by_id(ns, marks[3])) end) it('substitions over multiple lines with replace in substition', function() @@ -1311,16 +1330,16 @@ describe('API/extmarks', function() eq("Invalid ns_id", pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2])) eq("Invalid ns_id", pcall_err(curbufmeths.del_extmark, ns_invalid, marks[1])) eq("Invalid ns_id", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2])) - eq("Invalid ns_id", pcall_err(curbufmeths.get_extmark_by_id, ns_invalid, marks[1])) + eq("Invalid ns_id", pcall_err(get_extmark_by_id, ns_invalid, marks[1])) end) it('when col = line-length, set the mark on eol', function() set_extmark(ns, marks[1], 0, -1) - local rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + local rv = get_extmark_by_id(ns, marks[1]) eq({0, init_text:len()}, rv) -- Test another set_extmark(ns, marks[1], 0, -1) - rv = curbufmeths.get_extmark_by_id(ns, marks[1]) + rv = get_extmark_by_id(ns, marks[1]) eq({0, init_text:len()}, rv) end) @@ -1333,7 +1352,7 @@ describe('API/extmarks', function() local invalid_col = init_text:len() + 1 local invalid_lnum = 3 eq('line value outside range', pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col)) - eq({}, curbufmeths.get_extmark_by_id(ns, marks[1])) + eq({}, get_extmark_by_id(ns, marks[1])) end) it('bug from check_col in extmark_set', function() @@ -1357,14 +1376,14 @@ describe('API/extmarks', function() it('can set a mark to other buffer', function() local buf = request('nvim_create_buf', 0, 1) request('nvim_buf_set_lines', buf, 0, -1, 1, {"", ""}) - local id = bufmeths.set_extmark(buf, ns, 0, 1, 0, {}) + local id = bufmeths.set_extmark(buf, ns, 1, 0, {}) eq({{id, 1, 0}}, bufmeths.get_extmarks(buf, ns, 0, -1, {})) end) it('does not crash with append/delete/undo seqence', function() meths.exec([[ let ns = nvim_create_namespace('myplugin') - call nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) + call nvim_buf_set_extmark(0, ns, 0, 0, {}) call append(0, '') %delete undo]],false) diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index a9d4c72d31..daf20c006c 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -7,6 +7,7 @@ local meths = helpers.meths local funcs = helpers.funcs local pcall_err = helpers.pcall_err local ok = helpers.ok +local assert_alive = helpers.assert_alive describe('API: highlight',function() local expected_rgb = { @@ -145,4 +146,15 @@ describe('API: highlight',function() eq({foreground=tonumber("0x888888"), background=tonumber("0x888888")}, meths.get_hl_by_name("Shrubbery", true)) end) + + it("nvim_buf_add_highlight to other buffer doesn't crash if undo is disabled #12873", function() + command('vsplit file') + local err, _ = pcall(meths.buf_set_option, 1, 'undofile', false) + eq(true, err) + err, _ = pcall(meths.buf_set_option, 1, 'undolevels', -1) + eq(true, err) + err, _ = pcall(meths.buf_add_highlight, 1, -1, 'Question', 0, 0, -1) + eq(true, err) + assert_alive() + end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 72e810e3e4..0b52d06df9 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -449,19 +449,19 @@ describe('API', function() end) it('reports errors', function() - eq([[Error loading lua: [string "<nvim>"]:1: '=' expected near '+']], + eq([[Error loading lua: [string "<nvim>"]:0: '=' expected near '+']], pcall_err(meths.exec_lua, 'a+*b', {})) - eq([[Error loading lua: [string "<nvim>"]:1: unexpected symbol near '1']], + eq([[Error loading lua: [string "<nvim>"]:0: unexpected symbol near '1']], pcall_err(meths.exec_lua, '1+2', {})) - eq([[Error loading lua: [string "<nvim>"]:1: unexpected symbol]], + eq([[Error loading lua: [string "<nvim>"]:0: unexpected symbol]], pcall_err(meths.exec_lua, 'aa=bb\0', {})) - eq([[Error executing lua: [string "<nvim>"]:1: attempt to call global 'bork' (a nil value)]], + eq([[Error executing lua: [string "<nvim>"]:0: attempt to call global 'bork' (a nil value)]], pcall_err(meths.exec_lua, 'bork()', {})) - eq('Error executing lua: [string "<nvim>"]:1: did\nthe\nfail', + eq('Error executing lua: [string "<nvim>"]:0: did\nthe\nfail', pcall_err(meths.exec_lua, 'error("did\\nthe\\nfail")', {})) end) @@ -605,7 +605,7 @@ describe('API', function() end) it('vim.paste() failure', function() nvim('exec_lua', 'vim.paste = (function(lines, phase) error("fake fail") end)', {}) - eq([[Error executing lua: [string "<nvim>"]:1: fake fail]], + eq([[Error executing lua: [string "<nvim>"]:0: fake fail]], pcall_err(request, 'nvim_paste', 'line 1\nline 2\nline 3', false, 1)) end) end) @@ -1252,7 +1252,7 @@ describe('API', function() {0:~ }| {1:very fail} | ]]) - helpers.wait() + helpers.poke_eventloop() -- shows up to &cmdheight lines nvim_async('err_write', 'more fail\ntoo fail\n') diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 8c7c3208c0..7471f50dbd 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -3,7 +3,7 @@ local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local curwinmeths = helpers.curwinmeths local funcs = helpers.funcs local request = helpers.request @@ -82,7 +82,7 @@ describe('API/win', function() insert("epilogue") local win = curwin() feed('gg') - wait() -- let nvim process the 'gg' command + poke_eventloop() -- let nvim process the 'gg' command -- cursor position is at beginning eq({1, 0}, window('get_cursor', win)) @@ -128,7 +128,7 @@ describe('API/win', function() insert("second line") feed('gg') - wait() -- let nvim process the 'gg' command + poke_eventloop() -- let nvim process the 'gg' command -- cursor position is at beginning local win = curwin() @@ -139,7 +139,7 @@ describe('API/win', function() -- move down a line feed('j') - wait() -- let nvim process the 'j' command + poke_eventloop() -- let nvim process the 'j' command -- cursor is still in column 5 eq({2, 5}, window('get_cursor', win)) diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 80c65e4544..230b7f8e01 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -8,7 +8,7 @@ local run = helpers.run local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog local redir_exec = helpers.redir_exec -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('v:exiting', function() local cid @@ -52,7 +52,7 @@ describe(':cquit', function() local function test_cq(cmdline, exit_code, redir_msg) if redir_msg then eq('\n' .. redir_msg, redir_exec(cmdline)) - wait() + poke_eventloop() eq(2, eval("1+1")) -- Still alive? else funcs.system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline}) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 57e6f4fd63..6d1182478a 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -11,7 +11,7 @@ local os_kill = helpers.os_kill local retry = helpers.retry local meths = helpers.meths local NIL = helpers.NIL -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local iswin = helpers.iswin local get_pathsep = helpers.get_pathsep local pathroot = helpers.pathroot @@ -93,6 +93,7 @@ describe('jobs', function() {'notification', 'stdout', {0, {'hello world %VAR%', ''}}} }) else + nvim('command', "set shell=/bin/sh") nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]]) expect_msg_seq({ {'notification', 'stdout', {0, {'hello world', ''}}} @@ -427,7 +428,7 @@ describe('jobs', function() \ } let job = jobstart(['cat', '-'], g:callbacks) ]]) - wait() + poke_eventloop() source([[ function! g:JobHandler(job_id, data, event) endfunction diff --git a/test/functional/eval/interrupt_spec.lua b/test/functional/eval/interrupt_spec.lua index 7f4ca95317..05b1f4ff57 100644 --- a/test/functional/eval/interrupt_spec.lua +++ b/test/functional/eval/interrupt_spec.lua @@ -4,7 +4,7 @@ local command = helpers.command local meths = helpers.meths local clear = helpers.clear local sleep = helpers.sleep -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local feed = helpers.feed local eq = helpers.eq @@ -39,7 +39,7 @@ describe('List support code', function() feed(':let t_rt = reltime()<CR>:let t_bl = copy(bl)<CR>') sleep(min_dur / 16 * 1000) feed('<C-c>') - wait() + poke_eventloop() command('let t_dur = reltimestr(reltime(t_rt))') local t_dur = tonumber(meths.get_var('t_dur')) if t_dur >= dur / 8 then @@ -50,7 +50,7 @@ describe('List support code', function() feed(':let t_rt = reltime()<CR>:let t_j = join(bl)<CR>') sleep(min_dur / 16 * 1000) feed('<C-c>') - wait() + poke_eventloop() command('let t_dur = reltimestr(reltime(t_rt))') local t_dur = tonumber(meths.get_var('t_dur')) print(('t_dur: %g'):format(t_dur)) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index 802c3f68c6..003ab64dd4 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local buf, eq, feed_command = helpers.curbufmeths, helpers.eq, helpers.feed_command -local feed, wait = helpers.feed, helpers.wait +local feed, poke_eventloop = helpers.feed, helpers.poke_eventloop local ok = helpers.ok local eval = helpers.eval @@ -90,7 +90,7 @@ describe(':browse oldfiles', function() feed_command('edit testfile2') filename2 = buf.get_name() feed_command('wshada') - wait() + poke_eventloop() _clear() -- Ensure nvim is out of "Press ENTER..." prompt. diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index dca7f35923..a30eb748d0 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -125,6 +125,26 @@ function tests.basic_check_capabilities() } end +function tests.capabilities_for_client_supports_method() + skeleton { + on_init = function(params) + local expected_capabilities = protocol.make_client_capabilities() + assert_eq(params.capabilities, expected_capabilities) + return { + capabilities = { + textDocumentSync = protocol.TextDocumentSyncKind.Full; + completionProvider = true; + hoverProvider = true; + definitionProvider = false; + referencesProvider = false; + } + } + end; + body = function() + end; + } +end + function tests.basic_finish() skeleton { on_init = function(params) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index e8435cd3b7..0bd378d832 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -22,6 +22,7 @@ local ok = global_helpers.ok local sleep = global_helpers.sleep local tbl_contains = global_helpers.tbl_contains local write_file = global_helpers.write_file +local fail = global_helpers.fail local module = { NIL = mpack.NIL, @@ -553,9 +554,9 @@ function module.curbuf(method, ...) return module.buffer(method, 0, ...) end -function module.wait() - -- Execute 'nvim_eval' (a deferred function) to block - -- until all pending input is processed. +function module.poke_eventloop() + -- Execute 'nvim_eval' (a deferred function) to + -- force at least one main_loop iteration session:request('nvim_eval', '1') end @@ -565,7 +566,7 @@ end --@see buf_lines() function module.curbuf_contents() - module.wait() -- Before inspecting the buffer, process all input. + module.poke_eventloop() -- Before inspecting the buffer, do whatever. return table.concat(module.curbuf('get_lines', 0, -1, true), '\n') end @@ -592,6 +593,24 @@ function module.expect_any(contents) return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true)) end +function module.expect_events(expected, received, kind) + local inspect = require'vim.inspect' + if not pcall(eq, expected, received) then + local msg = 'unexpected '..kind..' received.\n\n' + + msg = msg .. 'received events:\n' + for _, e in ipairs(received) do + msg = msg .. ' ' .. inspect(e) .. ';\n' + end + msg = msg .. '\nexpected events:\n' + for _, e in ipairs(expected) do + msg = msg .. ' ' .. inspect(e) .. ';\n' + end + fail(msg) + end + return received +end + -- Checks that the Nvim session did not terminate. function module.assert_alive() assert(2 == module.eval('1+1'), 'crash? request failed') @@ -769,14 +788,14 @@ end function module.missing_provider(provider) if provider == 'ruby' or provider == 'node' or provider == 'perl' then - local prog = module.funcs['provider#' .. provider .. '#Detect']() - return prog == '' and (provider .. ' not detected') or false + local e = module.funcs['provider#'..provider..'#Detect']()[2] + return e ~= '' and e or false elseif provider == 'python' or provider == 'python3' then local py_major_version = (provider == 'python3' and 3 or 2) - local errors = module.funcs['provider#pythonx#Detect'](py_major_version)[2] - return errors ~= '' and errors or false + local e = module.funcs['provider#pythonx#Detect'](py_major_version)[2] + return e ~= '' and e or false else - assert(false, 'Unknown provider: ' .. provider) + assert(false, 'Unknown provider: '..provider) end end diff --git a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua index 8b92c877a6..8e977aa73e 100644 --- a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua +++ b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('test5', function() setup(clear) @@ -34,7 +34,7 @@ describe('test5', function() command('bwipe') feed('G?this is a<cr>') feed('othis is some more text<esc>') - wait() + poke_eventloop() -- Append some text to this file. @@ -45,7 +45,7 @@ describe('test5', function() command('bwipe!') -- Append an extra line to the output register. feed('ithis is another test line<esc>:yank A<cr>') - wait() + poke_eventloop() -- Output results command('%d') diff --git a/test/functional/legacy/006_argument_list_spec.lua b/test/functional/legacy/006_argument_list_spec.lua index 9f75a91fa8..d269bf8ec9 100644 --- a/test/functional/legacy/006_argument_list_spec.lua +++ b/test/functional/legacy/006_argument_list_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, dedent, eq = helpers.command, helpers.dedent, helpers.eq local curbuf_contents = helpers.curbuf_contents -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('argument list', function() setup(clear) @@ -17,7 +17,7 @@ describe('argument list', function() this is a test this is a test end of test file Xxx]]) - wait() + poke_eventloop() command('au BufReadPost Xxx2 next Xxx2 Xxx1') command('/^start of') @@ -30,7 +30,7 @@ describe('argument list', function() -- Write test file Xxx3 feed('$r3:.,/end of/w! Xxx3<cr>') - wait() + poke_eventloop() -- Redefine arglist; go to Xxx1 command('next! Xxx1 Xxx2 Xxx3') @@ -43,7 +43,7 @@ describe('argument list', function() -- Append contents of last window (Xxx1) feed('') - wait() + poke_eventloop() command('%yank A') -- should now be in Xxx2 diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua index cec4f93737..48dd24db9e 100644 --- a/test/functional/legacy/012_directory_spec.lua +++ b/test/functional/legacy/012_directory_spec.lua @@ -8,7 +8,7 @@ local lfs = require('lfs') local eq = helpers.eq local neq = helpers.neq -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local funcs = helpers.funcs local meths = helpers.meths local clear = helpers.clear @@ -64,7 +64,7 @@ describe("'directory' option", function() eq(nil, lfs.attributes('.Xtest1.swp')) command('edit! Xtest1') - wait() + poke_eventloop() eq('Xtest1', funcs.buffer_name('%')) -- Verify that the swapfile exists. In the legacy test this was done by -- reading the output from :!ls. @@ -72,7 +72,7 @@ describe("'directory' option", function() meths.set_option('directory', './Xtest2,.') command('edit Xtest1') - wait() + poke_eventloop() -- swapfile should no longer exist in CWD. eq(nil, lfs.attributes('.Xtest1.swp')) @@ -82,7 +82,7 @@ describe("'directory' option", function() meths.set_option('directory', 'Xtest.je') command('edit Xtest2/Xtest3') eq(true, curbufmeths.get_option('swapfile')) - wait() + poke_eventloop() eq({ "Xtest3" }, ls_dir_sorted("Xtest2")) eq({ "Xtest3.swp" }, ls_dir_sorted("Xtest.je")) diff --git a/test/functional/legacy/023_edit_arguments_spec.lua b/test/functional/legacy/023_edit_arguments_spec.lua index e705397a2b..f59d192c1e 100644 --- a/test/functional/legacy/023_edit_arguments_spec.lua +++ b/test/functional/legacy/023_edit_arguments_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, insert = helpers.clear, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe(':edit', function() setup(clear) @@ -13,7 +13,7 @@ describe(':edit', function() The result should be in Xfile1: "fooPIPEbar", in Xfile2: "fooSLASHbar" foo|bar foo/bar]]) - wait() + poke_eventloop() -- Prepare some test files command('$-1w! Xfile1') diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua index 2fd51602d8..15dbd05cf5 100644 --- a/test/functional/legacy/030_fileformats_spec.lua +++ b/test/functional/legacy/030_fileformats_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, clear, command = helpers.feed, helpers.clear, helpers.command local eq, write_file = helpers.eq, helpers.write_file -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('fileformats option', function() setup(function() @@ -107,7 +107,7 @@ describe('fileformats option', function() command('bwipe XXDosMac') command('e! XXEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt54') command('bwipeout! XXEol') command('set fileformats=dos,mac') @@ -116,7 +116,7 @@ describe('fileformats option', function() command('bwipe XXUxDs') command('e! XXUxMac') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt62') command('bwipeout! XXUxMac') command('e! XXUxDsMc') @@ -124,7 +124,7 @@ describe('fileformats option', function() command('bwipe XXUxDsMc') command('e! XXMacEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt64') command('bwipeout! XXMacEol') @@ -135,7 +135,7 @@ describe('fileformats option', function() command('bwipe XXUxDsMc') command('e! XXEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt72') command('bwipeout! XXEol') command('set fileformats=mac,dos,unix') @@ -144,7 +144,7 @@ describe('fileformats option', function() command('bwipe XXUxDsMc') command('e! XXEol') feed('ggO<C-R>=&ffs<CR>:<C-R>=&ff<CR><ESC>') - wait() + poke_eventloop() command('w! XXtt82') command('bwipeout! XXEol') -- Try with 'binary' set. @@ -165,7 +165,7 @@ describe('fileformats option', function() -- char was. command('set fileformat=unix nobin') feed('ggdGaEND<esc>') - wait() + poke_eventloop() command('w >>XXtt01') command('w >>XXtt02') command('w >>XXtt11') @@ -204,52 +204,52 @@ describe('fileformats option', function() command('$r XXtt01') command('$r XXtt02') feed('Go1<esc>') - wait() + poke_eventloop() command('$r XXtt11') command('$r XXtt12') command('$r XXtt13') feed('Go2<esc>') - wait() + poke_eventloop() command('$r XXtt21') command('$r XXtt22') command('$r XXtt23') feed('Go3<esc>') - wait() + poke_eventloop() command('$r XXtt31') command('$r XXtt32') command('$r XXtt33') feed('Go4<esc>') - wait() + poke_eventloop() command('$r XXtt41') command('$r XXtt42') command('$r XXtt43') feed('Go5<esc>') - wait() + poke_eventloop() command('$r XXtt51') command('$r XXtt52') command('$r XXtt53') command('$r XXtt54') feed('Go6<esc>') - wait() + poke_eventloop() command('$r XXtt61') command('$r XXtt62') command('$r XXtt63') command('$r XXtt64') feed('Go7<esc>') - wait() + poke_eventloop() command('$r XXtt71') command('$r XXtt72') feed('Go8<esc>') - wait() + poke_eventloop() command('$r XXtt81') command('$r XXtt82') feed('Go9<esc>') - wait() + poke_eventloop() command('$r XXtt91') command('$r XXtt92') command('$r XXtt93') feed('Go10<esc>') - wait() + poke_eventloop() command('$r XXUnix') command('set nobinary ff&') diff --git a/test/functional/legacy/033_lisp_indent_spec.lua b/test/functional/legacy/033_lisp_indent_spec.lua index 5132333a5c..b27de6c16d 100644 --- a/test/functional/legacy/033_lisp_indent_spec.lua +++ b/test/functional/legacy/033_lisp_indent_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('lisp indent', function() setup(clear) @@ -39,7 +39,7 @@ describe('lisp indent', function() command('set lisp') command('/^(defun') feed('=G:/^(defun/,$yank A<cr>') - wait() + poke_eventloop() -- Put @a and clean empty line command('%d') diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua index 38e8145d1c..6f66efcb67 100644 --- a/test/functional/legacy/036_regexp_character_classes_spec.lua +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -15,7 +15,7 @@ end local function diff(text, nodedent) local fname = helpers.tmpname() command('w! '..fname) - helpers.wait() + helpers.poke_eventloop() local data = io.open(fname):read('*all') if nodedent then helpers.eq(text, data) diff --git a/test/functional/legacy/045_folding_spec.lua b/test/functional/legacy/045_folding_spec.lua index 1e5239ceac..7d7856fd37 100644 --- a/test/functional/legacy/045_folding_spec.lua +++ b/test/functional/legacy/045_folding_spec.lua @@ -59,7 +59,7 @@ describe('folding', function() feed('kYpj') feed_command('call append("$", foldlevel("."))') - helpers.wait() + helpers.poke_eventloop() screen:expect([[ dd {{{ | ee {{{ }}} | @@ -88,7 +88,7 @@ describe('folding', function() feed_command('call append("$", foldlevel(2))') feed('zR') - helpers.wait() + helpers.poke_eventloop() screen:expect([[ aa | bb | diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index 0c9c9621ee..d3f2897493 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -5,7 +5,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed local expect = helpers.expect local eq = helpers.eq -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local exc_exec = helpers.exc_exec local feed_command = helpers.feed_command @@ -34,7 +34,7 @@ describe(':highlight', function() -- More --^ | ]]) feed('q') - wait() -- wait until we're back to normal + poke_eventloop() -- wait until we're back to normal feed_command('hi Search') feed_command('hi Normal') diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index bdc2c9779c..328d6f6fa0 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -2,8 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) -local insert, command, clear, expect, eq, wait = helpers.insert, - helpers.command, helpers.clear, helpers.expect, helpers.eq, helpers.wait +local insert, command, clear, expect, eq, poke_eventloop = helpers.insert, + helpers.command, helpers.clear, helpers.expect, helpers.eq, helpers.poke_eventloop local exc_exec = helpers.exc_exec describe(':sort', function() @@ -27,7 +27,7 @@ describe(':sort', function() it('alphabetical', function() insert(text) - wait() + poke_eventloop() command('sort') expect([[ @@ -67,7 +67,7 @@ describe(':sort', function() b321 b321b ]]) - wait() + poke_eventloop() command('sort n') expect([[ abc @@ -92,7 +92,7 @@ describe(':sort', function() it('hexadecimal', function() insert(text) - wait() + poke_eventloop() command('sort x') expect([[ @@ -114,7 +114,7 @@ describe(':sort', function() it('alphabetical, unique', function() insert(text) - wait() + poke_eventloop() command('sort u') expect([[ @@ -135,7 +135,7 @@ describe(':sort', function() it('alphabetical, reverse', function() insert(text) - wait() + poke_eventloop() command('sort!') expect([[ c321d @@ -157,7 +157,7 @@ describe(':sort', function() it('numerical, reverse', function() insert(text) - wait() + poke_eventloop() command('sort! n') expect([[ b322b @@ -179,7 +179,7 @@ describe(':sort', function() it('unique, reverse', function() insert(text) - wait() + poke_eventloop() command('sort! u') expect([[ c321d @@ -200,7 +200,7 @@ describe(':sort', function() it('octal', function() insert(text) - wait() + poke_eventloop() command('sort o') expect([[ abc @@ -222,7 +222,7 @@ describe(':sort', function() it('reverse, hexadecimal', function() insert(text) - wait() + poke_eventloop() command('sort! x') expect([[ c321d @@ -244,7 +244,7 @@ describe(':sort', function() it('alphabetical, skip first character', function() insert(text) - wait() + poke_eventloop() command('sort/./') expect([[ a @@ -266,7 +266,7 @@ describe(':sort', function() it('alphabetical, skip first 2 characters', function() insert(text) - wait() + poke_eventloop() command('sort/../') expect([[ ab @@ -288,7 +288,7 @@ describe(':sort', function() it('alphabetical, unique, skip first 2 characters', function() insert(text) - wait() + poke_eventloop() command('sort/../u') expect([[ ab @@ -309,7 +309,7 @@ describe(':sort', function() it('numerical, skip first character', function() insert(text) - wait() + poke_eventloop() command('sort/./n') expect([[ abc @@ -331,7 +331,7 @@ describe(':sort', function() it('alphabetical, sort on first character', function() insert(text) - wait() + poke_eventloop() command('sort/./r') expect([[ @@ -353,7 +353,7 @@ describe(':sort', function() it('alphabetical, sort on first 2 characters', function() insert(text) - wait() + poke_eventloop() command('sort/../r') expect([[ a @@ -375,7 +375,7 @@ describe(':sort', function() it('numerical, sort on first character', function() insert(text) - wait() + poke_eventloop() command('sort/./rn') expect([[ abc @@ -397,7 +397,7 @@ describe(':sort', function() it('alphabetical, skip past first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/]]) expect([[ abc @@ -419,7 +419,7 @@ describe(':sort', function() it('alphabetical, sort on first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/r]]) expect([[ abc @@ -441,7 +441,7 @@ describe(':sort', function() it('numerical, skip past first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/n]]) expect([[ abc @@ -463,7 +463,7 @@ describe(':sort', function() it('numerical, sort on first digit', function() insert(text) - wait() + poke_eventloop() command([[sort/\d/rn]]) expect([[ abc @@ -485,7 +485,7 @@ describe(':sort', function() it('alphabetical, skip past first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/]]) expect([[ abc @@ -507,7 +507,7 @@ describe(':sort', function() it('numerical, skip past first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/n]]) expect([[ abc @@ -529,7 +529,7 @@ describe(':sort', function() it('hexadecimal, skip past first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/x]]) expect([[ abc @@ -551,7 +551,7 @@ describe(':sort', function() it('alpha, on first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/r]]) expect([[ abc @@ -573,7 +573,7 @@ describe(':sort', function() it('numeric, on first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/rn]]) expect([[ abc @@ -595,7 +595,7 @@ describe(':sort', function() it('hexadecimal, on first 2 digits', function() insert(text) - wait() + poke_eventloop() command([[sort/\d\d/rx]]) expect([[ abc @@ -638,7 +638,7 @@ describe(':sort', function() 0b100010 0b100100 0b100010]]) - wait() + poke_eventloop() command([[sort b]]) expect([[ 0b000000 @@ -673,7 +673,7 @@ describe(':sort', function() 0b101010 0b000000 b0b111000]]) - wait() + poke_eventloop() command([[sort b]]) expect([[ 0b000000 @@ -700,7 +700,7 @@ describe(':sort', function() 1.15e-6 -1.1e3 -1.01e3]]) - wait() + poke_eventloop() command([[sort f]]) expect([[ -1.1e3 diff --git a/test/functional/legacy/074_global_var_in_viminfo_spec.lua b/test/functional/legacy/074_global_var_in_viminfo_spec.lua index f7f074c61a..445d742c1f 100644 --- a/test/functional/legacy/074_global_var_in_viminfo_spec.lua +++ b/test/functional/legacy/074_global_var_in_viminfo_spec.lua @@ -2,9 +2,9 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') -local clear, command, eq, neq, eval, wait = +local clear, command, eq, neq, eval, poke_eventloop = helpers.clear, helpers.command, helpers.eq, helpers.neq, helpers.eval, - helpers.wait + helpers.poke_eventloop describe('storing global variables in ShaDa files', function() local tempname = 'Xtest-functional-legacy-074' @@ -36,7 +36,7 @@ describe('storing global variables in ShaDa files', function() eq(test_list, eval('MY_GLOBAL_LIST')) command('wsh! ' .. tempname) - wait() + poke_eventloop() -- Assert that the shada file exists. neq(nil, lfs.attributes(tempname)) diff --git a/test/functional/legacy/075_maparg_spec.lua b/test/functional/legacy/075_maparg_spec.lua index ee2b041b51..ad6c190104 100644 --- a/test/functional/legacy/075_maparg_spec.lua +++ b/test/functional/legacy/075_maparg_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('maparg()', function() setup(clear) @@ -25,7 +25,7 @@ describe('maparg()', function() command('map abc y<S-char-114>y') command([[call append('$', maparg('abc'))]]) feed('Go<esc>:<cr>') - wait() + poke_eventloop() -- Outside of the range, minimum command('inoremap <Char-0x1040> a') diff --git a/test/functional/legacy/107_adjust_window_and_contents_spec.lua b/test/functional/legacy/107_adjust_window_and_contents_spec.lua index 239f60341a..841eeef0af 100644 --- a/test/functional/legacy/107_adjust_window_and_contents_spec.lua +++ b/test/functional/legacy/107_adjust_window_and_contents_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local clear = helpers.clear local insert = helpers.insert local command = helpers.command @@ -16,7 +16,7 @@ describe('107', function() screen:attach() insert('start:') - wait() + poke_eventloop() command('new') command('call setline(1, range(1,256))') command('let r=[]') diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 241a19d940..67c5750033 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -42,9 +42,7 @@ describe('argument list commands', function() end) it('test that argadd() works', function() - -- Fails with “E474: Invalid argument”. Not sure whether it is how it is - -- supposed to behave. - -- command('%argdelete') + command('%argdelete') command('argadd a b c') eq(0, eval('argidx()')) @@ -176,9 +174,14 @@ describe('argument list commands', function() command('last') command('argdelete %') eq({'b'}, eval('argv()')) - assert_fails('argdelete', 'E471:') + assert_fails('argdelete', 'E610:') assert_fails('1,100argdelete', 'E16:') - command('%argd') + reset_arglist() + command('args a b c d') + command('next') + command('argdel') + eq({'a', 'c', 'd'}, eval('argv()')) + command('%argdel') end) it('test for the :next, :prev, :first, :last, :rewind commands', function() diff --git a/test/functional/legacy/autoformat_join_spec.lua b/test/functional/legacy/autoformat_join_spec.lua index 84d661c190..22b1c258fe 100644 --- a/test/functional/legacy/autoformat_join_spec.lua +++ b/test/functional/legacy/autoformat_join_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('autoformat join', function() setup(clear) @@ -21,7 +21,7 @@ Results:]]) feed('gg') feed('0gqj<cr>') - wait() + poke_eventloop() command([[let a=string(getpos("'[")).'/'.string(getpos("']"))]]) command("g/^This line/;'}-join") diff --git a/test/functional/legacy/close_count_spec.lua b/test/functional/legacy/close_count_spec.lua index 9b932e2ef0..60ae155fbf 100644 --- a/test/functional/legacy/close_count_spec.lua +++ b/test/functional/legacy/close_count_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local eval = helpers.eval local feed = helpers.feed local clear = helpers.clear @@ -110,23 +110,23 @@ describe('close_count', function() command('for i in range(5)|new|endfor') command('4wincmd w') feed('<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({25, 24, 23, 21, 1}, eval('buffers')) feed('1<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({24, 23, 21, 1}, eval('buffers')) feed('9<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({24, 23, 21}, eval('buffers')) command('1wincmd w') feed('2<C-W>c<cr>') - wait() + poke_eventloop() command('let buffers = []') command('windo call add(buffers, bufnr("%"))') eq({24, 21}, eval('buffers')) diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua new file mode 100644 index 0000000000..3fbbe96947 --- /dev/null +++ b/test/functional/legacy/display_spec.lua @@ -0,0 +1,31 @@ +local helpers = require('test.functional.helpers')(after_each) + +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local poke_eventloop = helpers.poke_eventloop +local feed = helpers.feed +local feed_command = helpers.feed_command + +describe('display', function() + local screen + + it('scroll when modified at topline', function() + clear() + screen = Screen.new(20, 4) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true}, + }) + + feed_command([[call setline(1, repeat('a', 21))]]) + poke_eventloop() + feed('O') + screen:expect([[ + ^ | + aaaaaaaaaaaaaaaaaaaa| + a | + {1:-- INSERT --} | + ]]) + end) +end) + diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua index 4198ea8bfe..ee9bd29fc4 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, command, expect = helpers.clear, helpers.command, helpers.expect local eq, eval, write_file = helpers.eq, helpers.eval, helpers.write_file -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local exc_exec = helpers.exc_exec local dedent = helpers.dedent @@ -71,7 +71,7 @@ describe('eval', function() command([[call SetReg('I', 'abcI')]]) feed('Go{{{1 Appending single lines with setreg()<esc>') - wait() + poke_eventloop() command([[call SetReg('A', 'abcAc', 'c')]]) command([[call SetReg('A', 'abcAl', 'l')]]) command([[call SetReg('A', 'abcAc2','c')]]) @@ -700,13 +700,13 @@ describe('eval', function() start:]]) command('/^012345678') feed('6l') - wait() + poke_eventloop() command('let sp = getcurpos()') feed('0') - wait() + poke_eventloop() command("call setpos('.', sp)") feed('jyl') - wait() + poke_eventloop() command('$put') expect([[ 012345678 diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 56a5652184..92a757ca85 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect, wait = helpers.feed_command, helpers.expect, helpers.wait +local feed_command, expect, poke_eventloop = helpers.feed_command, helpers.expect, helpers.poke_eventloop describe('mapping', function() before_each(clear) @@ -29,9 +29,9 @@ describe('mapping', function() feed_command('cunmap <c-c>') feed('GA<cr>') feed('TEST2: CTRL-C |') - wait() + poke_eventloop() feed('<c-c>A|<cr><esc>') - wait() + poke_eventloop() feed_command('unmap <c-c>') feed_command('unmap! <c-c>') @@ -46,7 +46,7 @@ describe('mapping', function() feed('GV') -- XXX: For some reason the mapping is only triggered -- when <C-c> is in a separate feed command. - wait() + poke_eventloop() feed('<c-c>') feed_command('vunmap <c-c>') diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index 251e6a5ea4..fb0bacc2d2 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -7,7 +7,7 @@ local iswin = helpers.iswin local retry = helpers.retry local ok = helpers.ok local source = helpers.source -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local uname = helpers.uname local load_adjust = helpers.load_adjust @@ -102,7 +102,7 @@ describe('memory usage', function() call s:f(0) endfor ]]) - wait() + poke_eventloop() local after = monitor_memory_usage(pid) -- Estimate the limit of max usage as 2x initial usage. -- The lower limit can fluctuate a bit, use 97%. @@ -147,11 +147,11 @@ describe('memory usage', function() call s:f() endfor ]]) - wait() + poke_eventloop() local after = monitor_memory_usage(pid) for _ = 1, 3 do feed_command('so '..fname) - wait() + poke_eventloop() end local last = monitor_memory_usage(pid) -- The usage may be a bit less than the last value, use 80%. diff --git a/test/functional/legacy/search_mbyte_spec.lua b/test/functional/legacy/search_mbyte_spec.lua index a365f79cdf..ef7e41aa30 100644 --- a/test/functional/legacy/search_mbyte_spec.lua +++ b/test/functional/legacy/search_mbyte_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local clear = helpers.clear local insert = helpers.insert local expect = helpers.expect @@ -15,7 +15,7 @@ describe('search_mbyte', function() Test bce: A]=]) - wait() + poke_eventloop() command('/^Test bce:/+1') command([[$put =search('A', 'bce', line('.'))]]) diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua index a207b176d3..4ed08881de 100644 --- a/test/functional/legacy/search_spec.lua +++ b/test/functional/legacy/search_spec.lua @@ -6,7 +6,7 @@ local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed local funcs = helpers.funcs -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('search cmdline', function() local screen @@ -21,6 +21,7 @@ describe('search cmdline', function() err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, more = { bold = true, foreground = Screen.colors.SeaGreen4 }, tilde = { bold = true, foreground = Screen.colors.Blue1 }, + hl = { background = Screen.colors.Yellow }, }) end) @@ -482,9 +483,9 @@ describe('search cmdline', function() -- "interactive". This mimics Vim's test_override("char_avail"). -- (See legacy test: test_search.vim) feed('?the') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | @@ -495,11 +496,11 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 ^the first | @@ -510,13 +511,13 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<c-g>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | @@ -527,9 +528,9 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 ^the first | @@ -540,11 +541,11 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | @@ -555,13 +556,13 @@ describe('search cmdline', function() command('$') feed('?the') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<c-t>') - wait() + poke_eventloop() feed('<cr>') screen:expect([[ 1 the first | @@ -570,4 +571,72 @@ describe('search cmdline', function() ?the | ]]) end) + + it('incsearch works with :sort', function() + -- oldtest: Test_incsearch_sort_dump(). + screen:try_resize(20, 4) + command('set incsearch hlsearch scrolloff=0') + funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'}) + + feed(':sort ni u /on') + screen:expect([[ + another {inc:on}e 2 | + that {hl:on}e 3 | + the {hl:on}e 1 | + :sort ni u /on^ | + ]]) + feed('<esc>') + end) + + it('incsearch works with :vimgrep family', function() + -- oldtest: Test_incsearch_vimgrep_dump(). + screen:try_resize(30, 4) + command('set incsearch hlsearch scrolloff=0') + funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'}) + + feed(':vimgrep on') + screen:expect([[ + another {inc:on}e 2 | + that {hl:on}e 3 | + the {hl:on}e 1 | + :vimgrep on^ | + ]]) + feed('<esc>') + + feed(':vimg /on/ *.txt') + screen:expect([[ + another {inc:on}e 2 | + that {hl:on}e 3 | + the {hl:on}e 1 | + :vimg /on/ *.txt^ | + ]]) + feed('<esc>') + + feed(':vimgrepadd "\\<LT>on') + screen:expect([[ + another {inc:on}e 2 | + that {hl:on}e 3 | + the {hl:on}e 1 | + :vimgrepadd "\<on^ | + ]]) + feed('<esc>') + + feed(':lv "tha') + screen:expect([[ + another one 2 | + {inc:tha}t one 3 | + the one 1 | + :lv "tha^ | + ]]) + feed('<esc>') + + feed(':lvimgrepa "the" **/*.txt') + screen:expect([[ + ano{inc:the}r one 2 | + that one 3 | + {hl:the} one 1 | + :lvimgrepa "the" **/*.txt^ | + ]]) + feed('<esc>') + end) end) diff --git a/test/functional/legacy/utf8_spec.lua b/test/functional/legacy/utf8_spec.lua index 5b93f25b24..8b5fc02d11 100644 --- a/test/functional/legacy/utf8_spec.lua +++ b/test/functional/legacy/utf8_spec.lua @@ -5,7 +5,7 @@ local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command, expect = helpers.command, helpers.expect local eq, eval = helpers.eq, helpers.eval local source = helpers.source -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('utf8', function() before_each(clear) @@ -18,7 +18,7 @@ describe('utf8', function() -- Visual block Insert adjusts for multi-byte char feed('gg0l<C-V>jjIx<Esc>') - wait() + poke_eventloop() command('let r = getline(1, "$")') command('bwipeout!') diff --git a/test/functional/legacy/visual_mode_spec.lua b/test/functional/legacy/visual_mode_spec.lua new file mode 100644 index 0000000000..c8e83ed649 --- /dev/null +++ b/test/functional/legacy/visual_mode_spec.lua @@ -0,0 +1,42 @@ +-- Test visual line mode selection redraw after scrolling + +local helpers = require('test.functional.helpers')(after_each) + +local Screen = require('test.functional.ui.screen') +local call = helpers.call +local clear = helpers.clear +local feed = helpers.feed +local feed_command = helpers.feed_command +local funcs = helpers.funcs +local meths = helpers.meths +local eq = helpers.eq + +describe('visual line mode', function() + local screen + + it('redraws properly after scrolling with matchparen loaded and scrolloff=1', function() + clear{args={'-u', 'NORC'}} + screen = Screen.new(30, 7) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true}, + [2] = {background = Screen.colors.LightGrey}, + }) + + eq(1, meths.get_var('loaded_matchparen')) + feed_command('set scrolloff=1') + funcs.setline(1, {'a', 'b', 'c', 'd', 'e', '', '{', '}', '{', 'f', 'g', '}'}) + call('cursor', 5, 1) + + feed('V<c-d><c-d>') + screen:expect([[ + {2:{} | + {2:}} | + {2:{} | + {2:f} | + ^g | + } | + {1:-- VISUAL LINE --} | + ]]) + end) +end) diff --git a/test/functional/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua index 0c8bd2cdcc..826743b0ca 100644 --- a/test/functional/legacy/wordcount_spec.lua +++ b/test/functional/legacy/wordcount_spec.lua @@ -4,7 +4,7 @@ local helpers = require('test.functional.helpers')(after_each) local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, command = helpers.clear, helpers.command local eq, eval = helpers.eq, helpers.eval -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('wordcount', function() before_each(clear) @@ -14,7 +14,7 @@ describe('wordcount', function() insert([=[ RESULT test:]=]) - wait() + poke_eventloop() command('new') source([=[ @@ -127,7 +127,7 @@ describe('wordcount', function() -- -- Start visual mode quickly and select complete buffer. command('0') feed('V2jy<cr>') - wait() + poke_eventloop() command('set stl= ls=1') command('let log=DoRecordWin([3,99,0])') command('let log[1]=g:visual_stat') @@ -144,7 +144,7 @@ describe('wordcount', function() -- Start visual mode quickly and select complete buffer. command('0') feed('v$y<cr>') - wait() + poke_eventloop() command('set stl= ls=1') command('let log=DoRecordWin([3,99,0])') command('let log[1]=g:visual_stat') @@ -161,7 +161,7 @@ describe('wordcount', function() -- Start visual mode quickly and select complete buffer. command('2') feed('0v$y<cr>') - wait() + poke_eventloop() command('set stl= ls=1') command('let log=DoRecordWin([3,99,0])') command('let log[1]=g:visual_stat') diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 77f8189bb9..7e4de7c39a 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -3,10 +3,14 @@ local helpers = require('test.functional.helpers')(after_each) local command = helpers.command local meths = helpers.meths +local funcs = helpers.funcs local clear = helpers.clear local eq = helpers.eq +local fail = helpers.fail local exec_lua = helpers.exec_lua local feed = helpers.feed +local deepcopy = helpers.deepcopy +local expect_events = helpers.expect_events local origlines = {"original line 1", "original line 2", @@ -16,32 +20,37 @@ local origlines = {"original line 1", "original line 6", " indented line"} -describe('lua: buffer event callbacks', function() - before_each(function() - clear() - exec_lua([[ - local events = {} +local function attach_buffer(evname) + exec_lua([[ + local evname = ... + local events = {} - function test_register(bufnr, id, changedtick, utf_sizes) - local function callback(...) - table.insert(events, {id, ...}) - if test_unreg == id then - return true - end + function test_register(bufnr, id, changedtick, utf_sizes) + local function callback(...) + table.insert(events, {id, ...}) + if test_unreg == id then + return true end - local opts = {on_lines=callback, on_detach=callback, utf_sizes=utf_sizes} - if changedtick then - opts.on_changedtick = callback - end - vim.api.nvim_buf_attach(bufnr, false, opts) end - - function get_events() - local ret_events = events - events = {} - return ret_events + local opts = {[evname]=callback, on_detach=callback, utf_sizes=utf_sizes} + if changedtick then + opts.on_changedtick = callback end - ]]) + vim.api.nvim_buf_attach(bufnr, false, opts) + end + + function get_events() + local ret_events = events + events = {} + return ret_events + end + ]], evname) +end + +describe('lua buffer event callbacks: on_lines', function() + before_each(function() + clear() + attach_buffer('on_lines') end) @@ -62,7 +71,7 @@ describe('lua: buffer event callbacks', function() local function check_events(expected) local events = exec_lua("return get_events(...)" ) if utf_sizes then - -- this test case uses ASCII only, so sizes sshould be the same. + -- this test case uses ASCII only, so sizes should be the same. -- Unicode is tested below. for _, event in ipairs(expected) do event[9] = event[8] @@ -216,4 +225,282 @@ describe('lua: buffer event callbacks', function() eq(1, meths.get_var('listener_cursor_line')) end) + it('does not SEGFAULT when calling win_findbuf in on_detach', function() + + exec_lua[[ + local buf = vim.api.nvim_create_buf(false, false) + + vim.cmd"split" + vim.api.nvim_win_set_buf(0, buf) + + vim.api.nvim_buf_attach(buf, false, { + on_detach = function(_, buf) + vim.fn.win_findbuf(buf) + end + }) + ]] + + command("q!") + helpers.assert_alive() + end) + + it('#12718 lnume', function() + meths.buf_set_lines(0, 0, -1, true, {'1', '2', '3'}) + exec_lua([[ + vim.api.nvim_buf_attach(0, false, { + on_lines = function(...) + vim.api.nvim_set_var('linesev', { ... }) + end, + }) + ]]) + feed('1G0') + feed('y<C-v>2j') + feed('G0') + feed('p') + -- Is the last arg old_byte_size correct? Doesn't matter for this PR + eq(meths.get_var('linesev'), { "lines", 1, 4, 2, 3, 5, 4 }) + + feed('2G0') + feed('p') + eq(meths.get_var('linesev'), { "lines", 1, 5, 1, 4, 4, 8 }) + + feed('1G0') + feed('P') + eq(meths.get_var('linesev'), { "lines", 1, 6, 0, 3, 3, 9 }) + + end) +end) + +describe('lua: nvim_buf_attach on_bytes', function() + before_each(function() + clear() + attach_buffer('on_bytes') + end) + + -- verifying the sizes with nvim_buf_get_offset is nice (checks we cannot + -- assert the wrong thing), but masks errors with unflushed lines (as + -- nvim_buf_get_offset forces a flush of the memline). To be safe run the + -- test both ways. + local function setup_eventcheck(verify, start_txt) + meths.buf_set_lines(0, 0, -1, true, start_txt) + local shadow = deepcopy(start_txt) + local shadowbytes = table.concat(shadow, '\n') .. '\n' + -- TODO: while we are brewing the real strong coffe, + -- verify should check buf_get_offset after every check_events + if verify then + meths.buf_get_offset(0, meths.buf_line_count(0)) + end + exec_lua("return test_register(...)", 0, "test1",false, nil) + meths.buf_get_changedtick(0) + + local verify_name = "test1" + local function check_events(expected) + local events = exec_lua("return get_events(...)" ) + expect_events(expected, events, "byte updates") + + if not verify then + return + end + + for _, event in ipairs(events) do + for _, elem in ipairs(event) do + if type(elem) == "number" and elem < 0 then + fail(string.format("Received event has negative values")) + end + end + + if event[1] == verify_name and event[2] == "bytes" then + local _, _, _, _, _, _, start_byte, _, _, old_byte, _, _, new_byte = unpack(event) + local before = string.sub(shadowbytes, 1, start_byte) + -- no text in the tests will contain 0xff bytes (invalid UTF-8) + -- so we can use it as marker for unknown bytes + local unknown = string.rep('\255', new_byte) + local after = string.sub(shadowbytes, start_byte + old_byte + 1) + shadowbytes = before .. unknown .. after + end + end + + local text = meths.buf_get_lines(0, 0, -1, true) + local bytes = table.concat(text, '\n') .. '\n' + + eq(string.len(bytes), string.len(shadowbytes), '\non_bytes: total bytecount of buffer is wrong') + for i = 1, string.len(shadowbytes) do + local shadowbyte = string.sub(shadowbytes, i, i) + if shadowbyte ~= '\255' then + eq(string.sub(bytes, i, i), shadowbyte, i) + end + end + end + + return check_events + end + + -- Yes, we can do both + local function do_both(verify) + it('single and multiple join', function() + local check_events = setup_eventcheck(verify, origlines) + feed 'ggJ' + check_events { + {'test1', 'bytes', 1, 3, 0, 15, 15, 1, 0, 1, 0, 1, 1}; + } + + feed '3J' + check_events { + {'test1', 'bytes', 1, 5, 0, 31, 31, 1, 0, 1, 0, 1, 1}; + {'test1', 'bytes', 1, 5, 0, 47, 47, 1, 0, 1, 0, 1, 1}; + } + end) + + it('opening lines', function() + local check_events = setup_eventcheck(verify, origlines) + -- meths.buf_set_option(0, 'autoindent', true) + feed 'Go' + check_events { + { "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 1 }; + } + feed '<cr>' + check_events { + { "test1", "bytes", 1, 5, 7, 0, 114, 0, 0, 0, 1, 0, 1 }; + } + end) + + it('opening lines with autoindent', function() + local check_events = setup_eventcheck(verify, origlines) + meths.buf_set_option(0, 'autoindent', true) + feed 'Go' + check_events { + { "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 5 }; + } + feed '<cr>' + check_events { + { "test1", "bytes", 1, 4, 8, 0, 115, 0, 4, 4, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 7, 4, 118, 0, 0, 0, 1, 4, 5 }; + } + end) + + it('setline(num, line)', function() + local check_events = setup_eventcheck(verify, origlines) + funcs.setline(2, "babla") + check_events { + { "test1", "bytes", 1, 3, 1, 0, 16, 0, 15, 15, 0, 5, 5 }; + } + + funcs.setline(2, {"foo", "bar"}) + check_events { + { "test1", "bytes", 1, 4, 1, 0, 16, 0, 5, 5, 0, 3, 3 }; + { "test1", "bytes", 1, 5, 2, 0, 20, 0, 15, 15, 0, 3, 3 }; + } + + local buf_len = meths.buf_line_count(0) + funcs.setline(buf_len + 1, "baz") + check_events { + { "test1", "bytes", 1, 6, 7, 0, 90, 0, 0, 0, 1, 0, 4 }; + } + end) + + it('continuing comments with fo=or', function() + local check_events = setup_eventcheck(verify, {'// Comment'}) + meths.buf_set_option(0, 'formatoptions', 'ro') + meths.buf_set_option(0, 'filetype', 'c') + feed 'A<CR>' + check_events { + { "test1", "bytes", 1, 4, 0, 10, 10, 0, 0, 0, 1, 3, 4 }; + } + + feed '<ESC>' + check_events { + { "test1", "bytes", 1, 4, 1, 2, 13, 0, 1, 1, 0, 0, 0 }; + } + + feed 'ggo' -- goto first line to continue testing + check_events { + { "test1", "bytes", 1, 6, 1, 0, 11, 0, 0, 0, 1, 0, 4 }; + } + + feed '<CR>' + check_events { + { "test1", "bytes", 1, 6, 2, 2, 16, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 7, 1, 3, 14, 0, 0, 0, 1, 3, 4 }; + } + end) + + it('editing empty buffers', function() + local check_events = setup_eventcheck(verify, {}) + + feed 'ia' + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; + } + end) + + it("changing lines", function() + local check_events = setup_eventcheck(verify, origlines) + + feed "cc" + check_events { + { "test1", "bytes", 1, 4, 0, 0, 0, 0, 15, 15, 0, 0, 0 }; + } + + feed "<ESC>" + check_events {} + + feed "c3j" + check_events { + { "test1", "bytes", 1, 4, 1, 0, 1, 3, 0, 48, 0, 0, 0 }; + } + end) + + it("visual charwise paste", function() + local check_events = setup_eventcheck(verify, {'1234567890'}) + funcs.setreg('a', '___') + + feed '1G1|vll' + check_events {} + + feed '"ap' + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 0, 0, 0, 0, 0, 0, 0, 3, 3 }; + } + end) + + it('blockwise paste', function() + local check_events = setup_eventcheck(verify, {'1', '2', '3'}) + feed('1G0') + feed('y<C-v>2j') + feed('G0') + feed('p') + check_events { + { "test1", "bytes", 1, 3, 2, 1, 5, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 3, 3, 0, 7, 0, 0, 0, 0, 3, 3 }; + { "test1", "bytes", 1, 3, 4, 0, 10, 0, 0, 0, 0, 3, 3 }; + } + + feed('2G0') + feed('p') + check_events { + { "test1", "bytes", 1, 4, 1, 1, 3, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 4, 2, 1, 6, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 4, 3, 1, 10, 0, 0, 0, 0, 1, 1 }; + } + + feed('1G0') + feed('P') + check_events { + { "test1", "bytes", 1, 5, 0, 0, 0, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 5, 1, 0, 3, 0, 0, 0, 0, 1, 1 }; + { "test1", "bytes", 1, 5, 2, 0, 7, 0, 0, 0, 0, 1, 1 }; + } + + end) + end + + describe('(with verify) handles', function() + do_both(true) + end) + + describe('(without verify) handles', function() + do_both(false) + end) end) + diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index cbc3aee557..f2a1b7dede 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -43,7 +43,7 @@ describe(':lua command', function() eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) end) it('throws catchable errors', function() - eq([[Vim(lua):E5107: Error loading lua [string ":lua"]:1: unexpected symbol near ')']], + eq([[Vim(lua):E5107: Error loading lua [string ":lua"]:0: unexpected symbol near ')']], pcall_err(command, 'lua ()')) eq([[Vim(lua):E5108: Error executing lua [string ":lua"]:1: TEST]], exc_exec('lua error("TEST")')) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 75966393b1..2ec48777fd 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -477,14 +477,14 @@ describe('v:lua', function() eq(NIL, eval('v:lua.mymod.noisy("eval")')) eq("hey eval", meths.get_current_line()) - eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:10: attempt to call global 'nonexistent' (a nil value)", + eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)", pcall_err(eval, 'v:lua.mymod.crashy()')) end) it('works in :call', function() command(":call v:lua.mymod.noisy('command')") eq("hey command", meths.get_current_line()) - eq("Vim(call):E5108: Error executing lua [string \"<nvim>\"]:10: attempt to call global 'nonexistent' (a nil value)", + eq("Vim(call):E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)", pcall_err(command, 'call v:lua.mymod.crashy()')) end) diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua index aa3d55b06d..3526b64395 100644 --- a/test/functional/lua/treesitter_spec.lua +++ b/test/functional/lua/treesitter_spec.lua @@ -15,17 +15,16 @@ before_each(clear) describe('treesitter API', function() -- error tests not requiring a parser library it('handles missing language', function() - eq("Error executing lua: .../treesitter.lua: no parser for 'borklang' language", - pcall_err(exec_lua, "parser = vim.treesitter.create_parser(0, 'borklang')")) + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", + pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")) -- actual message depends on platform matches("Error executing lua: Failed to load parser: uv_dlopen: .+", pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')")) - eq("Error executing lua: .../treesitter.lua: no parser for 'borklang' language", + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')")) end) - end) describe('treesitter API with C parser', function() @@ -127,6 +126,58 @@ void ui_refresh(void) } }]] + it('allows to iterate over nodes children', function() + if not check_parser() then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse():root():child(0) + + res = {} + for node, field in func_node:iter_children() do + table.insert(res, {node:type(), field}) + end + return res + ]]) + + eq({ + {"primitive_type", "type"}, + {"function_declarator", "declarator"}, + {"compound_statement", "body"} + }, res) + end) + + it('allows to get a child by field', function() + if not check_parser() then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse():root():child(0) + + local res = {} + for _, node in ipairs(func_node:field("type")) do + table.insert(res, {node:type(), node:range()}) + end + return res + ]]) + + eq({{ "primitive_type", 0, 0, 0, 4 }}, res) + + local res_fail = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + return #func_node:field("foo") == 0 + ]]) + + assert(res_fail) + end) + local query = [[ ((call_expression function: (identifier) @minfunc (argument_list (identifier) @min_id)) (eq? @minfunc "MIN")) "for" @keyword @@ -134,6 +185,16 @@ void ui_refresh(void) (field_expression argument: (identifier) @fieldarg) ]] + it("supports runtime queries", function() + if not check_parser() then return end + + local ret = exec_lua [[ + return require"vim.treesitter.query".get_query("c", "highlights").captures[1] + ]] + + eq('variable', ret) + end) + it('support query and iter by capture', function() if not check_parser() then return end @@ -198,6 +259,82 @@ void ui_refresh(void) }, res) end) + it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() + if not check_parser() then return end + + insert('char* astring = "Hello World!";') + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", '((_) @quote (vim-match? @quote "^\\"$")) ((_) @quote (lua-match? @quote "^\\"$"))') + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse() + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0, 0, 1) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid,node in pairs(match) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end + table.insert(res, {pattern, mrepr}) + end + return res + ]]) + + eq({ + { 1, { { "quote", '"', 0, 16, 0, 17 } } }, + { 2, { { "quote", '"', 0, 16, 0, 17 } } }, + { 1, { { "quote", '"', 0, 29, 0, 30 } } }, + { 2, { { "quote", '"', 0, 29, 0, 30 } } }, + }, res) + end) + + it('allows to add predicates', function() + insert([[ + int main(void) { + return 0; + } + ]]) + + local custom_query = "((identifier) @main (#is-main? @main))" + + local res = exec_lua([[ + local query = require"vim.treesitter.query" + + local function is_main(match, pattern, bufnr, predicate) + local node = match[ predicate[2] ] + + return query.get_node_text(node, bufnr) + end + + local parser = vim.treesitter.get_parser(0, "c") + + query.add_predicate("is-main?", is_main) + + local query = query.parse_query("c", ...) + + local nodes = {} + for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do + table.insert(nodes, {node:range()}) + end + + return nodes + ]], custom_query) + + eq({{0, 4, 0, 8}}, res) + + local res_list = exec_lua[[ + local query = require'vim.treesitter.query' + + local list = query.list_predicates() + + table.sort(list) + + return list + ]] + + eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) + end) + it('supports highlighting', function() if not check_parser() then return end @@ -243,10 +380,10 @@ static int nlua_schedule(lua_State *const lstate) (primitive_type) @type (sized_type_specifier) @type -; defaults to very magic syntax, for best compatibility -((identifier) @Identifier (#match? @Identifier "^l(u)a_")) -; still support \M etc prefixes -((identifier) @Constant (#match? @Constant "\M^\[A-Z_]\+$")) +; Use lua regexes +((identifier) @Identifier (#contains? @Identifier "lua_")) +((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$")) +((identifier) @Normal (#vim-match? @Constant "^lstate$")) ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right)) @@ -292,13 +429,14 @@ static int nlua_schedule(lua_State *const lstate) ]]} exec_lua([[ - local TSHighlighter = vim.treesitter.TSHighlighter + local parser = vim.treesitter.get_parser(0, "c") + local highlighter = vim.treesitter.highlighter local query = ... - test_hl = TSHighlighter.new(query, 0, "c") + test_hl = highlighter.new(parser, query) ]], hl_query) screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | || {6:lstate} != {6:lstate}) { | @@ -306,9 +444,9 @@ static int nlua_schedule(lua_State *const lstate) {4:return} {11:lua_error}(lstate); | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | ^} | @@ -317,10 +455,33 @@ static int nlua_schedule(lua_State *const lstate) | ]]} + feed("5Goc<esc>dd") + + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + } | + {1:~ }| + {1:~ }| + | + ]]} + feed('7Go*/<esc>') screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | || {6:lstate} != {6:lstate}) { | @@ -329,9 +490,9 @@ static int nlua_schedule(lua_State *const lstate) {8:*^/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | } | @@ -342,7 +503,7 @@ static int nlua_schedule(lua_State *const lstate) feed('3Go/*<esc>') screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {2:/^*} | {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | @@ -352,9 +513,9 @@ static int nlua_schedule(lua_State *const lstate) {2:*/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | {8:}} | @@ -365,7 +526,7 @@ static int nlua_schedule(lua_State *const lstate) feed("~") screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queu^E} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {2:/*} | {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | @@ -375,9 +536,9 @@ static int nlua_schedule(lua_State *const lstate) {2:*/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | {8:}} | @@ -388,7 +549,7 @@ static int nlua_schedule(lua_State *const lstate) feed("re") screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queu^e} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {2:/*} | {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | @@ -398,9 +559,9 @@ static int nlua_schedule(lua_State *const lstate) {2:*/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | {8:}} | @@ -408,6 +569,72 @@ static int nlua_schedule(lua_State *const lstate) ]]} end) + it("supports highlighting with custom parser", function() + if not check_parser() then return end + + local screen = Screen.new(65, 18) + screen:attach() + screen:set_default_attr_ids({ {bold = true, foreground = Screen.colors.SeaGreen4} }) + + insert(test_text) + + screen:expect{ grid= [[ + int width = INT_MAX, height = INT_MAX; | + bool ext_widgets[kUIExtCount]; | + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool inclusive = ui_override(); | + for (size_t i = 0; i < ui_count; i++) { | + UI *ui = uis[i]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + query = vim.treesitter.parse_query("c", "(declaration) @decl") + + local nodes = {} + for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do + table.insert(nodes, node) + end + + parser:set_included_ranges(nodes) + + local hl = vim.treesitter.highlighter.new(parser, "(identifier) @type") + ]]) + + screen:expect{ grid = [[ + int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; | + bool {1:ext_widgets}[{1:kUIExtCount}]; | + for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool {1:inclusive} = {1:ui_override}(); | + for (size_t {1:i} = 0; i < ui_count; i++) { | + UI *{1:ui} = {1:uis}[{1:i}]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + end) + it('inspects language', function() if not check_parser() then return end @@ -453,23 +680,29 @@ static int nlua_schedule(lua_State *const lstate) insert(test_text) - local res = exec_lua([[ + local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") return { parser:parse():root():range() } - ]]) + ]] eq({0, 0, 19, 0}, res) -- The following sets the included ranges for the current parser -- As stated here, this only includes the function (thus the whole buffer, without the last line) - local res2 = exec_lua([[ + local res2 = exec_lua [[ local root = parser:parse():root() parser:set_included_ranges({root:child(0)}) parser.valid = false return { parser:parse():root():range() } - ]]) + ]] eq({0, 0, 18, 1}, res2) + + local range = exec_lua [[ + return parser:included_ranges() + ]] + + eq(range, { { 0, 0, 18, 1 } }) end) it("allows to set complex ranges", function() if not check_parser() then return end @@ -477,7 +710,7 @@ static int nlua_schedule(lua_State *const lstate) insert(test_text) - local res = exec_lua([[ + local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") query = vim.treesitter.parse_query("c", "(declaration) @decl") @@ -495,7 +728,7 @@ static int nlua_schedule(lua_State *const lstate) table.insert(res, { root:named_child(i):range() }) end return res - ]]) + ]] eq({ { 2, 2, 2, 40 }, @@ -509,4 +742,35 @@ static int nlua_schedule(lua_State *const lstate) { 10, 5, 10, 20 }, { 14, 9, 14, 27 } }, res) end) + + it("allows to create string parsers", function() + local ret = exec_lua [[ + local parser = vim.treesitter.get_string_parser("int foo = 42;", "c") + return { parser:parse():root():range() } + ]] + + eq({ 0, 0, 0, 13 }, ret) + end) + + it("allows to run queries with string parsers", function() + local txt = [[ + int foo = 42; + int bar = 13; + ]] + + local ret = exec_lua([[ + local str = ... + local parser = vim.treesitter.get_string_parser(str, "c") + + local nodes = {} + local query = vim.treesitter.parse_query("c", '((identifier) @id (eq? @id "foo"))') + + for _, node in query:iter_captures(parser:parse():root(), str, 0, 2) do + table.insert(nodes, { node:range() }) + end + + return nodes]], txt) + + eq({ {0, 10, 0, 13} }, ret) + end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 9b2697b3c2..1cbf6b21e3 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -4,13 +4,14 @@ local Screen = require('test.functional.ui.screen') local funcs = helpers.funcs local meths = helpers.meths +local dedent = helpers.dedent local command = helpers.command local clear = helpers.clear local eq = helpers.eq local ok = helpers.ok local eval = helpers.eval local feed = helpers.feed -local pcall_err = helpers.pcall_err +local pcall_err_withfile = helpers.pcall_err_withfile local exec_lua = helpers.exec_lua local matches = helpers.matches local source = helpers.source @@ -128,8 +129,8 @@ describe('lua stdlib', function() eq(false, funcs.luaeval('vim.startswith("123", "2")')) eq(false, funcs.luaeval('vim.startswith("123", "1234")')) - eq("string", type(pcall_err(funcs.luaeval, 'vim.startswith("123", nil)'))) - eq("string", type(pcall_err(funcs.luaeval, 'vim.startswith(nil, "123")'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.startswith("123", nil)'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.startswith(nil, "123")'))) end) it('vim.endswith', function() @@ -142,8 +143,8 @@ describe('lua stdlib', function() eq(false, funcs.luaeval('vim.endswith("123", "2")')) eq(false, funcs.luaeval('vim.endswith("123", "1234")')) - eq("string", type(pcall_err(funcs.luaeval, 'vim.endswith("123", nil)'))) - eq("string", type(pcall_err(funcs.luaeval, 'vim.endswith(nil, "123")'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.endswith("123", nil)'))) + eq("string", type(pcall_err_withfile(funcs.luaeval, 'vim.endswith(nil, "123")'))) end) it("vim.str_utfindex/str_byteindex", function() @@ -182,10 +183,10 @@ describe('lua stdlib', function() eq({"yy","xx"}, exec_lua("return test_table")) -- Validates args. - eq('Error executing lua: vim.schedule: expected function', - pcall_err(exec_lua, "vim.schedule('stringly')")) - eq('Error executing lua: vim.schedule: expected function', - pcall_err(exec_lua, "vim.schedule()")) + eq('.../helpers.lua:0: Error executing lua: vim.schedule: expected function', + pcall_err_withfile(exec_lua, "vim.schedule('stringly')")) + eq('.../helpers.lua:0: Error executing lua: vim.schedule: expected function', + pcall_err_withfile(exec_lua, "vim.schedule()")) exec_lua([[ vim.schedule(function() @@ -257,17 +258,29 @@ describe('lua stdlib', function() } for _, t in ipairs(loops) do - matches(".*Infinite loop detected", pcall_err(split, t[1], t[2])) + matches(".*Infinite loop detected", pcall_err_withfile(split, t[1], t[2])) end -- Validates args. eq(true, pcall(split, 'string', 'string')) - eq('Error executing lua: .../shared.lua: s: expected string, got number', - pcall_err(split, 1, 'string')) - eq('Error executing lua: .../shared.lua: sep: expected string, got number', - pcall_err(split, 'string', 1)) - eq('Error executing lua: .../shared.lua: plain: expected boolean, got number', - pcall_err(split, 'string', 'string', 1)) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: s: expected string, got number + stack traceback: + shared.lua:0: in function 'gsplit' + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(split, 1, 'string')) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: sep: expected string, got number + stack traceback: + shared.lua:0: in function 'gsplit' + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(split, 'string', 1)) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: plain: expected boolean, got number + stack traceback: + shared.lua:0: in function 'gsplit' + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(split, 'string', 'string', 1)) end) it('vim.trim', function() @@ -287,8 +300,11 @@ describe('lua stdlib', function() end -- Validates args. - eq('Error executing lua: .../shared.lua: s: expected string, got number', - pcall_err(trim, 2)) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: s: expected string, got number + stack traceback: + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(trim, 2)) end) it('vim.inspect', function() @@ -353,8 +369,8 @@ describe('lua stdlib', function() return t1.f() ~= t2.f() ]])) - eq('Error executing lua: .../shared.lua: Cannot deepcopy object of type thread', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: Cannot deepcopy object of type thread', + pcall_err_withfile(exec_lua, [[ local thread = coroutine.create(function () return 0 end) local t = {thr = thread} vim.deepcopy(t) @@ -366,8 +382,11 @@ describe('lua stdlib', function() eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) -- Validates args. - eq('Error executing lua: .../shared.lua: s: expected string, got number', - pcall_err(exec_lua, [[return vim.pesc(2)]])) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: s: expected string, got number + stack traceback: + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(exec_lua, [[return vim.pesc(2)]])) end) it('vim.tbl_keys', function() @@ -491,20 +510,20 @@ describe('lua stdlib', function() return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1 ]])) - eq('Error executing lua: .../shared.lua: invalid "behavior": nil', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: invalid "behavior": nil', + pcall_err_withfile(exec_lua, [[ return vim.tbl_extend() ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 1, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 1, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_extend("keep") ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 2, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 2, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_extend("keep", {}) ]]) ) @@ -579,20 +598,20 @@ describe('lua stdlib', function() return vim.tbl_islist(c) and count == 0 ]])) - eq('Error executing lua: .../shared.lua: invalid "behavior": nil', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: invalid "behavior": nil', + pcall_err_withfile(exec_lua, [[ return vim.tbl_deep_extend() ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 1, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 1, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_deep_extend("keep") ]]) ) - eq('Error executing lua: .../shared.lua: wrong number of arguments (given 2, expected at least 3)', - pcall_err(exec_lua, [[ + eq('.../helpers.lua:0: Error executing lua: shared.lua:0: wrong number of arguments (given 2, expected at least 3)', + pcall_err_withfile(exec_lua, [[ return vim.tbl_deep_extend("keep", {}) ]]) ) @@ -624,8 +643,11 @@ describe('lua stdlib', function() it('vim.list_extend', function() eq({1,2,3}, exec_lua [[ return vim.list_extend({1}, {2,3}) ]]) - eq('Error executing lua: .../shared.lua: src: expected table, got nil', - pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]])) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: shared.lua:0: src: expected table, got nil + stack traceback: + shared.lua:0: in function <shared.lua:0>]]), + pcall_err_withfile(exec_lua, [[ return vim.list_extend({1}, nil) ]])) eq({1,2}, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]]) eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]]) eq({2}, exec_lua [[ return vim.list_extend({}, {2;a=1}, 1) ]]) @@ -648,8 +670,8 @@ describe('lua stdlib', function() assert(vim.deep_equal(a, { A = 1; [1] = 'A'; })) vim.tbl_add_reverse_lookup(a) ]] - matches('Error executing lua: .../shared.lua: The reverse lookup found an existing value for "[1A]" while processing key "[1A]"', - pcall_err(exec_lua, code)) + matches('.../helpers.lua:0: Error executing lua: shared.lua:0: The reverse lookup found an existing value for "[1A]" while processing key "[1A]"', + pcall_err_withfile(exec_lua, code)) end) it('vim.call, vim.fn', function() @@ -820,34 +842,77 @@ describe('lua stdlib', function() exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") - eq("Error executing lua: .../shared.lua: 1: expected table, got number", - pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) - eq("Error executing lua: .../shared.lua: invalid type name: x", - pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) - eq("Error executing lua: .../shared.lua: invalid type name: 1", - pcall_err(exec_lua, "vim.validate{ arg1={ 1, 1 }}")) - eq("Error executing lua: .../shared.lua: invalid type name: nil", - pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: opt[1]: expected table, got number + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ 1, 'x' }")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: invalid type name: x + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: invalid type name: 1 + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ 1, 1 }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: invalid type name: nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ 1 }}")) -- Validated parameters are required by default. - eq("Error executing lua: .../shared.lua: arg1: expected string, got nil", - pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ nil, 's' }}")) -- Explicitly required. - eq("Error executing lua: .../shared.lua: arg1: expected string, got nil", - pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")) - - eq("Error executing lua: .../shared.lua: arg1: expected table, got number", - pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}")) - eq("Error executing lua: .../shared.lua: arg2: expected string, got number", - pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) - eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", - pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) - eq("Error executing lua: .../shared.lua: arg2: expected string, got nil", - pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) - eq("Error executing lua: .../shared.lua: arg1: expected even number, got 3", - pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) - eq("Error executing lua: .../shared.lua: arg1: expected ?, got 3", - pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{ arg1={ nil, 's', false }}")) + + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected table, got number + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={1, 't'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg2: expected string, got number + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg2: expected string, got nil + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected even number, got 3 + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}")) + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3 + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}")) + + -- Pass an additional message back. + eq(dedent([[ + .../helpers.lua:0: Error executing lua: [string "<nvim>"]:0: arg1: expected ?, got 3. Info: TEST_MSG + stack traceback: + [string "<nvim>"]:0: in main chunk]]), + pcall_err_withfile(exec_lua, "vim.validate{arg1={3, function(a) return a == 1, 'TEST_MSG' end}}")) end) it('vim.is_callable', function() @@ -992,10 +1057,10 @@ describe('lua stdlib', function() ]] eq('', funcs.luaeval "vim.bo.filetype") eq(true, funcs.luaeval "vim.bo[BUF].modifiable") - matches("^Error executing lua: .*: Invalid option name: 'nosuchopt'$", - pcall_err(exec_lua, 'return vim.bo.nosuchopt')) - matches("^Error executing lua: .*: Expected lua string$", - pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) + matches("^.../helpers.lua:0: Error executing lua: .*: Invalid option name: 'nosuchopt'$", + pcall_err_withfile(exec_lua, 'return vim.bo.nosuchopt')) + matches("^.../helpers.lua:0: Error executing lua: .*: Expected lua string$", + pcall_err_withfile(exec_lua, 'return vim.bo[0][0].autoread')) end) it('vim.wo', function() @@ -1011,10 +1076,10 @@ describe('lua stdlib', function() eq(0, funcs.luaeval "vim.wo.cole") eq(0, funcs.luaeval "vim.wo[0].cole") eq(0, funcs.luaeval "vim.wo[1001].cole") - matches("^Error executing lua: .*: Invalid option name: 'notanopt'$", - pcall_err(exec_lua, 'return vim.wo.notanopt')) - matches("^Error executing lua: .*: Expected lua string$", - pcall_err(exec_lua, 'return vim.wo[0][0].list')) + matches("^.../helpers.lua:0: Error executing lua: .*: Invalid option name: 'notanopt'$", + pcall_err_withfile(exec_lua, 'return vim.wo.notanopt')) + matches("^.../helpers.lua:0: Error executing lua: .*: Expected lua string$", + pcall_err_withfile(exec_lua, 'return vim.wo[0][0].list')) eq(2, funcs.luaeval "vim.wo[1000].cole") exec_lua [[ vim.wo[1000].cole = 0 @@ -1068,6 +1133,104 @@ describe('lua stdlib', function() eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]]) end) + describe('vim.execute_on_keystroke', function() + it('should keep track of keystrokes', function() + helpers.insert([[hello world ]]) + + exec_lua [[ + KeysPressed = {} + + vim.register_keystroke_callback(function(buf) + if buf:byte() == 27 then + buf = "<ESC>" + end + + table.insert(KeysPressed, buf) + end) + ]] + + helpers.insert([[next 🤦 lines å ]]) + + -- It has escape in the keys pressed + eq('inext 🤦 lines å <ESC>', exec_lua [[return table.concat(KeysPressed, '')]]) + end) + + it('should allow removing trackers.', function() + helpers.insert([[hello world]]) + + exec_lua [[ + KeysPressed = {} + + return vim.register_keystroke_callback(function(buf) + if buf:byte() == 27 then + buf = "<ESC>" + end + + table.insert(KeysPressed, buf) + end, vim.api.nvim_create_namespace("logger")) + ]] + + helpers.insert([[next lines]]) + + exec_lua("vim.register_keystroke_callback(nil, vim.api.nvim_create_namespace('logger'))") + + helpers.insert([[more lines]]) + + -- It has escape in the keys pressed + eq('inext lines<ESC>', exec_lua [[return table.concat(KeysPressed, '')]]) + end) + + it('should not call functions that error again.', function() + helpers.insert([[hello world]]) + + exec_lua [[ + KeysPressed = {} + + return vim.register_keystroke_callback(function(buf) + if buf:byte() == 27 then + buf = "<ESC>" + end + + table.insert(KeysPressed, buf) + + if buf == 'l' then + error("Dumb Error") + end + end) + ]] + + helpers.insert([[next lines]]) + helpers.insert([[more lines]]) + + -- Only the first letter gets added. After that we remove the callback + eq('inext l', exec_lua [[ return table.concat(KeysPressed, '') ]]) + end) + + it('should process mapped keys, not unmapped keys', function() + exec_lua [[ + KeysPressed = {} + + vim.cmd("inoremap hello world") + + vim.register_keystroke_callback(function(buf) + if buf:byte() == 27 then + buf = "<ESC>" + end + + table.insert(KeysPressed, buf) + end) + ]] + + helpers.insert("hello") + + local next_status = exec_lua [[ + return table.concat(KeysPressed, '') + ]] + + eq("iworld<ESC>", next_status) + end) + end) + describe('vim.wait', function() before_each(function() exec_lua[[ @@ -1116,6 +1279,23 @@ describe('lua stdlib', function() ]]) end) + it('should not process non-fast events when commanded', function() + eq({wait_result = false}, exec_lua[[ + start_time = get_time() + + vim.g.timer_result = false + timer = vim.loop.new_timer() + timer:start(100, 0, vim.schedule_wrap(function() + vim.g.timer_result = true + end)) + + wait_result = vim.wait(300, function() return vim.g.timer_result end, nil, true) + + return { + wait_result = wait_result, + } + ]]) + end) it('should work with vim.defer_fn', function() eq({time = true, wait_result = true}, exec_lua[[ start_time = get_time() @@ -1130,22 +1310,38 @@ describe('lua stdlib', function() ]]) end) - it('should require functions to be passed', function() + it('should not crash when callback errors', function() local pcall_result = exec_lua [[ - return {pcall(function() vim.wait(1000, 13) end)} + return {pcall(function() vim.wait(1000, function() error("As Expected") end) end)} ]] eq(pcall_result[1], false) - matches('condition must be a function', pcall_result[2]) + matches('As Expected', pcall_result[2]) end) - it('should not crash when callback errors', function() + it('if callback is passed, it must be a function', function() local pcall_result = exec_lua [[ - return {pcall(function() vim.wait(1000, function() error("As Expected") end) end)} + return {pcall(function() vim.wait(1000, 13) end)} ]] eq(pcall_result[1], false) - matches('As Expected', pcall_result[2]) + matches('if passed, condition must be a function', pcall_result[2]) + end) + + it('should allow waiting with no callback, explicit', function() + eq(true, exec_lua [[ + local start_time = vim.loop.hrtime() + vim.wait(50, nil) + return vim.loop.hrtime() - start_time > 25000 + ]]) + end) + + it('should allow waiting with no callback, implicit', function() + eq(true, exec_lua [[ + local start_time = vim.loop.hrtime() + vim.wait(50) + return vim.loop.hrtime() - start_time > 25000 + ]]) end) it('should call callbacks exactly once if they return true immediately', function() @@ -1232,4 +1428,29 @@ describe('lua stdlib', function() eq(false, pcall_result) end) end) + + describe('vim.api.nvim_buf_call', function() + it('can access buf options', function() + local buf1 = meths.get_current_buf() + local buf2 = exec_lua [[ + buf2 = vim.api.nvim_create_buf(false, true) + return buf2 + ]] + + eq(false, meths.buf_get_option(buf1, 'autoindent')) + eq(false, meths.buf_get_option(buf2, 'autoindent')) + + local val = exec_lua [[ + return vim.api.nvim_buf_call(buf2, function() + vim.cmd "set autoindent" + return vim.api.nvim_get_current_buf() + end) + ]] + + eq(false, meths.buf_get_option(buf1, 'autoindent')) + eq(true, meths.buf_get_option(buf2, 'autoindent')) + eq(buf1, meths.get_current_buf()) + eq(buf2, val) + end) + end) end) diff --git a/test/functional/normal/meta_key_spec.lua b/test/functional/normal/meta_key_spec.lua new file mode 100644 index 0000000000..9f9fad67d2 --- /dev/null +++ b/test/functional/normal/meta_key_spec.lua @@ -0,0 +1,22 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local command = helpers.command +local expect = helpers.expect + +describe('meta-keys-in-normal-mode', function() + before_each(function() + clear() + end) + + it('ALT/META', function() + -- Unmapped ALT-chords behave as Esc+c + insert('hello') + feed('0<A-x><M-x>') + expect('llo') + -- Mapped ALT-chord behaves as mapped. + command('nnoremap <M-l> Ameta-l<Esc>') + command('nnoremap <A-j> Aalt-j<Esc>') + feed('<A-j><M-l>') + expect('lloalt-jmeta-l') + end) +end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 11ce26410d..92d077ed14 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -290,9 +290,6 @@ describe('XDG-based defaults', function() end) end) - -- TODO(jkeyes): tests below fail on win32 because of path separator. - if helpers.pending_win32(pending) then return end - local function vimruntime_and_libdir() local vimruntime = eval('$VIMRUNTIME') -- libdir is hard to calculate reliably across various ci platforms @@ -301,71 +298,78 @@ describe('XDG-based defaults', function() return vimruntime, libdir end + local env_sep = iswin() and ';' or ':' + local data_dir = iswin() and 'nvim-data' or 'nvim' + local root_path = iswin() and 'C:' or '' + describe('with too long XDG variables', function() before_each(function() clear({env={ - XDG_CONFIG_HOME=('/x'):rep(4096), - XDG_CONFIG_DIRS=(('/a'):rep(2048) - .. ':' .. ('/b'):rep(2048) - .. (':/c'):rep(512)), - XDG_DATA_HOME=('/X'):rep(4096), - XDG_DATA_DIRS=(('/A'):rep(2048) - .. ':' .. ('/B'):rep(2048) - .. (':/C'):rep(512)), + XDG_CONFIG_HOME=(root_path .. ('/x'):rep(4096)), + XDG_CONFIG_DIRS=(root_path .. ('/a'):rep(2048) + .. env_sep.. root_path .. ('/b'):rep(2048) + .. (env_sep .. root_path .. '/c'):rep(512)), + XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)), + XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048) + .. env_sep .. root_path .. ('/B'):rep(2048) + .. (env_sep .. root_path .. '/C'):rep(512)), }}) end) it('are correctly set', function() local vimruntime, libdir = vimruntime_and_libdir() - eq((('/x'):rep(4096) .. '/nvim' - .. ',' .. ('/a'):rep(2048) .. '/nvim' - .. ',' .. ('/b'):rep(2048) .. '/nvim' - .. (',' .. '/c/nvim'):rep(512) - .. ',' .. ('/X'):rep(4096) .. '/nvim/site' - .. ',' .. ('/A'):rep(2048) .. '/nvim/site' - .. ',' .. ('/B'):rep(2048) .. '/nvim/site' - .. (',' .. '/C/nvim/site'):rep(512) + eq(((root_path .. ('/x'):rep(4096) .. '/nvim' + .. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim' + .. ',' .. root_path .. ('/b'):rep(2048) .. '/nvim' + .. (',' .. root_path .. '/c/nvim'):rep(512) + .. ',' .. root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/site' + .. ',' .. root_path .. ('/A'):rep(2048) .. '/nvim/site' + .. ',' .. root_path .. ('/B'):rep(2048) .. '/nvim/site' + .. (',' .. root_path .. '/C/nvim/site'):rep(512) .. ',' .. vimruntime .. ',' .. libdir - .. (',' .. '/C/nvim/site/after'):rep(512) - .. ',' .. ('/B'):rep(2048) .. '/nvim/site/after' - .. ',' .. ('/A'):rep(2048) .. '/nvim/site/after' - .. ',' .. ('/X'):rep(4096) .. '/nvim/site/after' - .. (',' .. '/c/nvim/after'):rep(512) - .. ',' .. ('/b'):rep(2048) .. '/nvim/after' - .. ',' .. ('/a'):rep(2048) .. '/nvim/after' - .. ',' .. ('/x'):rep(4096) .. '/nvim/after' - ), meths.get_option('runtimepath')) + .. (',' .. root_path .. '/C/nvim/site/after'):rep(512) + .. ',' .. root_path .. ('/B'):rep(2048) .. '/nvim/site/after' + .. ',' .. root_path .. ('/A'):rep(2048) .. '/nvim/site/after' + .. ',' .. root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/site/after' + .. (',' .. root_path .. '/c/nvim/after'):rep(512) + .. ',' .. root_path .. ('/b'):rep(2048) .. '/nvim/after' + .. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim/after' + .. ',' .. root_path .. ('/x'):rep(4096) .. '/nvim/after' + ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) meths.command('set runtimepath&') meths.command('set backupdir&') meths.command('set directory&') meths.command('set undodir&') meths.command('set viewdir&') - eq((('/x'):rep(4096) .. '/nvim' - .. ',' .. ('/a'):rep(2048) .. '/nvim' - .. ',' .. ('/b'):rep(2048) .. '/nvim' - .. (',' .. '/c/nvim'):rep(512) - .. ',' .. ('/X'):rep(4096) .. '/nvim/site' - .. ',' .. ('/A'):rep(2048) .. '/nvim/site' - .. ',' .. ('/B'):rep(2048) .. '/nvim/site' - .. (',' .. '/C/nvim/site'):rep(512) + eq(((root_path .. ('/x'):rep(4096) .. '/nvim' + .. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim' + .. ',' .. root_path .. ('/b'):rep(2048) .. '/nvim' + .. (',' .. root_path .. '/c/nvim'):rep(512) + .. ',' .. root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/site' + .. ',' .. root_path .. ('/A'):rep(2048) .. '/nvim/site' + .. ',' .. root_path .. ('/B'):rep(2048) .. '/nvim/site' + .. (',' .. root_path .. '/C/nvim/site'):rep(512) .. ',' .. vimruntime .. ',' .. libdir - .. (',' .. '/C/nvim/site/after'):rep(512) - .. ',' .. ('/B'):rep(2048) .. '/nvim/site/after' - .. ',' .. ('/A'):rep(2048) .. '/nvim/site/after' - .. ',' .. ('/X'):rep(4096) .. '/nvim/site/after' - .. (',' .. '/c/nvim/after'):rep(512) - .. ',' .. ('/b'):rep(2048) .. '/nvim/after' - .. ',' .. ('/a'):rep(2048) .. '/nvim/after' - .. ',' .. ('/x'):rep(4096) .. '/nvim/after' - ), meths.get_option('runtimepath')) - eq('.,' .. ('/X'):rep(4096) .. '/nvim/backup', - meths.get_option('backupdir')) - eq(('/X'):rep(4096) .. '/nvim/swap//', meths.get_option('directory')) - eq(('/X'):rep(4096) .. '/nvim/undo', meths.get_option('undodir')) - eq(('/X'):rep(4096) .. '/nvim/view', meths.get_option('viewdir')) + .. (',' .. root_path .. '/C/nvim/site/after'):rep(512) + .. ',' .. root_path .. ('/B'):rep(2048) .. '/nvim/site/after' + .. ',' .. root_path .. ('/A'):rep(2048) .. '/nvim/site/after' + .. ',' .. root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/site/after' + .. (',' .. root_path .. '/c/nvim/after'):rep(512) + .. ',' .. root_path .. ('/b'):rep(2048) .. '/nvim/after' + .. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim/after' + .. ',' .. root_path .. ('/x'):rep(4096) .. '/nvim/after' + ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) + eq('.,' .. root_path .. ('/X'):rep(4096).. '/' .. data_dir .. '/backup', + (meths.get_option('backupdir'):gsub('\\', '/'))) + eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/swap//', + (meths.get_option('directory')):gsub('\\', '/')) + eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/undo', + (meths.get_option('undodir')):gsub('\\', '/')) + eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/view', + (meths.get_option('viewdir')):gsub('\\', '/')) end) end) @@ -381,53 +385,61 @@ describe('XDG-based defaults', function() it('are not expanded', function() local vimruntime, libdir = vimruntime_and_libdir() - eq(('$XDG_DATA_HOME/nvim' + eq((('$XDG_DATA_HOME/nvim' .. ',$XDG_DATA_DIRS/nvim' - .. ',$XDG_CONFIG_HOME/nvim/site' + .. ',$XDG_CONFIG_HOME/' .. data_dir .. '/site' .. ',$XDG_CONFIG_DIRS/nvim/site' .. ',' .. vimruntime .. ',' .. libdir .. ',$XDG_CONFIG_DIRS/nvim/site/after' - .. ',$XDG_CONFIG_HOME/nvim/site/after' + .. ',$XDG_CONFIG_HOME/' .. data_dir .. '/site/after' .. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_HOME/nvim/after' - ), meths.get_option('runtimepath')) + ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) meths.command('set runtimepath&') meths.command('set backupdir&') meths.command('set directory&') meths.command('set undodir&') meths.command('set viewdir&') - eq(('$XDG_DATA_HOME/nvim' + eq((('$XDG_DATA_HOME/nvim' .. ',$XDG_DATA_DIRS/nvim' - .. ',$XDG_CONFIG_HOME/nvim/site' + .. ',$XDG_CONFIG_HOME/' .. data_dir .. '/site' .. ',$XDG_CONFIG_DIRS/nvim/site' .. ',' .. vimruntime .. ',' .. libdir .. ',$XDG_CONFIG_DIRS/nvim/site/after' - .. ',$XDG_CONFIG_HOME/nvim/site/after' + .. ',$XDG_CONFIG_HOME/' .. data_dir .. '/site/after' .. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_HOME/nvim/after' - ), meths.get_option('runtimepath')) - eq('.,$XDG_CONFIG_HOME/nvim/backup', meths.get_option('backupdir')) - eq('$XDG_CONFIG_HOME/nvim/swap//', meths.get_option('directory')) - eq('$XDG_CONFIG_HOME/nvim/undo', meths.get_option('undodir')) - eq('$XDG_CONFIG_HOME/nvim/view', meths.get_option('viewdir')) + ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) + eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup'), + meths.get_option('backupdir'):gsub('\\', '/')) + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'), + meths.get_option('directory'):gsub('\\', '/')) + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo'), + meths.get_option('undodir'):gsub('\\', '/')) + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view'), + meths.get_option('viewdir'):gsub('\\', '/')) meths.command('set all&') eq(('$XDG_DATA_HOME/nvim' .. ',$XDG_DATA_DIRS/nvim' - .. ',$XDG_CONFIG_HOME/nvim/site' + .. ',$XDG_CONFIG_HOME/' .. data_dir .. '/site' .. ',$XDG_CONFIG_DIRS/nvim/site' .. ',' .. vimruntime .. ',' .. libdir .. ',$XDG_CONFIG_DIRS/nvim/site/after' - .. ',$XDG_CONFIG_HOME/nvim/site/after' + .. ',$XDG_CONFIG_HOME/' .. data_dir .. '/site/after' .. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_HOME/nvim/after' - ), meths.get_option('runtimepath')) - eq('.,$XDG_CONFIG_HOME/nvim/backup', meths.get_option('backupdir')) - eq('$XDG_CONFIG_HOME/nvim/swap//', meths.get_option('directory')) - eq('$XDG_CONFIG_HOME/nvim/undo', meths.get_option('undodir')) - eq('$XDG_CONFIG_HOME/nvim/view', meths.get_option('viewdir')) + ):gsub('\\', '/'), (meths.get_option('runtimepath')):gsub('\\', '/')) + eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup'), + meths.get_option('backupdir'):gsub('\\', '/')) + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'), + meths.get_option('directory'):gsub('\\', '/')) + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo'), + meths.get_option('undodir'):gsub('\\', '/')) + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view'), + meths.get_option('viewdir'):gsub('\\', '/')) end) end) @@ -435,53 +447,58 @@ describe('XDG-based defaults', function() before_each(function() clear({env={ XDG_CONFIG_HOME=', , ,', - XDG_CONFIG_DIRS=',-,-,:-,-,-', + XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-', XDG_DATA_HOME=',=,=,', - XDG_DATA_DIRS=',≡,≡,:≡,≡,≡', + XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡', }}) end) it('are escaped properly', function() local vimruntime, libdir = vimruntime_and_libdir() - eq(('\\, \\, \\,/nvim' - .. ',\\,-\\,-\\,/nvim' - .. ',-\\,-\\,-/nvim' - .. ',\\,=\\,=\\,/nvim/site' - .. ',\\,≡\\,≡\\,/nvim/site' - .. ',≡\\,≡\\,≡/nvim/site' + local path_sep = iswin() and '\\' or '/' + eq(('\\, \\, \\,' .. path_sep .. 'nvim' + .. ',\\,-\\,-\\,' .. path_sep .. 'nvim' + .. ',-\\,-\\,-' .. path_sep .. 'nvim' + .. ',\\,=\\,=\\,' .. path_sep .. data_dir .. path_sep .. 'site' + .. ',\\,≡\\,≡\\,' .. path_sep .. 'nvim' .. path_sep .. 'site' + .. ',≡\\,≡\\,≡' .. path_sep .. 'nvim' .. path_sep .. 'site' .. ',' .. vimruntime .. ',' .. libdir - .. ',≡\\,≡\\,≡/nvim/site/after' - .. ',\\,≡\\,≡\\,/nvim/site/after' - .. ',\\,=\\,=\\,/nvim/site/after' - .. ',-\\,-\\,-/nvim/after' - .. ',\\,-\\,-\\,/nvim/after' - .. ',\\, \\, \\,/nvim/after' + .. ',≡\\,≡\\,≡' .. path_sep .. 'nvim' .. path_sep .. 'site' .. path_sep .. 'after' + .. ',\\,≡\\,≡\\,' .. path_sep .. 'nvim' .. path_sep .. 'site' .. path_sep .. 'after' + .. ',\\,=\\,=\\,' .. path_sep.. data_dir .. path_sep .. 'site' .. path_sep .. 'after' + .. ',-\\,-\\,-' .. path_sep .. 'nvim' .. path_sep .. 'after' + .. ',\\,-\\,-\\,' .. path_sep .. 'nvim' .. path_sep .. 'after' + .. ',\\, \\, \\,' .. path_sep .. 'nvim' .. path_sep .. 'after' ), meths.get_option('runtimepath')) meths.command('set runtimepath&') meths.command('set backupdir&') meths.command('set directory&') meths.command('set undodir&') meths.command('set viewdir&') - eq(('\\, \\, \\,/nvim' - .. ',\\,-\\,-\\,/nvim' - .. ',-\\,-\\,-/nvim' - .. ',\\,=\\,=\\,/nvim/site' - .. ',\\,≡\\,≡\\,/nvim/site' - .. ',≡\\,≡\\,≡/nvim/site' + eq(('\\, \\, \\,' .. path_sep .. 'nvim' + .. ',\\,-\\,-\\,' .. path_sep ..'nvim' + .. ',-\\,-\\,-' .. path_sep ..'nvim' + .. ',\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'site' + .. ',\\,≡\\,≡\\,' .. path_sep ..'nvim' .. path_sep ..'site' + .. ',≡\\,≡\\,≡' .. path_sep ..'nvim' .. path_sep ..'site' .. ',' .. vimruntime .. ',' .. libdir - .. ',≡\\,≡\\,≡/nvim/site/after' - .. ',\\,≡\\,≡\\,/nvim/site/after' - .. ',\\,=\\,=\\,/nvim/site/after' - .. ',-\\,-\\,-/nvim/after' - .. ',\\,-\\,-\\,/nvim/after' - .. ',\\, \\, \\,/nvim/after' + .. ',≡\\,≡\\,≡' .. path_sep ..'nvim' .. path_sep ..'site' .. path_sep ..'after' + .. ',\\,≡\\,≡\\,' .. path_sep ..'nvim' .. path_sep ..'site' .. path_sep ..'after' + .. ',\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'site' .. path_sep ..'after' + .. ',-\\,-\\,-' .. path_sep ..'nvim' .. path_sep ..'after' + .. ',\\,-\\,-\\,' .. path_sep ..'nvim' .. path_sep ..'after' + .. ',\\, \\, \\,' .. path_sep ..'nvim' .. path_sep ..'after' ), meths.get_option('runtimepath')) - eq('.,\\,=\\,=\\,/nvim/backup', meths.get_option('backupdir')) - eq('\\,=\\,=\\,/nvim/swap//', meths.get_option('directory')) - eq('\\,=\\,=\\,/nvim/undo', meths.get_option('undodir')) - eq('\\,=\\,=\\,/nvim/view', meths.get_option('viewdir')) + eq('.,\\,=\\,=\\,' .. path_sep .. data_dir .. '' .. path_sep ..'backup', + meths.get_option('backupdir')) + eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'swap' .. (path_sep):rep(2), + meths.get_option('directory')) + eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'undo', + meths.get_option('undodir')) + eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'view', + meths.get_option('viewdir')) end) end) end) @@ -491,6 +508,7 @@ describe('stdpath()', function() -- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions -- due to XDG_CONFIG_HOME and XDG_DATA_HOME being the same. local datadir = iswin() and 'nvim-data' or 'nvim' + local env_sep = iswin() and ';' or ':' it('acceptance', function() clear() -- Do not explicitly set any env vars. @@ -634,13 +652,13 @@ describe('stdpath()', function() local function set_paths_via_system(var_name, paths) local env = base_env() - env[var_name] = table.concat(paths, ':') + env[var_name] = table.concat(paths, env_sep) clear({env=env}) end local function set_paths_at_runtime(var_name, paths) clear({env=base_env()}) - meths.set_var('env_val', table.concat(paths, ':')) + meths.set_var('env_val', table.concat(paths, env_sep)) command(('let $%s=g:env_val'):format(var_name)) end diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 1002011999..73f3fe5d0c 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -270,6 +270,76 @@ describe('LSP', function() test_name = "basic_check_capabilities"; on_init = function(client) client.stop() + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}, "expected callback") + end; + } + end) + + it('client.supports_methods() should validate capabilities', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + } + test_rpc_server { + test_name = "capabilities_for_client_supports_method"; + on_init = function(client) + client.stop() + local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full") + eq(full_kind, client.resolved_capabilities().text_document_did_change) + eq(true, client.resolved_capabilities().completion) + eq(true, client.resolved_capabilities().hover) + eq(false, client.resolved_capabilities().goto_definition) + eq(false, client.resolved_capabilities().rename) + + -- known methods for resolved capabilities + eq(true, client.supports_method("textDocument/hover")) + eq(false, client.supports_method("textDocument/definition")) + + -- unknown methods are assumed to be supported. + eq(true, client.supports_method("unknown-method")) + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_callback = function(...) + eq(table.remove(expected_callbacks), {...}, "expected callback") + end; + } + end) + + it('should call unsupported_method when trying to call an unsupported method', function() + local expected_callbacks = { + {NIL, "shutdown", {}, 1}; + } + test_rpc_server { + test_name = "capabilities_for_client_supports_method"; + on_setup = function() + exec_lua([=[ + vim.lsp.callbacks['textDocument/hover'] = function(err, method) + vim.lsp._last_lsp_callback = { err = err; method = method } + end + vim.lsp._unsupported_method = function(method) + vim.lsp._last_unsupported_method = method + return 'fake-error' + end + vim.lsp.buf.hover() + ]=]) + end; + on_init = function(client) + client.stop() + local method = exec_lua("return vim.lsp._last_unsupported_method") + eq("textDocument/hover", method) + local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_callback") + eq("fake-error", lsp_cb_call.err) + eq("textDocument/hover", lsp_cb_call.method) end; on_exit = function(code, signal) eq(0, code, "exit code", fake_lsp_logfile) @@ -747,8 +817,16 @@ describe('LSP', function() end) it('should invalid cmd argument', function() - eq('Error executing lua: .../shared.lua: cmd: expected list, got nvim', pcall_err(_cmd_parts, "nvim")) - eq('Error executing lua: .../shared.lua: cmd argument: expected string, got number', pcall_err(_cmd_parts, {"nvim", 1})) + eq(dedent([[ + Error executing lua: .../lsp.lua:0: cmd: expected list, got nvim + stack traceback: + .../lsp.lua:0: in function .../lsp.lua:0>]]), + pcall_err(_cmd_parts, 'nvim')) + eq(dedent([[ + Error executing lua: .../lsp.lua:0: cmd argument: expected string, got number + stack traceback: + .../lsp.lua:0: in function .../lsp.lua:0>]]), + pcall_err(_cmd_parts, {'nvim', 1})) end) end) end) @@ -971,34 +1049,34 @@ describe('LSP', function() local prefix = 'foo' local completion_list = { -- resolves into label - { label='foobar' }, - { label='foobar', textEdit={} }, + { label='foobar', sortText="a" }, + { label='foobar', sortText="b", textEdit={} }, -- resolves into insertText - { label='foocar', insertText='foobar' }, - { label='foocar', insertText='foobar', textEdit={} }, + { label='foocar', sortText="c", insertText='foobar' }, + { label='foocar', sortText="d", insertText='foobar', textEdit={} }, -- resolves into textEdit.newText - { label='foocar', insertText='foodar', textEdit={newText='foobar'} }, - { label='foocar', textEdit={newText='foobar'} }, + { label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} }, + { label='foocar', sortText="f", textEdit={newText='foobar'} }, -- real-world snippet text - { label='foocar', insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} }, - { label='foocar', insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} }, + { label='foocar', sortText="g", insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} }, + { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} }, -- nested snippet tokens - { label='foocar', insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} }, + { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} }, -- plain text - { label='foocar', insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} }, + { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} }, } local completion_list_items = {items=completion_list} local expected = { - { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } }, - { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2,typ3 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } }, + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar', sortText="a" } } } } }, + { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', sortText="b", textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="c", insertText='foobar' } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="d", insertText='foobar', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="f", textEdit={newText='foobar'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="g", insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2,typ3 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } }, } eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix)) diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index da9dd09129..1431054494 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -153,6 +153,16 @@ describe('clipboard', function() eq('', eval('provider#clipboard#Error()')) end) + it('g:clipboard using lists', function() + source([[let g:clipboard = { + \ 'name': 'custom', + \ 'copy': { '+': ['any', 'command'], '*': ['some', 'other'] }, + \ 'paste': { '+': ['any', 'command'], '*': ['some', 'other'] }, + \}]]) + eq('custom', eval('provider#clipboard#Executable()')) + eq('', eval('provider#clipboard#Error()')) + end) + it('g:clipboard using VimL functions', function() -- Implements a fake clipboard provider. cache_enabled is meaningless here. source([[let g:clipboard = { diff --git a/test/functional/provider/perl_spec.lua b/test/functional/provider/perl_spec.lua index 7b446e4ab3..125674660b 100644 --- a/test/functional/provider/perl_spec.lua +++ b/test/functional/provider/perl_spec.lua @@ -5,6 +5,10 @@ local command = helpers.command local write_file = helpers.write_file local eval = helpers.eval local retry = helpers.retry +local curbufmeths = helpers.curbufmeths +local insert = helpers.insert +local expect = helpers.expect +local feed = helpers.feed do clear() @@ -19,7 +23,51 @@ before_each(function() clear() end) -describe('perl host', function() +describe('legacy perl provider', function() + if helpers.pending_win32(pending) then return end + + it('feature test', function() + eq(1, eval('has("perl")')) + end) + + it(':perl command', function() + command('perl $vim->vars->{set_by_perl} = [100, 0];') + eq({100, 0}, eval('g:set_by_perl')) + end) + + it(':perlfile command', function() + local fname = 'perlfile.pl' + write_file(fname, '$vim->command("let set_by_perlfile = 123")') + command('perlfile perlfile.pl') + eq(123, eval('g:set_by_perlfile')) + os.remove(fname) + end) + + it(':perldo command', function() + -- :perldo 1; doesn't change $_, + -- the buffer should not be changed + command('normal :perldo 1;') + 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:perldo $_ = reverse ($_)."$linenr"<CR>') + expect([[ + cba1 + fed2 + ghi]]) + end) + + it('perleval()', function() + eq({1, 2, {['key'] = 'val'}}, eval([[perleval('[1, 2, {"key" => "val"}]')]])) + end) +end) + +describe('perl provider', function() if helpers.pending_win32(pending) then return end teardown(function () os.remove('Xtest-perl-hello.pl') diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index bb7d23ede6..2729d8dfa2 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -5,6 +5,7 @@ local command = helpers.command local curbufmeths = helpers.curbufmeths local eq = helpers.eq local eval = helpers.eval +local exc_exec = helpers.exc_exec local expect = helpers.expect local feed = helpers.feed local feed_command = helpers.feed_command @@ -109,3 +110,24 @@ describe('ruby provider', function() eq(2, eval('1+1')) -- Still alive? end) end) + +describe('rubyeval()', function() + it('evaluates ruby objects', function() + eq({1, 2, {['key'] = 'val'}}, funcs.rubyeval('[1, 2, {key: "val"}]')) + end) + + it('returns nil for empty strings', function() + eq(helpers.NIL, funcs.rubyeval('')) + end) + + it('errors out when given non-string', function() + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(10)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:_null_dict)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:_null_list)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(0.0)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(function("tr"))')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:true)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:false)')) + eq('Vim(call):E474: Invalid argument', exc_exec('call rubyeval(v:null)')) + end) +end) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 6372cd935e..8e171d31aa 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -1,7 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file @@ -13,7 +13,7 @@ describe(':terminal buffer', function() before_each(function() clear() feed_command('set modifiable swapfile undolevels=20') - wait() + poke_eventloop() screen = thelpers.screen_setup() end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index ef12438ecc..8d70ebf679 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -70,7 +70,7 @@ describe(':terminal cursor', function() :set number | ]]) feed('i') - helpers.wait() + helpers.poke_eventloop() screen:expect([[ {7: 1 }tty ready | {7: 2 }rows: 6, cols: 46 | diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 138befd978..4b512605e1 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local clear, wait, nvim = helpers.clear, helpers.wait, helpers.nvim +local clear, poke_eventloop, nvim = helpers.clear, helpers.poke_eventloop, helpers.nvim local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq local feed = helpers.feed local feed_command, eval = helpers.feed_command, helpers.eval @@ -29,7 +29,7 @@ describe(':terminal', function() -- Invoke a command that emits frequent terminal activity. feed([[:terminal "]]..nvim_dir..[[/shell-test" REP 9999 !terminal_output!<cr>]]) feed([[<C-\><C-N>]]) - wait() + poke_eventloop() -- Wait for some terminal activity. retry(nil, 4000, function() ok(funcs.line('$') > 6) @@ -60,7 +60,7 @@ describe(':terminal', function() feed_command([[terminal while true; do echo foo; sleep .1; done]]) end feed([[<C-\><C-N>M]]) -- move cursor away from last line - wait() + poke_eventloop() eq(3, eval("line('$')")) -- window height eq(2, eval("line('.')")) -- cursor is in the middle feed_command('vsplit') @@ -76,11 +76,11 @@ describe(':terminal', function() -- Create a new line (in the shell). For a normal buffer this -- increments the jumplist; for a terminal-buffer it should not. #3723 feed('i') - wait() + poke_eventloop() feed('<CR><CR><CR><CR>') - wait() + poke_eventloop() feed([[<C-\><C-N>]]) - wait() + poke_eventloop() -- Wait for >=1 lines to be created. retry(nil, 4000, function() ok(funcs.line('$') > lines_before) @@ -210,7 +210,7 @@ describe(':terminal (with fake shell)', function() it('ignores writes if the backing stream closes', function() terminal_with_fake_shell() feed('iiXXXXXXX') - wait() + poke_eventloop() -- Race: Though the shell exited (and streams were closed by SIGCHLD -- handler), :terminal cleanup is pending on the main-loop. -- This write should be ignored (not crash, #5445). diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 1df8df6f6e..77fdba7fc4 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -6,7 +6,7 @@ local feed, nvim_dir, feed_command = helpers.feed, helpers.nvim_dir, helpers.fee local iswin = helpers.iswin local eval = helpers.eval local command = helpers.command -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local retry = helpers.retry local curbufmeths = helpers.curbufmeths local nvim = helpers.nvim @@ -347,7 +347,7 @@ describe(':terminal prints more lines than the screen height and exits', functio local screen = Screen.new(30, 7) screen:attach({rgb=false}) feed_command('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert') - wait() + poke_eventloop() screen:expect([[ line6 | line7 | @@ -423,7 +423,7 @@ describe("'scrollback' option", function() retry(nil, nil, function() expect_lines(33, 2) end) curbufmeths.set_option('scrollback', 10) - wait() + poke_eventloop() retry(nil, nil, function() expect_lines(16) end) curbufmeths.set_option('scrollback', 10000) retry(nil, nil, function() expect_lines(16) end) diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index f1c828d17e..9f278fd157 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local feed_data = thelpers.feed_data local feed, clear = helpers.feed, helpers.clear -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local iswin = helpers.iswin local command = helpers.command local retry = helpers.retry @@ -127,7 +127,7 @@ describe(':terminal window', function() it('wont show any folds', function() feed([[<C-\><C-N>ggvGzf]]) - wait() + poke_eventloop() screen:expect([[ ^tty ready | line1 | diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index 3cb592c714..d7269d2c29 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -690,7 +690,7 @@ describe('Buffer highlighting', function() end) it('can be retrieved', function() - local get_virtual_text = curbufmeths.get_virtual_text + local get_extmarks = curbufmeths.get_extmarks local line_count = curbufmeths.line_count local s1 = {{'Köttbullar', 'Comment'}, {'Kräuterbutter'}} @@ -699,12 +699,14 @@ 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(s1, get_virtual_text(0)) + eq({{1, 0, 0, {virt_text = s1}}}, get_extmarks(id1, {0,0}, {0, -1}, {details=true})) - set_virtual_text(-1, line_count(), s2, {}) - eq(s2, get_virtual_text(line_count())) + -- 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, {virt_text = s2}}}, get_extmarks(id1, {lastline,0}, {lastline, -1}, {details=true})) - eq({}, get_virtual_text(line_count() + 9000)) + eq({}, get_extmarks(id1, {lastline+9000,0}, {lastline+9000, -1}, {})) end) it('is not highlighted by visual selection', function() diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 21c01b3458..01f0d8a4d7 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local source = helpers.source local command = helpers.command +local feed_command = helpers.feed_command local function new_screen(opt) local screen = Screen.new(25, 5) @@ -842,3 +843,34 @@ describe('cmdline redraw', function() ]], unchanged=true} end) end) + +describe('cmdline', function() + before_each(function() + clear() + end) + + it('prints every executed Ex command if verbose >= 16', function() + local screen = Screen.new(50, 12) + screen:attach() + source([[ + command DoSomething echo 'hello' |set ts=4 |let v = '123' |echo v + call feedkeys("\r", 't') " for the hit-enter prompt + set verbose=20 + ]]) + feed_command('DoSomething') + screen:expect([[ + | + ~ | + | + Executing: DoSomething | + Executing: echo 'hello' |set ts=4 |let v = '123' || + echo v | + hello | + Executing: set ts=4 |let v = '123' |echo v | + Executing: let v = '123' |echo v | + Executing: echo v | + 123 | + Press ENTER or type command to continue^ | + ]]) + end) +end) diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 6c913124ac..e1a72ced05 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -286,6 +286,21 @@ describe('ui/cursor', function() eq(173, named.normal.blinkon) eq(42, named.showmatch.cell_percentage) end) + + -- If there is no setting for guicursor, it becomes the default setting. + meths.set_option('guicursor', 'n:ver35-blinkwait171-blinkoff172-blinkon173-Cursor/lCursor') + screen:expect(function() + for _,m in ipairs(screen._mode_info) do + if m.name ~= 'normal' then + eq('block', m.cursor_shape or 'block') + eq(0, m.blinkon or 0) + eq(0, m.blinkoff or 0) + eq(0, m.blinkwait or 0) + eq(0, m.hl_id or 0) + eq(0, m.id_lm or 0) + end + end + end) end) it("empty 'guicursor' sets cursor_shape=block in all modes", function() @@ -297,6 +312,8 @@ describe('ui/cursor', function() if m['cursor_shape'] ~= nil then eq('block', m.cursor_shape) eq(0, m.blinkon) + eq(0, m.hl_id) + eq(0, m.id_lm) end end end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua new file mode 100644 index 0000000000..304c5aecb1 --- /dev/null +++ b/test/functional/ui/decorations_spec.lua @@ -0,0 +1,118 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +local clear = helpers.clear +local feed = helpers.feed +local insert = helpers.insert +local exec_lua = helpers.exec_lua +local expect_events = helpers.expect_events + +describe('decorations provider', function() + local screen + before_each(function() + clear() + screen = Screen.new(40, 8) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold=true, foreground=Screen.colors.Blue}, + }) + end) + + local mudholland = [[ + // just to see if there was an accident + // on Mulholland Drive + try_start(); + bufref_T save_buf; + switch_buffer(&save_buf, buf); + posp = getmark(mark, false); + restore_buffer(&save_buf); ]] + + local function setup_provider(code) + exec_lua ([[ + local a = vim.api + test1 = a.nvim_create_namespace "test1" + ]] .. (code or [[ + beamtrace = {} + function on_do(kind, ...) + table.insert(beamtrace, {kind, ...}) + end + ]]) .. [[ + a.nvim_set_decoration_provider( + test1, { + on_start = on_do; on_buf = on_do; + on_win = on_do; on_line = on_do; + on_end = on_do; + }) + ]]) + end + + local function check_trace(expected) + local actual = exec_lua [[ local b = beamtrace beamtrace = {} return b ]] + expect_events(expected, actual, "beam trace") + end + + it('leaves a trace', function() + insert(mudholland) + + setup_provider() + + screen:expect{grid=[[ + // just to see if there was an accident | + // on Mulholland Drive | + try_start(); | + bufref_T save_buf; | + switch_buffer(&save_buf, buf); | + posp = getmark(mark, false); | + restore_buffer(&save_buf);^ | + | + ]]} + check_trace { + { "start", 4, 40 }; + { "win", 1000, 1, 0, 8 }; + { "line", 1000, 1, 0 }; + { "line", 1000, 1, 1 }; + { "line", 1000, 1, 2 }; + { "line", 1000, 1, 3 }; + { "line", 1000, 1, 4 }; + { "line", 1000, 1, 5 }; + { "line", 1000, 1, 6 }; + { "end", 4 }; + } + + feed "iü<esc>" + screen:expect{grid=[[ + // just to see if there was an accident | + // on Mulholland Drive | + try_start(); | + bufref_T save_buf; | + switch_buffer(&save_buf, buf); | + posp = getmark(mark, false); | + restore_buffer(&save_buf);^ü | + | + ]]} + check_trace { + { "start", 5, 10 }; + { "buf", 1 }; + { "win", 1000, 1, 0, 8 }; + { "line", 1000, 1, 6 }; + { "end", 5 }; + } + end) + + it('single provider', function() + insert(mudholland) + setup_provider [[ + local hl = a.nvim_get_hl_id_by_name "ErrorMsg" + function do_it(event, ...) + if event == "line" then + local win, buf, line = ... + a.nvim_buf_set_extmark(buf, test_ns, line, line, + { end_line = line, end_col = line+1, + hl_group = hl, + ephemeral = true + }) + end + end + ]] + end) +end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 11fe861de8..eec8eb93d4 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -59,7 +59,7 @@ describe('floatwin', function() end) it('closed immediately by autocmd #11383', function() - eq('Error executing lua: [string "<nvim>"]:4: Window was closed immediately', + eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately', pcall_err(exec_lua, [[ local a = vim.api local function crashes(contents) diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 6ec45064da..9877f30206 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -6,6 +6,8 @@ local feed_command = helpers.feed_command local insert = helpers.insert local funcs = helpers.funcs local meths = helpers.meths +local source = helpers.source +local assert_alive = helpers.assert_alive describe("folded lines", function() local screen @@ -21,6 +23,8 @@ describe("folded lines", function() [5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey}, [6] = {background = Screen.colors.Yellow}, [7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray}, + [8] = {foreground = Screen.colors.Brown }, + [9] = {bold = true, foreground = Screen.colors.Brown} }) end) @@ -29,7 +33,7 @@ describe("folded lines", function() feed("i<cr><esc>") feed("vkzf") screen:expect([[ - {5: ^+-- 2 lines: ·············}| + {7: }{5:^+-- 2 lines: ·············}| {1:~ }| {1:~ }| {1:~ }| @@ -49,8 +53,8 @@ describe("folded lines", function() funcs.setline(4, 'line 2') feed("j") screen:expect([[ - {7:+ }{5: 1 +-- 2 lines: ·························}| - {7:+ }{5: 0 ^+-- 2 lines: ·························}| + {7:+ }{8: 1 }{5:+-- 2 lines: ·························}| + {7:+ }{9: 0 }{5:^+-- 2 lines: ·························}| {1:~ }| {1:~ }| {1:~ }| @@ -130,8 +134,8 @@ describe("folded lines", function() ]]) feed('vkzf') - screen:expect([[ - {5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ ﺎﻠﻋَﺮَﺒِﻳَّﺓ·················}| + screen:expect{grid=[[ + {5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة·················}| {1:~ }| {1:~ }| {1:~ }| @@ -139,7 +143,7 @@ describe("folded lines", function() {1:~ }| {1:~ }| | - ]]) + ]]} feed_command("set noarabicshape") screen:expect([[ @@ -155,7 +159,7 @@ describe("folded lines", function() feed_command("set number foldcolumn=2") screen:expect([[ - {7:+ }{5: 1 ^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة···········}| + {7:+ }{8: 1 }{5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة···········}| {1:~ }| {1:~ }| {1:~ }| @@ -168,7 +172,7 @@ describe("folded lines", function() -- Note: too much of the folded line gets cut off.This is a vim bug. feed_command("set rightleft") screen:expect([[ - {5:+-- 2 lines: å ······················^· 1 }{7: +}| + {5:···········ةيَّبِرَعَلا x̎͂̀̂͛͛ 语 å :senil 2 --^+}{8: 1 }{7: +}| {1: ~}| {1: ~}| {1: ~}| @@ -180,7 +184,7 @@ describe("folded lines", function() feed_command("set nonumber foldcolumn=0") screen:expect([[ - {5:+-- 2 lines: å 语 x̎͂̀̂͛͛ ال·····················^·}| + {5:·················ةيَّبِرَعَلا x̎͂̀̂͛͛ 语 å :senil 2 --^+}| {1: ~}| {1: ~}| {1: ~}| @@ -192,7 +196,7 @@ describe("folded lines", function() feed_command("set arabicshape") screen:expect([[ - {5:+-- 2 lines: å 语 x̎͂̀̂͛͛ ﺍﻟ·····················^·}| + {5:·················ةيَّبِرَعَلا x̎͂̀̂͛͛ 语 å :senil 2 --^+}| {1: ~}| {1: ~}| {1: ~}| @@ -355,4 +359,26 @@ describe("folded lines", function() | ]]} end) + + it('does not crash when foldtext is longer than columns #12988', function() + source([[ + function! MyFoldText() abort + return repeat('-', &columns + 100) + endfunction + ]]) + command('set foldtext=MyFoldText()') + feed("i<cr><esc>") + feed("vkzf") + screen:expect{grid=[[ + {5:^---------------------------------------------}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + assert_alive() + end) end) diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index afb0c9cfa6..712c1f377a 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -14,7 +14,7 @@ local neq = helpers.neq local ok = helpers.ok local retry = helpers.retry local source = helpers.source -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop local nvim = helpers.nvim local sleep = helpers.sleep local nvim_dir = helpers.nvim_dir @@ -114,7 +114,7 @@ describe(":substitute, inccommand=split interactivity", function() it("no preview if invoked by a script", function() source('%s/tw/MO/g') - wait() + poke_eventloop() eq(1, eval("bufnr('$')")) -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) @@ -123,10 +123,10 @@ describe(":substitute, inccommand=split interactivity", function() it("no preview if invoked by feedkeys()", function() -- in a script... source([[:call feedkeys(":%s/tw/MO/g\<CR>")]]) - wait() + poke_eventloop() -- or interactively... feed([[:call feedkeys(":%s/tw/MO/g\<CR>")<CR>]]) - wait() + poke_eventloop() eq(1, eval("bufnr('$')")) -- sanity check: assert the buffer state expect(default_text:gsub("tw", "MO")) @@ -162,7 +162,7 @@ describe(":substitute, 'inccommand' preserves", function() insert(default_text) feed_command("set inccommand=" .. case) - local delims = { '/', '#', ';', '%', ',', '@', '!', ''} + local delims = { '/', '#', ';', '%', ',', '@', '!' } for _,delim in pairs(delims) do feed_command("%s"..delim.."lines"..delim.."LINES"..delim.."g") expect([[ @@ -194,7 +194,7 @@ describe(":substitute, 'inccommand' preserves", function() -- Start typing an incomplete :substitute command. feed([[:%s/e/YYYY/g]]) - wait() + poke_eventloop() -- Cancel the :substitute. feed([[<C-\><C-N>]]) @@ -230,7 +230,7 @@ describe(":substitute, 'inccommand' preserves", function() -- Start typing an incomplete :substitute command. feed([[:%s/e/YYYY/g]]) - wait() + poke_eventloop() -- Cancel the :substitute. feed([[<C-\><C-N>]]) @@ -251,7 +251,7 @@ describe(":substitute, 'inccommand' preserves", function() some text 1 some text 2]]) feed(":%s/e/XXX/") - wait() + poke_eventloop() eq(expected_tick, eval("b:changedtick")) end) @@ -1128,15 +1128,15 @@ describe(":substitute, inccommand=split", function() feed(":%s/tw/Xo/g") -- Delete and re-type the g a few times. feed("<BS>") - wait() + poke_eventloop() feed("g") - wait() + poke_eventloop() feed("<BS>") - wait() + poke_eventloop() feed("g") - wait() + poke_eventloop() feed("<CR>") - wait() + poke_eventloop() feed(":vs tmp<enter>") eq(3, helpers.call('bufnr', '$')) end) @@ -1171,7 +1171,7 @@ describe(":substitute, inccommand=split", function() feed_command("silent edit! test/functional/fixtures/bigfile_oneline.txt") -- Start :substitute with a slow pattern. feed([[:%s/B.*N/x]]) - wait() + poke_eventloop() -- Assert that 'inccommand' is DISABLED in cmdline mode. eq("", eval("&inccommand")) @@ -1360,7 +1360,7 @@ describe("inccommand=nosplit", function() feed("<Esc>") command("set icm=nosplit") feed(":%s/tw/OKOK") - wait() + poke_eventloop() screen:expect([[ Inc substitution on | {12:OKOK}o lines | @@ -2592,7 +2592,7 @@ describe(":substitute", function() feed("<C-c>") feed('gg') - wait() + poke_eventloop() feed([[:%s/\(some\)\@<lt>!thing/one/]]) screen:expect([[ something | @@ -2613,7 +2613,7 @@ describe(":substitute", function() ]]) feed([[<C-c>]]) - wait() + poke_eventloop() feed([[:%s/some\(thing\)\@=/every/]]) screen:expect([[ {12:every}thing | @@ -2634,7 +2634,7 @@ describe(":substitute", function() ]]) feed([[<C-c>]]) - wait() + poke_eventloop() feed([[:%s/some\(thing\)\@!/every/]]) screen:expect([[ something | @@ -2718,7 +2718,7 @@ it(':substitute with inccommand during :terminal activity', function() feed('gg') feed(':%s/foo/ZZZ') sleep(20) -- Allow some terminal activity. - helpers.wait() + helpers.poke_eventloop() screen:expect_unchanged() end) end) @@ -2750,6 +2750,26 @@ it(':substitute with inccommand, timer-induced :redraw #9777', function() ]]) end) +it(":substitute doesn't crash with inccommand, if undo is empty #12932", function() + local screen = Screen.new(10,5) + clear() + command('set undolevels=-1') + common_setup(screen, 'split', 'test') + feed(':%s/test') + sleep(100) + feed('/') + sleep(100) + feed('f') + screen:expect([[ + {12:f} | + {15:~ }| + {15:~ }| + {15:~ }| + :%s/test/f^ | + ]]) + assert_alive() +end) + it('long :%s/ with inccommand does not collapse cmdline', function() local screen = Screen.new(10,5) clear() diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index efc02db159..5df4a1d533 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -323,7 +323,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:~ }| ]], messages={ - {content = {{"/line [1/2] W"}}, kind = "search_count"} + {content = {{"/line W [1/2]"}}, kind = "search_count"} }} feed('n') diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index d857b57a31..a741136111 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -546,7 +546,7 @@ describe('ui/mouse/input', function() :tabprevious | ]]) feed('<LeftMouse><10,0><LeftRelease>') -- go to second tab - helpers.wait() + helpers.poke_eventloop() feed('<LeftMouse><0,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index e4d1187dea..6601c2d68e 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -4,7 +4,7 @@ local clear = helpers.clear local feed, command, insert = helpers.feed, helpers.command, helpers.insert local eq = helpers.eq local meths = helpers.meths -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('ext_multigrid', function() @@ -1846,8 +1846,8 @@ describe('ext_multigrid', function() meths.input_mouse('left', 'press', '', 1,6, 20) -- TODO(bfredl): "batching" input_mouse is formally not supported yet. -- Normally it should work fine in async context when nvim is not blocked, - -- but add a wait be sure. - wait() + -- but add a poke_eventloop be sure. + poke_eventloop() meths.input_mouse('left', 'drag', '', 1, 4, 20) screen:expect{grid=[[ ## grid 1 @@ -1921,7 +1921,7 @@ describe('ext_multigrid', function() ]]} meths.input_mouse('left', 'press', '', 1,8, 26) - wait() + poke_eventloop() meths.input_mouse('left', 'drag', '', 1, 6, 30) screen:expect{grid=[[ ## grid 1 diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index 9646c3fdad..2f113f6ac6 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -14,10 +14,10 @@ describe('UI receives option updates', function() arabicshape=true, emoji=true, guifont='', - guifontset='', guifontwide='', linespace=0, pumblend=0, + mousefocus=false, showtabline=1, termguicolors=false, ttimeout=true, @@ -110,6 +110,12 @@ describe('UI receives option updates', function() eq(expected, screen.options) end) + command("set mousefocus") + expected.mousefocus = true + screen:expect(function() + eq(expected, screen.options) + end) + command("set nottimeout") expected.ttimeout = false screen:expect(function() diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index c1c5d1ce2e..3f984ff943 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1213,10 +1213,10 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{1: }| - {n:choice }{1: }| - {n:text }{1: }| - {n:thing }{1: }| + {1:~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{n: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {1:~ }| @@ -1261,10 +1261,10 @@ describe('builtin popupmenu', function() feed('<c-p>') screen:expect([[ some long prefix before the text| - {n:^word }{1: }| - {n:choice }{1: }| - {s:text }{1: }| - {n:thing }{1: }| + {1:^~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{s: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {1:~ }| @@ -1341,10 +1341,10 @@ describe('builtin popupmenu', function() screen:expect([[ some long prefix | before the text^ | - {1:~ }{n: word }| - {1:~ }{n: choice }| - {1:~ }{s: text }| - {1:~ }{n: thing }| + {1:~ }{n: word }{1: }| + {1:~ }{n: choice }{1: }| + {1:~ }{s: text }{1: }| + {1:~ }{n: thing }{1: }| {1:~ }| {2:-- INSERT --} | ]]) @@ -1358,10 +1358,10 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{1: }| - {n:choice }{1: }| - {n:text }{1: }| - {n:thing }{1: }| + {1:~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{n: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {1:~ }| @@ -2168,8 +2168,8 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{c: }{1: }| - {n:choice }{s: }{1: }| + {1:~ }{n: word }{c: }| + {1:~ }{n: choice}{s: }| {1:~ }| {1:~ }| {1:~ }| @@ -2187,10 +2187,10 @@ describe('builtin popupmenu', function() funcs.complete(29, {'word', 'choice', 'text', 'thing'}) screen:expect([[ some long prefix before the ^ | - {n:word }{1: }| - {n:choice }{1: }| - {n:text }{1: }| - {n:thing }{1: }| + {1:~ }{n: word }| + {1:~ }{n: choice}| + {1:~ }{n: text }| + {1:~ }{n: thing }| {1:~ }| {1:~ }| {2:-- INSERT --} | diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 635ce7392b..222275eb4d 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -158,7 +158,7 @@ describe('search highlighting', function() bar foo baz ]]) feed('/foo') - helpers.wait() + helpers.poke_eventloop() screen:expect_unchanged() end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 9d4cb325d9..01fc50289d 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -6,7 +6,7 @@ local feed_command, source, expect = helpers.feed_command, helpers.source, helpe local curbufmeths = helpers.curbufmeths local command = helpers.command local meths = helpers.meths -local wait = helpers.wait +local poke_eventloop = helpers.poke_eventloop describe('completion', function() local screen @@ -737,8 +737,8 @@ describe('completion', function() -- Does not indent when "ind" is typed. feed("in<C-X><C-N>") -- Completion list is generated incorrectly if we send everything at once - -- via nvim_input(). So wait() before sending <BS>. #8480 - wait() + -- via nvim_input(). So poke_eventloop() before sending <BS>. #8480 + poke_eventloop() feed("<BS>d") screen:expect([[ @@ -778,7 +778,7 @@ describe('completion', function() ]]) -- Works for unindenting too. feed("ounin<C-X><C-N>") - helpers.wait() + helpers.poke_eventloop() feed("<BS>d") screen:expect([[ inc uninc indent unindent | @@ -1000,65 +1000,65 @@ describe('completion', function() command('let g:foo = []') feed('o') - wait() + poke_eventloop() feed('<esc>') eq({'I'}, eval('g:foo')) command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() eq({'I', 'I'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() eq({'I', 'I', 'P'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() eq({'I', 'I', 'P', 'P'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() eq({'I', 'I', 'P', 'P', 'P'}, eval('g:foo')) feed('<esc>') command('let g:foo = []') feed('S') - wait() + poke_eventloop() feed('f') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') - wait() + poke_eventloop() feed('<C-N>') eq({'I', 'I', 'P', 'P', 'P', 'P'}, eval('g:foo')) feed('<esc>') diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua index c5390cbd12..9acc61e398 100644 --- a/test/functional/viml/errorlist_spec.lua +++ b/test/functional/viml/errorlist_spec.lua @@ -27,7 +27,7 @@ describe('setqflist()', function() setqflist({''}, 'r', 'foo') command('copen') eq('foo', get_cur_win_var('quickfix_title')) - setqflist({''}, 'r', {['title'] = 'qf_title'}) + setqflist({}, 'r', {['title'] = 'qf_title'}) eq('qf_title', get_cur_win_var('quickfix_title')) end) diff --git a/test/functional/visual/meta_key_spec.lua b/test/functional/visual/meta_key_spec.lua new file mode 100644 index 0000000000..11f7203da0 --- /dev/null +++ b/test/functional/visual/meta_key_spec.lua @@ -0,0 +1,22 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local command = helpers.command +local expect = helpers.expect + +describe('meta-keys-in-visual-mode', function() + before_each(function() + clear() + end) + + it('ALT/META', function() + -- Unmapped ALT-chords behave as Esc+c + insert('peaches') + feed('viw<A-x>viw<M-x>') + expect('peach') + -- 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('peachalt-jmeta-l') + end) +end) diff --git a/test/helpers.lua b/test/helpers.lua index 40b93d9935..68f0c92244 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -82,6 +82,17 @@ end function module.ok(res, msg, logfile) return dumplog(logfile, assert.is_true, res, msg) end + +-- TODO(bfredl): this should "failure" not "error" (issue with dumplog() ) +local function epicfail(state, arguments, _) + state.failure_message = arguments[1] + return false +end +assert:register("assertion", "epicfail", epicfail) +function module.fail(msg, logfile) + return dumplog(logfile, assert.epicfail, msg) +end + function module.matches(pat, actual) if nil ~= string.match(actual, pat) then return true @@ -105,8 +116,12 @@ function module.assert_log(pat, logfile) pat, nrlines, logfile, logtail)) end --- Invokes `fn` and returns the error string (may truncate full paths), or --- raises an error if `fn` succeeds. +-- Invokes `fn` and returns the error string (with truncated paths), or raises +-- an error if `fn` succeeds. +-- +-- Replaces line/column numbers with zero: +-- shared.lua:0: in function 'gsplit' +-- shared.lua:0: in function <shared.lua:0>' -- -- Usage: -- -- Match exact string. @@ -114,29 +129,36 @@ end -- -- Match Lua pattern. -- matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2')) -- -function module.pcall_err(fn, ...) +function module.pcall_err_withfile(fn, ...) assert(type(fn) == 'function') local status, rv = pcall(fn, ...) if status == true then error('expected failure, but got success') end - -- From this: - -- /home/foo/neovim/runtime/lua/vim/shared.lua:186: Expected string, got number - -- to this: - -- Expected string, got number - local errmsg = tostring(rv):gsub('^[^:]+:%d+: ', '') - -- From this: - -- Error executing lua: /very/long/foo.lua:186: Expected string, got number - -- to this: - -- Error executing lua: .../foo.lua:186: Expected string, got number - errmsg = errmsg:gsub([[lua: [a-zA-Z]?:?[^:]-[/\]([^:/\]+):%d+: ]], 'lua: .../%1: ') - -- Compiled modules will not have a path and will just be a name like - -- shared.lua:186, so strip the number. - errmsg = errmsg:gsub([[lua: ([^:/\ ]+):%d+: ]], 'lua: .../%1: ') - -- ^ Windows drive-letter (C:) + -- From: + -- C:/long/path/foo.lua:186: Expected string, got number + -- to: + -- .../foo.lua:0: Expected string, got number + local errmsg = tostring(rv):gsub('[^%s]-[/\\]([^%s:/\\]+):%d+', '.../%1:0') + -- Scrub numbers in paths/stacktraces: + -- shared.lua:0: in function 'gsplit' + -- shared.lua:0: in function <shared.lua:0>' + errmsg = errmsg:gsub('([^%s]):%d+', '%1:0') + -- Scrub tab chars: + errmsg = errmsg:gsub('\t', ' ') + -- In Lua 5.1, we sometimes get a "(tail call): ?" on the last line. + -- We remove this so that the tests are not lua dependent. + errmsg = errmsg:gsub('%s*%(tail call%): %?', '') + return errmsg end +function module.pcall_err(fn, ...) + local errmsg = module.pcall_err_withfile(fn, ...) + + return errmsg:gsub('.../helpers.lua:0: ', '') +end + -- initial_path: directory to recurse into -- re: include pattern (string) -- exc_re: exclude pattern(s) (string or table) @@ -200,14 +222,25 @@ function module.check_logs() end end fd:close() - os.remove(file) if #lines > 0 then + local status, f local out = io.stdout + if os.getenv('SYMBOLIZER') then + status, f = pcall(module.popen_r, os.getenv('SYMBOLIZER'), '-l', file) + end out:write(start_msg .. '\n') - out:write('= ' .. table.concat(lines, '\n= ') .. '\n') + if status then + for line in f:lines() do + out:write('= '..line..'\n') + end + f:close() + else + out:write('= ' .. table.concat(lines, '\n= ') .. '\n') + end out:write(select(1, start_msg:gsub('.', '=')) .. '\n') table.insert(runtime_errors, file) end + os.remove(file) end end end @@ -323,7 +356,7 @@ function module.check_cores(app, force) exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir } db_cmd = os.getenv('NVIM_TEST_CORE_DB_CMD') or gdb_db_cmd random_skip = os.getenv('NVIM_TEST_CORE_RANDOM_SKIP') - elseif os.getenv('TRAVIS_OS_NAME') == 'osx' then + elseif 'darwin' == module.uname() then initial_path = '/cores' re = nil exc_re = { local_tmpdir } diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index 95853a6834..f9bc3fabc4 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -69,7 +69,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { pre = ptr[1]; - // Detect hexadecimal: 0x or 0X follwed by hex digit + // Detect hexadecimal: 0x or 0X followed by hex digit if ((what & STR2NR_HEX) && !STRING_ENDED(ptr + 2) && (pre == 'X' || pre == 'x') @@ -77,7 +77,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, ptr += 2; goto vim_str2nr_hex; } - // Detect binary: 0b or 0B follwed by 0 or 1 + // Detect binary: 0b or 0B followed by 0 or 1 if ((what & STR2NR_BIN) && !STRING_ENDED(ptr + 2) && (pre == 'B' || pre == 'b') diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua index ad05b134e0..e7cb5e5d5e 100644 --- a/test/unit/os/env_spec.lua +++ b/test/unit/os/env_spec.lua @@ -78,15 +78,22 @@ describe('env.c', function() end) describe('os_setenv_append_path', function() - itp('appends /foo/bar to $PATH', function() + itp('appends :/foo/bar to $PATH', function() local original_path = os.getenv('PATH') - eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz'))) + eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz.exe'))) eq(original_path..':/foo/bar', os.getenv('PATH')) end) + itp('avoids redundant separator when appending to $PATH #7377', function() + os_setenv('PATH', '/a/b/c:', true) + eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz.exe'))) + -- Must not have duplicate separators. #7377 + eq('/a/b/c:/foo/bar', os.getenv('PATH')) + end) + itp('returns false if `fname` is not absolute', function() local original_path = os.getenv('PATH') - eq(false, cimp.os_setenv_append_path(to_cstr('foo/bar/baz'))) + eq(false, cimp.os_setenv_append_path(to_cstr('foo/bar/baz.exe'))) eq(original_path, os.getenv('PATH')) end) end) |