diff options
Diffstat (limited to 'test')
173 files changed, 5678 insertions, 1540 deletions
diff --git a/test/README.md b/test/README.md index a6e9080a40..37aa54c157 100644 --- a/test/README.md +++ b/test/README.md @@ -256,8 +256,15 @@ Number; !must be defined to function properly): - `VALGRIND_LOG` (F) (S): overrides valgrind log file name used for `VALGRIND`. +- `TEST_COLORS` (F) (U) (D): enable pretty colors in test runner. + - `TEST_SKIP_FRAGILE` (F) (D): makes test suite skip some fragile tests. +- `TEST_TIMEOUT` (FU) (I): specifies maximum time, in seconds, before the test + suite run is killed + +- `NVIM_LUA_NOTRACK` (F) (D): disable reference counting of Lua objects + - `NVIM_PROG`, `NVIM_PRG` (F) (S): override path to Neovim executable (default to `build/bin/nvim`). diff --git a/test/busted/outputHandlers/nvim.lua b/test/busted/outputHandlers/nvim.lua index 5456e9ca98..0e9801b94b 100644 --- a/test/busted/outputHandlers/nvim.lua +++ b/test/busted/outputHandlers/nvim.lua @@ -1,13 +1,9 @@ local pretty = require 'pl.pretty' local global_helpers = require('test.helpers') -local colors - -local isWindows = package.config:sub(1,1) == '\\' - -if isWindows then - colors = setmetatable({}, {__index = function() return function(s) return s end end}) -else +-- Colors are disabled by default. #15610 +local colors = setmetatable({}, {__index = function() return function(s) return s == nil and '' or tostring(s) end end}) +if os.getenv "TEST_COLORS" then colors = require 'term.colors' end diff --git a/test/deprecated.lua b/test/deprecated.lua new file mode 100644 index 0000000000..b162c8fc93 --- /dev/null +++ b/test/deprecated.lua @@ -0,0 +1,9 @@ +-- Island of Misfit Toys + +local M = {} + +function M.redir_exec() + error('redir_exec is deprecated, use nvim_exec() or pcall_err()') +end + +return M diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua index 05ca0d5f4d..c9c9be5406 100644 --- a/test/functional/api/buffer_updates_spec.lua +++ b/test/functional/api/buffer_updates_spec.lua @@ -159,9 +159,8 @@ describe('API: buffer events:', function() tick = tick + 1 expectn('nvim_buf_lines_event', {b, tick, 29, 29, firstfour, false}) - -- create a new empty buffer and wipe out the old one ... this will - -- turn off buffer events - command('enew!') + -- delete the current buffer to turn off buffer events + command('bdelete!') expectn('nvim_buf_detach_event', {b}) -- add a line at the start of an empty file @@ -269,7 +268,7 @@ describe('API: buffer events:', function() 'original foo'}, false}) -- type text into the first line of a blank file, one character at a time - command('enew!') + command('bdelete!') tick = 2 expectn('nvim_buf_detach_event', {b}) local bnew = nvim('get_current_buf') @@ -666,7 +665,8 @@ describe('API: buffer events:', function() tick = tick + 1 expectn('nvim_buf_changedtick_event', {b, tick}) - -- close our buffer by creating a new one + -- close our buffer and create a new one + command('bdelete') command('enew') expectn('nvim_buf_detach_event', {b}) diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index e6a9e11fd9..37331d11c7 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -53,7 +53,7 @@ describe('nvim_get_commands', function() end) it('gets various command attributes', function() - local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='0', range='10', register=false, script_id=0, } + local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, script_id=0, } local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, script_id=1, } local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, } local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, } @@ -62,7 +62,7 @@ describe('nvim_get_commands', function() command -complete=custom,ListUsers -nargs=+ Finger !finger <args> ]]) eq({Finger=cmd1}, meths.get_commands({builtin=false})) - command('command -complete=dir -addr=arguments -count=10 TestCmd pwd <args>') + command('command -nargs=1 -complete=dir -addr=arguments -count=10 TestCmd pwd <args>') eq({Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false})) source([[ diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index d2b555ee5b..50b4b85d2a 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -12,6 +12,7 @@ local feed = helpers.feed local clear = helpers.clear local command = helpers.command local meths = helpers.meths +local assert_alive = helpers.assert_alive local function expect(contents) return eq(contents, helpers.curbuf_contents()) @@ -1381,13 +1382,13 @@ describe('API/extmarks', function() end) it('does not crash with append/delete/undo seqence', function() - meths.exec([[ + meths.exec([[ let ns = nvim_create_namespace('myplugin') call nvim_buf_set_extmark(0, ns, 0, 0, {}) call append(0, '') %delete undo]],false) - eq(2, meths.eval('1+1')) -- did not crash + assert_alive() end) it('works with left and right gravity', function() diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 9ee2570798..6367cc5caa 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -5,6 +5,8 @@ local eq, clear, eval, command, nvim, next_msg = local meths = helpers.meths local exec_lua = helpers.exec_lua local retry = helpers.retry +local isCI = helpers.isCI +local assert_alive = helpers.assert_alive describe('notify', function() local channel @@ -72,18 +74,23 @@ describe('notify', function() nvim('subscribe', 'event1') nvim('unsubscribe', 'doesnotexist') nvim('unsubscribe', 'event1') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it('cancels stale events on channel close', function() + if isCI() then + pending('hangs on CI #14083 #15251') + return + end if helpers.pending_win32(pending) then return end local catchan = eval("jobstart(['cat'], {'rpc': v:true})") - eq({id=catchan, stream='job', mode='rpc', client = {}}, exec_lua ([[ + local catpath = eval('exepath("cat")') + eq({id=catchan, argv={catpath}, stream='job', mode='rpc', client = {}}, exec_lua ([[ vim.rpcnotify(..., "nvim_call_function", 'chanclose', {..., 'rpc'}) vim.rpcnotify(..., "nvim_subscribe", "daily_rant") return vim.api.nvim_get_chan_info(...) ]], catchan)) - eq(2, eval('1+1')) -- Still alive? + assert_alive() eq({false, 'Invalid channel: '..catchan}, exec_lua ([[ return {pcall(vim.rpcrequest, ..., 'nvim_eval', '1+1')}]], catchan)) retry(nil, 3000, function() eq({}, meths.get_chan_info(catchan)) end) -- cat be dead :( diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 237a4b01e4..e408890906 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -11,6 +11,7 @@ local meths = helpers.meths local spawn, merge_args = helpers.spawn, helpers.merge_args local set_session = helpers.set_session local pcall_err = helpers.pcall_err +local assert_alive = helpers.assert_alive describe('server -> client', function() local cid @@ -33,7 +34,7 @@ describe('server -> client', function() call jobstop(ch1) ]]) - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) describe('simple call', function() @@ -158,7 +159,7 @@ describe('server -> client', function() -- do some busywork, so the first request will return -- before this one for _ = 1, 5 do - eq(2, eval("1+1")) + assert_alive() end eq(1, eval('rpcnotify('..cid..', "nested_done")')) return 'done!' diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 91d2745130..ffef6a6066 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,6 +1,8 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local fmt = string.format +local assert_alive = helpers.assert_alive local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local command = helpers.command @@ -57,7 +59,7 @@ describe('API', function() eq({'notification', 'nvim_error_event', {error_types.Exception.id, 'Invalid method: nvim_bogus'}}, next_msg()) -- error didn't close channel. - eq(2, eval('1+1')) + assert_alive() end) it('failed async request emits nvim_error_event', function() @@ -67,7 +69,7 @@ describe('API', function() {error_types.Exception.id, 'Vim:E492: Not an editor command: bogus'}}, next_msg()) -- error didn't close channel. - eq(2, eval('1+1')) + assert_alive() end) it('does not set CA_COMMAND_BUSY #7254', function() @@ -115,6 +117,19 @@ describe('API', function() nvim('exec','autocmd BufAdd * :let x1 = "Hello"', false) nvim('command', 'new foo') eq('Hello', request('nvim_eval', 'g:x1')) + + -- Line continuations + nvim('exec', [[ + let abc = #{ + \ a: 1, + "\ b: 2, + \ c: 3 + \ }]], false) + eq({a = 1, c = 3}, request('nvim_eval', 'g:abc')) + + -- try no spaces before continuations to catch off-by-one error + nvim('exec', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', false) + eq({a = 98}, request('nvim_eval', 'g:ab')) end) it('non-ASCII input', function() @@ -518,7 +533,7 @@ describe('API', function() nvim("notify", "hello world", 2, {}) end) - it('can be overriden', function() + it('can be overridden', function() command("lua vim.notify = function(...) return 42 end") eq(42, meths.exec_lua("return vim.notify('Hello world')", {})) nvim("notify", "hello world", 4, {}) @@ -1326,10 +1341,10 @@ describe('API', function() end) end) - describe('nvim_list_chans and nvim_get_chan_info', function() + describe('nvim_list_chans, nvim_get_chan_info', function() before_each(function() - command('autocmd ChanOpen * let g:opened_event = copy(v:event)') - command('autocmd ChanInfo * let g:info_event = copy(v:event)') + command('autocmd ChanOpen * let g:opened_event = deepcopy(v:event)') + command('autocmd ChanInfo * let g:info_event = deepcopy(v:event)') end) local testinfo = { stream = 'stdio', @@ -1350,7 +1365,7 @@ describe('API', function() eq({}, meths.get_chan_info(10)) end) - it('works for stdio channel', function() + it('stream=stdio channel', function() eq({[1]=testinfo,[2]=stderr}, meths.list_chans()) eq(testinfo, meths.get_chan_info(1)) eq(stderr, meths.get_chan_info(2)) @@ -1377,11 +1392,13 @@ describe('API', function() eq(info, meths.get_chan_info(1)) end) - it('works for job channel', function() + it('stream=job channel', function() eq(3, eval("jobstart(['cat'], {'rpc': v:true})")) + local catpath = eval('exepath("cat")') local info = { stream='job', id=3, + argv={ catpath }, mode='rpc', client={}, } @@ -1394,6 +1411,7 @@ describe('API', function() info = { stream='job', id=3, + argv={ catpath }, mode='rpc', client = { name='amazing-cat', @@ -1410,14 +1428,15 @@ describe('API', function() pcall_err(eval, 'rpcrequest(3, "nvim_set_current_buf", -1)')) end) - it('works for :terminal channel', function() - command(":terminal") + it('stream=job :terminal channel', function() + command(':terminal') eq({id=1}, meths.get_current_buf()) - eq(3, meths.buf_get_option(1, "channel")) + eq(3, meths.buf_get_option(1, 'channel')) local info = { stream='job', id=3, + argv={ eval('exepath(&shell)') }, mode='terminal', buffer = 1, pty='?', @@ -1431,6 +1450,38 @@ describe('API', function() info.buffer = {id=1} eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans()) eq(info, meths.get_chan_info(3)) + + -- :terminal with args + running process. + command(':exe "terminal" shellescape(v:progpath) "-u NONE -i NONE"') + eq(-1, eval('jobwait([&channel], 0)[0]')) -- Running? + local expected2 = { + stream = 'job', + id = 4, + argv = ( + iswin() and { + eval('&shell'), + '/s', + '/c', + fmt('"%s -u NONE -i NONE"', eval('shellescape(v:progpath)')), + } or { + eval('&shell'), + eval('&shellcmdflag'), + fmt('%s -u NONE -i NONE', eval('shellescape(v:progpath)')), + } + ), + mode = 'terminal', + buffer = 2, + pty = '?', + } + local actual2 = eval('nvim_get_chan_info(&channel)') + expected2.pty = actual2.pty + eq(expected2, actual2) + + -- :terminal with args + stopped process. + eq(1, eval('jobstop(&channel)')) + eval('jobwait([&channel], 1000)') -- Wait. + expected2.pty = (iswin() and '?' or '') -- pty stream was closed. + eq(expected2, eval('nvim_get_chan_info(&channel)')) end) end) @@ -1540,7 +1591,10 @@ describe('API', function() eq({'a', '', 'b'}, meths.list_runtime_paths()) meths.set_option('runtimepath', ',a,b') eq({'', 'a', 'b'}, meths.list_runtime_paths()) + -- trailing , is ignored, use ,, if you really really want $CWD meths.set_option('runtimepath', 'a,b,') + eq({'a', 'b'}, meths.list_runtime_paths()) + meths.set_option('runtimepath', 'a,b,,') eq({'a', 'b', ''}, meths.list_runtime_paths()) end) it('truncates too long paths', function() @@ -1961,8 +2015,13 @@ describe('API', function() ok(endswith(val[1], p"autoload/remote/define.vim") or endswith(val[1], p"autoload/remote/host.vim")) - eq({}, meths.get_runtime_file("lua", true)) - eq({}, meths.get_runtime_file("lua/vim", true)) + val = meths.get_runtime_file("lua", true) + eq(1, #val) + ok(endswith(val[1], p"lua")) + + val = meths.get_runtime_file("lua/vim", true) + eq(1, #val) + ok(endswith(val[1], p"lua/vim")) end) it('can find directories', function() diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index bb72b63b6c..11755a9d97 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -1,8 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, - ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, + ok, feed, insert, eval, tabpage = 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 + helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval, + helpers.tabpage local poke_eventloop = helpers.poke_eventloop local curwinmeths = helpers.curwinmeths local funcs = helpers.funcs @@ -11,6 +12,7 @@ local NIL = helpers.NIL local meths = helpers.meths local command = helpers.command local pcall_err = helpers.pcall_err +local assert_alive = helpers.assert_alive -- check if str is visible at the beginning of some line local function is_visible(str) @@ -206,7 +208,7 @@ describe('API/win', function() end) end) - describe('{get,set}_option', function() + describe('nvim_win_get_option, nvim_win_set_option', function() it('works', function() curwin('set_option', 'colorcolumn', '4,3') eq('4,3', curwin('get_option', 'colorcolumn')) @@ -224,6 +226,18 @@ describe('API/win', function() pcall_err(curwin, 'get_option', 'statusline')) eq('', eval('&l:statusline')) -- confirm local value was not copied end) + + it('after switching windows #15390', function() + nvim('command', 'tabnew') + local tab1 = unpack(nvim('list_tabpages')) + local win1 = unpack(tabpage('list_wins', tab1)) + window('set_option', win1, 'statusline', 'window-status') + nvim('command', 'split') + nvim('command', 'wincmd J') + nvim('command', 'wincmd j') + eq('window-status', window('get_option', win1, 'statusline')) + assert_alive() + end) end) describe('get_position', function() @@ -311,7 +325,8 @@ describe('API/win', function() eq({newwin}, meths.list_wins()) end) - it('handles changed buffer', function() + it("handles changed buffer when 'hidden' is unset", function() + command('set nohidden') local oldwin = meths.get_current_win() insert('text') command('new') @@ -346,6 +361,21 @@ describe('API/win', function() eq(2, #meths.list_wins()) eq('', funcs.getcmdwintype()) end) + + it('closing current (float) window of another tabpage #15313', function() + command('tabedit') + eq(2, eval('tabpagenr()')) + local win = meths.open_win(0, true, { + relative='editor', row=10, col=10, width=50, height=10 + }) + local tab = eval('tabpagenr()') + command('tabprevious') + eq(1, eval('tabpagenr()')) + meths.win_close(win, false) + + eq(1001, meths.tabpage_get_win(tab).id) + assert_alive() + end) end) describe('hide', function() diff --git a/test/functional/autoread/focus_spec.lua b/test/functional/autocmd/focus_spec.lua index 3f9a0ad09b..3f9a0ad09b 100644 --- a/test/functional/autoread/focus_spec.lua +++ b/test/functional/autocmd/focus_spec.lua diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua index dc2fd3e97d..b186aa1f50 100644 --- a/test/functional/autocmd/tabnewentered_spec.lua +++ b/test/functional/autocmd/tabnewentered_spec.lua @@ -7,7 +7,7 @@ local eval = helpers.eval local eq = helpers.eq local feed = helpers.feed local nvim = helpers.nvim -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture describe('TabNewEntered', function() describe('au TabNewEntered', function() @@ -77,7 +77,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 [No Name] Tab page 2 @@ -86,7 +85,7 @@ describe('tabpage/previous', function() > [No Name] Tab page 4 # [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the fourth. @@ -117,7 +116,6 @@ describe('tabpage/previous', function() feed(characters) eq(dedent([=[ - Tab page 1 [No Name] Tab page 2 @@ -128,7 +126,7 @@ describe('tabpage/previous', function() [No Name] Tab page 5 [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the third. @@ -161,7 +159,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 # [No Name] Tab page 2 @@ -170,7 +167,7 @@ describe('tabpage/previous', function() [No Name] Tab page 4 > [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the first. @@ -205,7 +202,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 > [No Name] Tab page 2 @@ -214,7 +210,7 @@ describe('tabpage/previous', function() [No Name] Tab page 4 # [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the fourth. @@ -247,7 +243,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 [No Name] Tab page 2 @@ -256,7 +251,7 @@ describe('tabpage/previous', function() # [No Name] Tab page 4 > [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the third. @@ -291,7 +286,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 # [No Name] Tab page 2 @@ -300,7 +294,7 @@ describe('tabpage/previous', function() > [No Name] Tab page 4 [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the first. @@ -333,7 +327,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 [No Name] Tab page 2 @@ -342,7 +335,7 @@ describe('tabpage/previous', function() [No Name] Tab page 4 > [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the second. @@ -377,7 +370,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 [No Name] Tab page 2 @@ -386,7 +378,7 @@ describe('tabpage/previous', function() [No Name] Tab page 4 # [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the fourth. @@ -444,14 +436,13 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 [No Name] Tab page 2 [No Name] Tab page 3 > [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) -- The previous tab is now the "zero". @@ -567,7 +558,6 @@ describe('tabpage/previous', function() eq(dedent([=[ - Tab page 1 [No Name] Tab page 2 @@ -578,7 +568,7 @@ describe('tabpage/previous', function() [No Name] Tab page 4 > [No Name]]=]), - redir_exec('tabs') + exec_capture('tabs') ) end) end) diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index b12c24b58d..1e8f981437 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -13,7 +13,7 @@ describe('autocmd TermClose', function() before_each(function() clear() nvim('set_option', 'shell', nvim_dir .. '/shell-test') - nvim('set_option', 'shellcmdflag', 'EXE') + command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=') end) it('triggers when fast-exiting terminal job stops', function() @@ -90,6 +90,17 @@ describe('autocmd TermClose', function() retry(nil, nil, function() eq('3', eval('g:abuf')) end) feed('<c-c>:qa!<cr>') end) + + it('exposes v:event.status', function() + command('set shellcmdflag=EXIT') + command('autocmd TermClose * let g:status = v:event.status') + + command('terminal 0') + retry(nil, nil, function() eq(0, eval('g:status')) end) + + command('terminal 42') + retry(nil, nil, function() eq(42, eval('g:status')) end) + end) end) it('autocmd TermEnter, TermLeave', function() diff --git a/test/functional/cmdline/ctrl_r_spec.lua b/test/functional/cmdline/ctrl_r_spec.lua deleted file mode 100644 index a0f3955282..0000000000 --- a/test/functional/cmdline/ctrl_r_spec.lua +++ /dev/null @@ -1,34 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, insert, funcs, eq, feed = - helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed - -describe('cmdline CTRL-R', function() - before_each(clear) - - it('pasting non-special register inserts <CR> *between* lines', function() - insert([[ - line1abc - line2somemoretext - ]]) - -- Yank 2 lines linewise, then paste to cmdline. - feed([[<C-\><C-N>gg0yj:<C-R>0]]) - -- <CR> inserted between lines, NOT after the final line. - eq('line1abc\rline2somemoretext', funcs.getcmdline()) - - -- Yank 2 lines charwise, then paste to cmdline. - feed([[<C-\><C-N>gg05lyvj:<C-R>0]]) - -- <CR> inserted between lines, NOT after the final line. - eq('abc\rline2', funcs.getcmdline()) - - -- Yank 1 line linewise, then paste to cmdline. - feed([[<C-\><C-N>ggyy:<C-R>0]]) - -- No <CR> inserted. - eq('line1abc', funcs.getcmdline()) - end) - - it('pasting special register inserts <CR>, <NL>', function() - feed([[:<C-R>="foo\nbar\rbaz"<CR>]]) - eq('foo\nbar\rbaz', funcs.getcmdline()) - end) -end) - diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 1ef34c7318..6efa4f9b80 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -89,6 +89,9 @@ describe('channels', function() command("call chansend(id, 'howdy')") eq({"notification", "stdout", {id, {"[1, ['howdy'], 'stdin']"}}}, next_msg()) + command("call chansend(id, 0z686f6c61)") + eq({"notification", "stdout", {id, {"[1, ['hola'], 'stdin']"}}}, next_msg()) + command("call chanclose(id, 'stdin')") expect_twostreams({{"notification", "stdout", {id, {"[1, [''], 'stdin']"}}}, {'notification', 'stdout', {id, {''}}}}, @@ -131,6 +134,8 @@ describe('channels', function() command("call chansend(id, 'TEXT\n')") expect_twoline(id, "stdout", "TEXT\r", "[1, ['TEXT', ''], 'stdin']") + command("call chansend(id, 0z426c6f6273210a)") + expect_twoline(id, "stdout", "Blobs!\r", "[1, ['Blobs!', ''], 'stdin']") command("call chansend(id, 'neovan')") eq({"notification", "stdout", {id, {"neovan"}}}, next_msg()) diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 230b7f8e01..a47e7568a9 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local command = helpers.command local feed_command = helpers.feed_command local eval = helpers.eval @@ -7,7 +8,8 @@ local eq = helpers.eq local run = helpers.run local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err +local exec_capture = helpers.exec_capture local poke_eventloop = helpers.poke_eventloop describe('v:exiting', function() @@ -51,9 +53,9 @@ end) describe(':cquit', function() local function test_cq(cmdline, exit_code, redir_msg) if redir_msg then - eq('\n' .. redir_msg, redir_exec(cmdline)) + eq(redir_msg, pcall_err(function() return exec_capture(cmdline) end)) poke_eventloop() - eq(2, eval("1+1")) -- Still alive? + assert_alive() else funcs.system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline}) eq(exit_code, eval('v:shell_error')) @@ -85,14 +87,14 @@ describe(':cquit', function() end) it('exits with redir msg for multiple exit codes after :cquit 1 2', function() - test_cq('cquit 1 2', nil, 'E488: Trailing characters: cquit 1 2') + test_cq('cquit 1 2', nil, 'Vim(cquit):E488: Trailing characters: cquit 1 2') end) it('exits with redir msg for non-number exit code after :cquit X', function() - test_cq('cquit X', nil, 'E488: Trailing characters: cquit X') + test_cq('cquit X', nil, 'Vim(cquit):E488: Trailing characters: cquit X') end) it('exits with redir msg for negative exit code after :cquit -1', function() - test_cq('cquit -1', nil, 'E488: Trailing characters: cquit -1') + test_cq('cquit -1', nil, 'Vim(cquit):E488: Trailing characters: cquit -1') end) end) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 34ab90d760..5e127bce26 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -5,6 +5,7 @@ local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim, helpers.nvim_dir, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir +local assert_alive = helpers.assert_alive local command = helpers.command local funcs = helpers.funcs local os_kill = helpers.os_kill @@ -348,6 +349,12 @@ describe('jobs', function() eq(false, pcall(function() nvim('command', 'call jobsend(j, ["some data"])') end)) + + command("let g:job_opts.stdin = 'null'") + nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + eq(false, pcall(function() + nvim('command', 'call jobsend(j, ["some data"])') + end)) end) it('disallows jobsend on a non-existent job', function() @@ -864,7 +871,7 @@ describe('jobs', function() -- loop tick. This is also prevented by try-block, so feed must be used. feed_command("call DoIt()") feed('<cr>') -- press RETURN - eq(2,eval('1+1')) + assert_alive() end) it('jobstop() kills entire process tree #6530', function() diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index a70b94c0e9..1d83eb799f 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command local ok = helpers.ok @@ -231,7 +232,7 @@ describe('startup', function() it('does not crash if --embed is given twice', function() clear{args={'--embed'}} - eq(2, eval('1+1')) + assert_alive() end) it('does not crash when expanding cdpath during early_init', function() @@ -247,9 +248,9 @@ describe('startup', function() [[" -u NONE -i NONE --cmd "set noruler" --cmd "let g:foo = g:bar"')]]) screen:expect([[ ^ | + | Error detected while processing pre-vimrc command line: | E121: Undefined variable: g:bar | - E15: Invalid expression: g:bar | Press ENTER or type command to continue | | ]]) @@ -309,7 +310,8 @@ describe('startup', function() end) local function pack_clear(cmd) - clear('--cmd', 'set packpath=test/functional/fixtures', '--cmd', cmd) + -- add packages after config dir in rtp but before config/after + clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}} end @@ -347,6 +349,22 @@ describe('startup', function() pack_clear [[ packadd! bonus | lua _G.y = require'bonus'.launch() ]] eq('CPE 1704 TKS', exec_lua [[ return _G.y ]]) end) + + it("handles the correct order with start packages and after/", function() + pack_clear [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]] + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with opt packages and after/", function() + pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! superspecial\nruntime! filen.lua" ]] + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with a package that changes packpath", function() + pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! funky\nruntime! filen.lua" ]] + eq({'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({'ordinary', 'funky!', 'mittel', 'ordinary after'}, exec_lua [[ return _G.nested_order ]]) + end) end) describe('sysinit', function() @@ -438,12 +456,15 @@ describe('user config init', function() local xhome = 'Xhome' local pathsep = helpers.get_pathsep() local xconfig = xhome .. pathsep .. 'Xconfig' + local xdata = xhome .. pathsep .. 'Xdata' local init_lua_path = table.concat({xconfig, 'nvim', 'init.lua'}, pathsep) + local xenv = { XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } before_each(function() rmdir(xhome) mkdir_p(xconfig .. pathsep .. 'nvim') + mkdir_p(xdata) write_file(init_lua_path, [[ vim.g.lua_rc = 1 @@ -455,10 +476,10 @@ describe('user config init', function() end) it('loads init.lua from XDG config home by default', function() - clear{ args_rm={'-u' }, env={ XDG_CONFIG_HOME=xconfig }} + clear{ args_rm={'-u' }, env=xenv } eq(1, eval('g:lua_rc')) - eq(init_lua_path, eval('$MYVIMRC')) + eq(funcs.fnamemodify(init_lua_path, ':p'), eval('$MYVIMRC')) end) describe 'with explicitly provided config'(function() @@ -470,7 +491,7 @@ describe('user config init', function() end) it('loads custom lua config and does not set $MYVIMRC', function() - clear{ args={'-u', custom_lua_path }, env={ XDG_CONFIG_HOME=xconfig }} + clear{ args={'-u', custom_lua_path }, env=xenv } eq(1, eval('g:custom_lua_rc')) eq('', eval('$MYVIMRC')) end) @@ -484,10 +505,10 @@ describe('user config init', function() end) it('loads default lua config, but shows an error', function() - clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig }} + clear{ args_rm={'-u'}, env=xenv } feed('<cr>') -- TODO check this, test execution is blocked without it eq(1, eval('g:lua_rc')) - matches('Conflicting configs', meths.exec('messages', true)) + matches('^E5422: Conflicting configs', meths.exec('messages', true)) end) end) end) @@ -496,9 +517,13 @@ describe('runtime:', function() local xhome = 'Xhome' local pathsep = helpers.get_pathsep() local xconfig = xhome .. pathsep .. 'Xconfig' + local xdata = xhome .. pathsep .. 'Xdata' + local xenv = { XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } setup(function() + rmdir(xhome) mkdir_p(xconfig .. pathsep .. 'nvim') + mkdir_p(xdata) end) teardown(function() @@ -511,13 +536,13 @@ describe('runtime:', function() mkdir_p(plugin_folder_path) write_file(plugin_file_path, [[ vim.g.lua_plugin = 1 ]]) - clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig }} + clear{ args_rm={'-u'}, env=xenv } eq(1, eval('g:lua_plugin')) rmdir(plugin_folder_path) end) - it('loads plugin/*.lua from start plugins', function() + it('loads plugin/*.lua from start packages', function() local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'catagory', 'start', 'test_plugin'}, pathsep) local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep) @@ -528,7 +553,7 @@ describe('runtime:', function() mkdir_p(plugin_folder_path) write_file(plugin_file_path, [[vim.g.lua_plugin = 2]]) - clear{ args_rm={'-u'}, args={'--startuptime', profiler_file}, env={ XDG_CONFIG_HOME=xconfig }} + clear{ args_rm={'-u'}, args={'--startuptime', profiler_file}, env=xenv } eq(2, eval('g:lua_plugin')) -- Check if plugin_file_path is listed in :scriptname @@ -545,6 +570,27 @@ describe('runtime:', function() rmdir(plugin_path) end) + it('loads plugin/*.lua from site packages', function() + local nvimdata = iswin() and "nvim-data" or "nvim" + local plugin_path = table.concat({xdata, nvimdata, 'site', 'pack', 'xa', 'start', 'yb'}, pathsep) + local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep) + local plugin_after_path = table.concat({plugin_path, 'after', 'plugin'}, pathsep) + local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) + local plugin_after_file_path = table.concat({plugin_after_path, 'helloo.lua'}, pathsep) + + mkdir_p(plugin_folder_path) + write_file(plugin_file_path, [[table.insert(_G.lista, "unos")]]) + mkdir_p(plugin_after_path) + write_file(plugin_after_file_path, [[table.insert(_G.lista, "dos")]]) + + clear{ args_rm={'-u'}, args={'--cmd', 'lua _G.lista = {}'}, env=xenv } + + eq({'unos', 'dos'}, exec_lua "return _G.lista") + + rmdir(plugin_path) + end) + + it('loads ftdetect/*.lua', function() local ftdetect_folder = table.concat({xconfig, 'nvim', 'ftdetect'}, pathsep) local ftdetect_file = table.concat({ftdetect_folder , 'new-ft.lua'}, pathsep) @@ -554,6 +600,7 @@ describe('runtime:', function() -- TODO(shadmansaleh): Figure out why this test fails without -- setting VIMRUNTIME clear{ args_rm={'-u'}, env={XDG_CONFIG_HOME=xconfig, + XDG_DATA_HOME=xdata, VIMRUNTIME='runtime/'}} eq(1, eval('g:lua_ftdetect')) diff --git a/test/functional/normal/K_spec.lua b/test/functional/editor/K_spec.lua index 174313d80e..40f36491e4 100644 --- a/test/functional/normal/K_spec.lua +++ b/test/functional/editor/K_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, feed = - helpers.eq, helpers.clear, helpers.eval, helpers.feed +local eq, clear, eval, feed, retry = + helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.retry describe('K', function() local test_file = 'K_spec_out' @@ -31,7 +31,7 @@ describe('K', function() -- K on the text "K_spec_out" resolves to `!echo fnord >> K_spec_out`. feed('i'..test_file..'<ESC>K') - feed('<CR>') -- Press ENTER + retry(nil, nil, function() eq(1, eval('filereadable("'..test_file..'")')) end) eq({'fnord'}, eval("readfile('"..test_file.."')")) end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/editor/completion_spec.lua index a4241fe5aa..befad29922 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/editor/completion_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local clear, feed = helpers.clear, helpers.feed local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect @@ -870,7 +871,7 @@ describe('completion', function() {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | ]]) - eval('1 + 1') + assert_alive() -- popupmenu still visible screen:expect{grid=[[ foobar fooegg | diff --git a/test/functional/normal/count_spec.lua b/test/functional/editor/count_spec.lua index 94f741250a..94f741250a 100644 --- a/test/functional/normal/count_spec.lua +++ b/test/functional/editor/count_spec.lua diff --git a/test/functional/normal/fold_spec.lua b/test/functional/editor/fold_spec.lua index 00e83bedc8..00e83bedc8 100644 --- a/test/functional/normal/fold_spec.lua +++ b/test/functional/editor/fold_spec.lua diff --git a/test/functional/normal/jump_spec.lua b/test/functional/editor/jump_spec.lua index 9e7158e2f7..d09c20f226 100644 --- a/test/functional/normal/jump_spec.lua +++ b/test/functional/editor/jump_spec.lua @@ -5,7 +5,7 @@ local command = helpers.command local eq = helpers.eq local funcs = helpers.funcs local feed = helpers.feed -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture local write_file = helpers.write_file describe('jumplist', function() @@ -78,7 +78,7 @@ describe("jumpoptions=stack behaves like 'tagstack'", function() feed('<C-O>') feed('<C-O>') - eq( '\n' + eq( '' .. ' jump line col file/text\n' .. ' 4 102 0 \n' .. ' 3 1 0 Line 1\n' @@ -87,11 +87,11 @@ describe("jumpoptions=stack behaves like 'tagstack'", function() .. '> 0 30 0 Line 30\n' .. ' 1 40 0 Line 40\n' .. ' 2 50 0 Line 50', - redir_exec('jumps')) + exec_capture('jumps')) feed('90gg') - eq( '\n' + eq( '' .. ' jump line col file/text\n' .. ' 5 102 0 \n' .. ' 4 1 0 Line 1\n' @@ -99,14 +99,14 @@ describe("jumpoptions=stack behaves like 'tagstack'", function() .. ' 2 20 0 Line 20\n' .. ' 1 30 0 Line 30\n' .. '>', - redir_exec('jumps')) + exec_capture('jumps')) end) it('does not add the same location twice adjacently', function() feed('60gg') feed('60gg') - eq( '\n' + eq( '' .. ' jump line col file/text\n' .. ' 7 102 0 \n' .. ' 6 1 0 Line 1\n' @@ -116,14 +116,14 @@ describe("jumpoptions=stack behaves like 'tagstack'", function() .. ' 2 40 0 Line 40\n' .. ' 1 50 0 Line 50\n' .. '>', - redir_exec('jumps')) + exec_capture('jumps')) end) it('does add the same location twice nonadjacently', function() feed('10gg') feed('20gg') - eq( '\n' + eq( '' .. ' jump line col file/text\n' .. ' 8 102 0 \n' .. ' 7 1 0 Line 1\n' @@ -134,6 +134,6 @@ describe("jumpoptions=stack behaves like 'tagstack'", function() .. ' 2 50 0 Line 50\n' .. ' 1 10 0 Line 10\n' .. '>', - redir_exec('jumps')) + exec_capture('jumps')) end) end) diff --git a/test/functional/normal/lang_spec.lua b/test/functional/editor/lang_spec.lua index 24d1262f5f..24d1262f5f 100644 --- a/test/functional/normal/lang_spec.lua +++ b/test/functional/editor/lang_spec.lua diff --git a/test/functional/normal/langmap_spec.lua b/test/functional/editor/langmap_spec.lua index e4349a22e7..e4349a22e7 100644 --- a/test/functional/normal/langmap_spec.lua +++ b/test/functional/editor/langmap_spec.lua diff --git a/test/functional/normal/macro_spec.lua b/test/functional/editor/macro_spec.lua index 102d8fc723..102d8fc723 100644 --- a/test/functional/normal/macro_spec.lua +++ b/test/functional/editor/macro_spec.lua diff --git a/test/functional/insert/insert_spec.lua b/test/functional/editor/meta_key_spec.lua index 330cfbd830..2a9541ba96 100644 --- a/test/functional/insert/insert_spec.lua +++ b/test/functional/editor/meta_key_spec.lua @@ -1,32 +1,40 @@ local helpers = require('test.functional.helpers')(after_each) local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command = helpers.command -local eq = helpers.eq local expect = helpers.expect local funcs = helpers.funcs +local eq = helpers.eq -describe('insert-mode', function() +describe('meta-keys #8226 #13042', function() before_each(function() clear() end) - it('CTRL-@', function() - -- Inserts last-inserted text, leaves insert-mode. + it('ALT/META, normal-mode', function() + -- Unmapped ALT-chords behave as ESC+c insert('hello') - feed('i<C-@>x') - expect('hellhello') - - -- C-Space is the same as C-@. - -- CTRL-SPC inserts last-inserted text, leaves insert-mode. - feed('i<C-Space>x') - expect('hellhellhello') + 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) - -- CTRL-A inserts last inserted text - feed('i<C-A>x') - expect('hellhellhellhelloxo') + it('ALT/META, visual-mode', 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) - it('ALT/META #8213', function() + it('ALT/META insert-mode', function() -- Mapped ALT-chord behaves as mapped. command('inoremap <M-l> meta-l') command('inoremap <A-j> alt-j') diff --git a/test/functional/cmdline/history_spec.lua b/test/functional/editor/mode_cmdline_spec.lua index ee2d36f642..0f7d821bb5 100644 --- a/test/functional/cmdline/history_spec.lua +++ b/test/functional/editor/mode_cmdline_spec.lua @@ -1,8 +1,41 @@ +-- Cmdline-mode tests. + local helpers = require('test.functional.helpers')(after_each) -local clear, meths, funcs, eq = - helpers.clear, helpers.meths, helpers.funcs, helpers.eq +local clear, insert, funcs, eq, feed = + helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed +local meths = helpers.meths + +describe('cmdline CTRL-R', function() + before_each(clear) + + it('pasting non-special register inserts <CR> *between* lines', function() + insert([[ + line1abc + line2somemoretext + ]]) + -- Yank 2 lines linewise, then paste to cmdline. + feed([[<C-\><C-N>gg0yj:<C-R>0]]) + -- <CR> inserted between lines, NOT after the final line. + eq('line1abc\rline2somemoretext', funcs.getcmdline()) + + -- Yank 2 lines charwise, then paste to cmdline. + feed([[<C-\><C-N>gg05lyvj:<C-R>0]]) + -- <CR> inserted between lines, NOT after the final line. + eq('abc\rline2', funcs.getcmdline()) + + -- Yank 1 line linewise, then paste to cmdline. + feed([[<C-\><C-N>ggyy:<C-R>0]]) + -- No <CR> inserted. + eq('line1abc', funcs.getcmdline()) + end) + + it('pasting special register inserts <CR>, <NL>', function() + feed([[:<C-R>="foo\nbar\rbaz"<CR>]]) + eq('foo\nbar\rbaz', funcs.getcmdline()) + end) +end) -describe('history support code', function() +describe('cmdline history', function() before_each(clear) it('correctly clears start of the history', function() diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua new file mode 100644 index 0000000000..46ab483036 --- /dev/null +++ b/test/functional/editor/mode_insert_spec.lua @@ -0,0 +1,89 @@ +-- Insert-mode tests. + +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert +local expect = helpers.expect +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval +local meths = helpers.meths + +describe('insert-mode', function() + before_each(function() + clear() + end) + + it('CTRL-@', function() + -- Inserts last-inserted text, leaves insert-mode. + insert('hello') + feed('i<C-@>x') + expect('hellhello') + + -- C-Space is the same as C-@. + -- CTRL-SPC inserts last-inserted text, leaves insert-mode. + feed('i<C-Space>x') + expect('hellhellhello') + + -- CTRL-A inserts last inserted text + feed('i<C-A>x') + expect('hellhellhellhelloxo') + end) + + describe('Ctrl-R', function() + it('works', function() + command("let @@ = 'test'") + feed('i<C-r>"') + expect('test') + end) + + it('works with multi-byte text', function() + command("let @@ = 'påskägg'") + feed('i<C-r>"') + expect('påskägg') + end) + end) + + describe('Ctrl-O', function() + it('enters command mode for one command', function() + feed('ihello world<C-o>') + feed(':let ctrlo = "test"<CR>') + feed('iii') + expect('hello worldiii') + eq(1, eval('ctrlo ==# "test"')) + end) + + it('re-enters insert mode at the end of the line when running startinsert', function() + -- #6962 + feed('ihello world<C-o>') + feed(':startinsert<CR>') + feed('iii') + expect('hello worldiii') + end) + + it('re-enters insert mode at the beginning of the line when running startinsert', function() + insert('hello world') + feed('0<C-o>') + feed(':startinsert<CR>') + feed('aaa') + expect('aaahello world') + end) + + it('re-enters insert mode in the middle of the line when running startinsert', function() + insert('hello world') + feed('bi<C-o>') + feed(':startinsert<CR>') + feed('ooo') + expect('hello oooworld') + end) + + it("doesn't cancel Ctrl-O mode when processing event", function() + feed('iHello World<c-o>') + eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event + eq(2, eval('1+1')) -- causes K_EVENT key + eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode + feed('dd') + eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode + expect('') -- executed the command + end) + end) +end) diff --git a/test/functional/editor/mode_visual_spec.lua b/test/functional/editor/mode_visual_spec.lua new file mode 100644 index 0000000000..468ae00e01 --- /dev/null +++ b/test/functional/editor/mode_visual_spec.lua @@ -0,0 +1,27 @@ +-- Visual-mode tests. + +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local expect = helpers.expect +local feed = helpers.feed +local meths = helpers.meths + +describe('visual-mode', function() + before_each(clear) + + it("select-mode Ctrl-O doesn't cancel Ctrl-O mode when processing event #15688", function() + feed('iHello World<esc>gh<c-o>') + eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event + eq({mode='vs', blocking=false}, meths.get_mode()) -- again #15288 + eq(2, eval('1+1')) -- causes K_EVENT key + eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode + feed('^') + eq({mode='s', blocking=false}, meths.get_mode()) -- left ctrl-o mode + feed('h') + eq({mode='i', blocking=false}, meths.get_mode()) -- entered insert mode + expect('h') -- selection is the whole line and is replaced + end) +end) + diff --git a/test/functional/normal/put_spec.lua b/test/functional/editor/put_spec.lua index 26967ecbba..26967ecbba 100644 --- a/test/functional/normal/put_spec.lua +++ b/test/functional/editor/put_spec.lua diff --git a/test/functional/normal/search_spec.lua b/test/functional/editor/search_spec.lua index d5df131725..d5df131725 100644 --- a/test/functional/normal/search_spec.lua +++ b/test/functional/editor/search_spec.lua diff --git a/test/functional/normal/tabpage_spec.lua b/test/functional/editor/tabpage_spec.lua index d1d6854b07..d1d6854b07 100644 --- a/test/functional/normal/tabpage_spec.lua +++ b/test/functional/editor/tabpage_spec.lua diff --git a/test/functional/normal/undo_spec.lua b/test/functional/editor/undo_spec.lua index a023ca3d90..a023ca3d90 100644 --- a/test/functional/normal/undo_spec.lua +++ b/test/functional/editor/undo_spec.lua diff --git a/test/functional/eval/backtick_expansion_spec.lua b/test/functional/eval/backtick_expansion_spec.lua deleted file mode 100644 index b1b44cfa8b..0000000000 --- a/test/functional/eval/backtick_expansion_spec.lua +++ /dev/null @@ -1,50 +0,0 @@ -local lfs = require('lfs') -local helpers = require('test.functional.helpers')(after_each) -local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq -local write_file = helpers.write_file - -describe("backtick expansion", function() - setup(function() - clear() - lfs.mkdir("test-backticks") - write_file("test-backticks/file1", "test file 1") - write_file("test-backticks/file2", "test file 2") - write_file("test-backticks/file3", "test file 3") - lfs.mkdir("test-backticks/subdir") - write_file("test-backticks/subdir/file4", "test file 4") - -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. - command('silent cd test-backticks') - end) - - teardown(function() - helpers.rmdir('test-backticks') - end) - - it("with default 'shell'", function() - if helpers.iswin() then - command(":silent args `dir /b *2`") - else - command(":silent args `echo ***2`") - end - eq({ "file2", }, eval("argv()")) - if helpers.iswin() then - command(":silent args `dir /s/b *4`") - eq({ "subdir\\file4", }, eval("map(argv(), 'fnamemodify(v:val, \":.\")')")) - else - command(":silent args `echo */*4`") - eq({ "subdir/file4", }, eval("argv()")) - end - end) - - it("with shell=fish", function() - if eval("executable('fish')") == 0 then - pending('missing "fish" command') - return - end - command("set shell=fish") - command(":silent args `echo ***2`") - eq({ "file2", }, eval("argv()")) - command(":silent args `echo */*4`") - eq({ "subdir/file4", }, eval("argv()")) - end) -end) diff --git a/test/functional/eval/function_spec.lua b/test/functional/eval/function_spec.lua deleted file mode 100644 index ce8850fcc2..0000000000 --- a/test/functional/eval/function_spec.lua +++ /dev/null @@ -1,37 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local eq = helpers.eq -local matches = helpers.matches -local exc_exec = helpers.exc_exec -local iswin = helpers.iswin -local eval = helpers.eval - -describe('Up to MAX_FUNC_ARGS arguments are handled by', function() - local max_func_args = 20 -- from eval.h - local range = helpers.funcs.range - - before_each(clear) - - it('printf()', function() - local printf = helpers.funcs.printf - local rep = helpers.funcs['repeat'] - local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' - eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) - local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') - eq('Vim(call):E740: Too many arguments for function printf', ret) - end) - - it('rpcnotify()', function() - local rpcnotify = helpers.funcs.rpcnotify - local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) - eq(1, ret) - ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') - eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) - end) -end) - -it('windowsversion()', function() - clear() - matches(iswin() and '^%d+%.%d+$' or '^$', eval('windowsversion()')) -end) diff --git a/test/functional/eval/interrupt_spec.lua b/test/functional/eval/interrupt_spec.lua deleted file mode 100644 index 05b1f4ff57..0000000000 --- a/test/functional/eval/interrupt_spec.lua +++ /dev/null @@ -1,61 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local command = helpers.command -local meths = helpers.meths -local clear = helpers.clear -local sleep = helpers.sleep -local poke_eventloop = helpers.poke_eventloop -local feed = helpers.feed -local eq = helpers.eq - -local dur -local min_dur = 8 -local len = 131072 - -describe('List support code', function() - if not pending('does not actually allows interrupting with just got_int', function() end) then return end - -- The following tests are confirmed to work with os_breakcheck() just before - -- `if (got_int) {break;}` in tv_list_copy and list_join_inner() and not to - -- work without. - setup(function() - clear() - dur = 0 - while true do - command(([[ - let rt = reltime() - let bl = range(%u) - let dur = reltimestr(reltime(rt)) - ]]):format(len)) - dur = tonumber(meths.get_var('dur')) - if dur >= min_dur then - -- print(('Using len %u, dur %g'):format(len, dur)) - break - else - len = len * 2 - end - end - end) - it('allows interrupting copy', function() - feed(':let t_rt = reltime()<CR>:let t_bl = copy(bl)<CR>') - sleep(min_dur / 16 * 1000) - feed('<C-c>') - 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 - eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) - end - end) - it('allows interrupting join', function() - feed(':let t_rt = reltime()<CR>:let t_j = join(bl)<CR>') - sleep(min_dur / 16 * 1000) - feed('<C-c>') - 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)) - if t_dur >= dur / 8 then - eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) - end - end) -end) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index e5c9a20db3..21adcf37da 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source local insert = helpers.insert local eq, next_msg = helpers.eq, helpers.next_msg @@ -325,7 +326,7 @@ describe('VimL dictionary notifications', function() ]]) command('call MakeWatch()') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) @@ -354,7 +355,7 @@ describe('VimL dictionary notifications', function() command([[call dictwatcherdel(b:, 'changedtick', 'OnTickChanged')]]) insert('t'); - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it('does not cause use-after-free when unletting from callback', function() diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua index ef53fe75e3..9d84a2d4f6 100644 --- a/test/functional/ex_cmds/drop_spec.lua +++ b/test/functional/ex_cmds/drop_spec.lua @@ -55,6 +55,7 @@ describe(":drop", function() end) it("splits off a new window when a buffer can't be abandoned", function() + command("set nohidden") feed_command("edit tmp1") feed_command("vsplit") feed_command("edit tmp2") diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua index 404dc39ad2..d320425de1 100644 --- a/test/functional/ex_cmds/echo_spec.lua +++ b/test/functional/ex_cmds/echo_spec.lua @@ -10,7 +10,7 @@ local source = helpers.source local dedent = helpers.dedent local command = helpers.command local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture local matches = helpers.matches describe(':echo :echon :echomsg :echoerr', function() @@ -199,10 +199,8 @@ describe(':echo :echon :echomsg :echoerr', function() let d.tdr = TestDictRef ]]) eq(dedent([[ - - function('TestDict', {'tdr': function('TestDict', {...@1})}) function('TestDict', {'tdr': function('TestDict', {...@1})})]]), - redir_exec('echo String(d.tdr)')) + exec_capture('echo String(d.tdr)')) end) it('dumps automatically created partials', function() @@ -229,10 +227,8 @@ describe(':echo :echon :echomsg :echoerr', function() function() meths.set_var('d', {v=true}) eq(dedent([[ - - {'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true} {'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]]), - redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) + exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) end) it('does not show errors when dumping partials referencing the same dictionary', @@ -256,10 +252,8 @@ describe(':echo :echon :echomsg :echoerr', function() -- test/unit/api/private_helpers_spec.lua. eval('add(l, function("Test1", l))') eq(dedent([=[ - - function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])]) function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])]=]), - redir_exec('echo String(function("Test1", l))')) + exec_capture('echo String(function("Test1", l))')) end) it('does not crash or halt when dumping partials with reference cycles in self and arguments', @@ -270,10 +264,8 @@ describe(':echo :echon :echomsg :echoerr', function() eval('add(l, function("Test1", l))') eval('add(l, function("Test1", d))') eq(dedent([=[ - - {'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true} {'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]=]), - redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) + exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) end) end) @@ -303,8 +295,8 @@ describe(':echo :echon :echomsg :echoerr', function() it('dumps recursive lists without error', function() meths.set_var('l', {}) eval('add(l, l)') - eq('\n[[...@0]]\n[[...@0]]', redir_exec('echo String(l)')) - eq('\n[[[...@1]]]\n[[[...@1]]]', redir_exec('echo String([l])')) + eq('[[...@0]]', exec_capture('echo String(l)')) + eq('[[[...@1]]]', exec_capture('echo String([l])')) end) end) @@ -333,10 +325,10 @@ describe(':echo :echon :echomsg :echoerr', function() it('dumps recursive dictionaries without the error', function() meths.set_var('d', {d=1}) eval('extend(d, {"d": d})') - eq('\n{\'d\': {...@0}}\n{\'d\': {...@0}}', - redir_exec('echo String(d)')) - eq('\n{\'out\': {\'d\': {...@1}}}\n{\'out\': {\'d\': {...@1}}}', - redir_exec('echo String({"out": d})')) + eq('{\'d\': {...@0}}', + exec_capture('echo String(d)')) + eq('{\'out\': {\'d\': {...@1}}}', + exec_capture('echo String({"out": d})')) end) end) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 949724bb53..09eaa36686 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -43,7 +43,7 @@ describe(':mksession', function() -- Restore session. command('source '..session_file) - eq({3,3,2}, + eq({2,2,4}, {funcs.winbufnr(1), funcs.winbufnr(2), funcs.winbufnr(3)}) end) @@ -91,7 +91,12 @@ describe(':mksession', function() command('tabnext 1') eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '1', funcs.expand('%:p')) command('tabnext 2') - eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p')) + -- :mksession stores paths using unix slashes, but Nvim doesn't adjust these + -- for absolute paths in all cases yet. Absolute paths are used in the + -- session file after :tcd, so we need to expect unix slashes here for now + -- eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p')) + eq(cwd_dir:gsub([[\]], '/') .. '/' .. tmpfile_base .. '2', + funcs.expand('%:p')) end) it('restores CWD for :terminal buffers #11288', function() diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua index 3392a90270..c956a2df2d 100644 --- a/test/functional/ex_cmds/quickfix_commands_spec.lua +++ b/test/functional/ex_cmds/quickfix_commands_spec.lua @@ -37,9 +37,9 @@ for _, c in ipairs({'l', 'c'}) do -- Second line of each entry (i.e. `nr=-1, …`) was obtained from actual -- results. First line (i.e. `{lnum=…`) was obtained from legacy test. local list = { - {lnum=700, col=10, text='Line 700', module='', + {lnum=700, end_lnum=0, col=10, end_col=0, text='Line 700', module='', nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, - {lnum=800, col=15, text='Line 800', module='', + {lnum=800, end_lnum=0, col=15, end_col=0, text='Line 800', module='', nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, } eq(list, getlist()) @@ -47,6 +47,7 @@ for _, c in ipairs({'l', 'c'}) do eq(('%s-2.res'):format(file), funcs.bufname(list[2].bufnr)) -- Run cfile/lfile from a modified buffer + command('set nohidden') command('enew!') curbufmeths.set_lines(1, 1, true, {'Quickfix'}) eq(('Vim(%s):E37: No write since last change (add ! to override)'):format( @@ -58,7 +59,7 @@ for _, c in ipairs({'l', 'c'}) do ]]):format(file)) command(('%s %s'):format(addfcmd, file)) list[#list + 1] = { - lnum=900, col=30, text='Line 900', module='', + lnum=900, end_lnum=0, col=30, end_col=0, text='Line 900', module='', nr=-1, bufnr=5, valid=1, pattern='', vcol=0, ['type']='', } eq(list, getlist()) @@ -71,9 +72,9 @@ for _, c in ipairs({'l', 'c'}) do command('enew!') command(('%s %s'):format(getfcmd, file)) list = { - {lnum=222, col=77, text='Line 222', module='', + {lnum=222, end_lnum=0, col=77, end_col=0, text='Line 222', module='', nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, - {lnum=333, col=88, text='Line 333', module='', + {lnum=333, end_lnum=0, col=88, end_col=0, text='Line 333', module='', nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, } eq(list, getlist()) diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index 37c97f519a..bdf6ae76d1 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -8,6 +8,8 @@ local feed = helpers.feed local feed_command = helpers.feed_command local write_file = helpers.write_file local exec = helpers.exec +local exc_exec = helpers.exc_exec +local exec_lua = helpers.exec_lua local eval = helpers.eval local exec_capture = helpers.exec_capture local neq = helpers.neq @@ -18,16 +20,30 @@ describe(':source', function() end) it('current buffer', function() - insert('let a = 2') + insert([[ + let a = 2 + let b = #{ + \ k: "v" + "\ (o_o) + \ }]]) + command('source') eq('2', meths.exec('echo a', true)) + eq("{'k': 'v'}", meths.exec('echo b', true)) + + exec('set cpoptions+=C') + eq('Vim(let):E15: Invalid expression: #{', exc_exec('source')) end) it('selection in current buffer', function() - insert( - 'let a = 2\n'.. - 'let a = 3\n'.. - 'let a = 4\n') + insert([[ + let a = 2 + let a = 3 + let a = 4 + let b = #{ + "\ (>_<) + \ K: "V" + \ }]]) -- Source the 2nd line only feed('ggjV') @@ -38,13 +54,26 @@ describe(':source', function() feed('ggjVG') feed_command(':source') eq('4', meths.exec('echo a', true)) + eq("{'K': 'V'}", meths.exec('echo b', true)) + + exec('set cpoptions+=C') + eq('Vim(let):E15: Invalid expression: #{', exc_exec("'<,'>source")) + end) + + it('does not break if current buffer is modified while sourced', function() + insert [[ + bwipeout! + let a = 123 + ]] + command('source') + eq('123', meths.exec('echo a', true)) end) it('multiline heredoc command', function() - insert( - 'lua << EOF\n'.. - 'y = 4\n'.. - 'EOF\n') + insert([[ + lua << EOF + y = 4 + EOF]]) command('source') eq('4', meths.exec('echo luaeval("y")', true)) @@ -67,13 +96,21 @@ describe(':source', function() vim.g.b = 5 vim.g.b = 6 vim.g.b = 7 + a = [=[ + "\ a + \ b]=] ]]) command('edit '..test_file) + feed('ggjV') feed_command(':source') - eq(6, eval('g:b')) + + feed('GVkk') + feed_command(':source') + eq(' "\\ a\n \\ b', exec_lua('return _G.a')) + os.remove(test_file) end) @@ -84,12 +121,16 @@ describe(':source', function() vim.g.c = 10 vim.g.c = 11 vim.g.c = 12 + a = [=[ + \ 1 + "\ 2]=] ]]) command('edit '..test_file) feed_command(':source') eq(12, eval('g:c')) + eq(' \\ 1\n "\\ 2', exec_lua('return _G.a')) os.remove(test_file) end) diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index f2a381869e..d91feb4bc1 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') local eq, eval, expect, source = helpers.eq, helpers.eval, helpers.expect, helpers.source +local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command local feed = helpers.feed @@ -26,7 +27,7 @@ describe(':recover', function() -- Also check filename ending with ".swp". #9504 eq('Vim(recover):E306: Cannot open '..swapname2, pcall_err(command, 'recover '..swapname2)) -- Should not segfault. #2117 - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 4f526ddfca..32fe397c03 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -3,8 +3,7 @@ local lfs = require('lfs') local eq, eval, clear, write_file, source, insert = helpers.eq, helpers.eval, helpers.clear, helpers.write_file, helpers.source, helpers.insert -local redir_exec = helpers.redir_exec -local exc_exec = helpers.exc_exec +local pcall_err = helpers.pcall_err local command = helpers.command local feed_command = helpers.feed_command local funcs = helpers.funcs @@ -96,25 +95,24 @@ describe(':write', function() eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) -- Message from check_overwrite if not iswin() then - eq(('\nE17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), - redir_exec('write .')) + eq(('Vim(write):E17: "'..funcs.fnamemodify('.', ':p:h')..'" is a directory'), + pcall_err(command, 'write .')) end meths.set_option('writeany', true) -- Message from buf_write - eq(('\nE502: "." is a directory'), - redir_exec('write .')) + eq(('Vim(write):E502: "." is a directory'), pcall_err(command, 'write .')) funcs.mkdir(fname_bak) meths.set_option('backupdir', '.') meths.set_option('backup', true) write_file(fname, 'content0') - eq(0, exc_exec('edit ' .. fname)) + command('edit ' .. fname) funcs.setline(1, 'TTY') eq('Vim(write):E510: Can\'t make backup file (add ! to override)', - exc_exec('write')) + pcall_err(command, 'write')) meths.set_option('backup', false) funcs.setfperm(fname, 'r--------') eq('Vim(write):E505: "Xtest-functional-ex_cmds-write" is read-only (add ! to override)', - exc_exec('write')) + pcall_err(command, 'write')) if iswin() then eq(0, os.execute('del /q/f ' .. fname)) eq(0, os.execute('rd /q/s ' .. fname_bak)) @@ -127,6 +125,6 @@ describe(':write', function() if iswin() then return end lfs.link(fname_bak .. ('/xxxxx'):rep(20), fname, true) eq('Vim(write):E166: Can\'t open linked file for writing', - exc_exec('write!')) + pcall_err(command, 'write!')) end) end) diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index b7fddc8f29..8e03d9a46e 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -43,11 +43,11 @@ end local function read_message() local line = io.read("*l") local length = line:lower():match("content%-length:%s*(%d+)") - return vim.fn.json_decode(io.read(2 + length):sub(2)) + return vim.json.decode(io.read(2 + length):sub(2)) end local function send(payload) - io.stdout:write(format_message_with_content_length(vim.fn.json_encode(payload))) + io.stdout:write(format_message_with_content_length(vim.json.encode(payload))) end local function respond(id, err, result) @@ -126,6 +126,89 @@ function tests.check_workspace_configuration() } end +function tests.prepare_rename_nil() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return nil, nil + end) + notify('shutdown') + end; + } +end + +function tests.prepare_rename_placeholder() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return nil, {placeholder = 'placeholder'} + end) + expect_request('textDocument/rename', function(params) + assert_eq(params.newName, 'renameto') + return nil, nil + end) + notify('shutdown') + end; + } +end + +function tests.prepare_rename_range() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return nil, { + start = { line = 1, character = 8 }, + ['end'] = { line = 1, character = 12 }, + } + end) + expect_request('textDocument/rename', function(params) + assert_eq(params.newName, 'renameto') + return nil, nil + end) + notify('shutdown') + end; + } +end + +function tests.prepare_rename_error() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return {}, nil + end) + expect_request('textDocument/rename', function(params) + assert_eq(params.newName, 'renameto') + return nil, nil + end) + notify('shutdown') + end; + } +end + function tests.basic_check_capabilities() skeleton { on_init = function(params) @@ -481,6 +564,35 @@ function tests.decode_nil() } end + +function tests.code_action_with_resolve() + skeleton { + on_init = function() + return { + capabilities = { + codeActionProvider = { + resolveProvider = true + } + } + } + end; + body = function() + notify('start') + local cmd = { + title = 'Command 1', + command = 'dummy1' + } + expect_request('textDocument/codeAction', function() + return nil, { cmd, } + end) + expect_request('codeAction/resolve', function() + return nil, cmd + end) + notify('shutdown') + end; + } +end + -- Tests will be indexed by TEST_NAME local kill_timer = vim.loop.new_timer() diff --git a/test/functional/fixtures/middle/filen.lua b/test/functional/fixtures/middle/filen.lua new file mode 100644 index 0000000000..fce50cc776 --- /dev/null +++ b/test/functional/fixtures/middle/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "mittel") diff --git a/test/functional/fixtures/nvim/after/filen.lua b/test/functional/fixtures/nvim/after/filen.lua new file mode 100644 index 0000000000..0128a0a16a --- /dev/null +++ b/test/functional/fixtures/nvim/after/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "ordinary after") diff --git a/test/functional/fixtures/nvim/filen.lua b/test/functional/fixtures/nvim/filen.lua new file mode 100644 index 0000000000..e2bd5e8f7f --- /dev/null +++ b/test/functional/fixtures/nvim/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "ordinary") diff --git a/test/functional/fixtures/pack/foo/opt/funky/filen.lua b/test/functional/fixtures/pack/foo/opt/funky/filen.lua new file mode 100644 index 0000000000..a33b83c2a7 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/funky/filen.lua @@ -0,0 +1,12 @@ +table.insert(_G.test_loadorder, "funky!") + +if not _G.nesty then + _G.nesty = true + local save_order = _G.test_loadorder + _G.test_loadorder = {} + _G.vim.o.pp = "" -- funky! + vim.cmd [[runtime! filen.lua ]] + _G.nested_order = _G.test_loadorder + _G.test_loadorder = save_order + _G.nesty = nil +end diff --git a/test/functional/fixtures/pack/foo/opt/superspecial/after/filen.lua b/test/functional/fixtures/pack/foo/opt/superspecial/after/filen.lua new file mode 100644 index 0000000000..94bf6850b2 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/superspecial/after/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "SuperSpecial after") diff --git a/test/functional/fixtures/pack/foo/opt/superspecial/filen.lua b/test/functional/fixtures/pack/foo/opt/superspecial/filen.lua new file mode 100644 index 0000000000..cbaab1a45a --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/superspecial/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "SuperSpecial") diff --git a/test/functional/fixtures/pack/foo/start/fancyplugin/after/filen.lua b/test/functional/fixtures/pack/foo/start/fancyplugin/after/filen.lua new file mode 100644 index 0000000000..9beac762ee --- /dev/null +++ b/test/functional/fixtures/pack/foo/start/fancyplugin/after/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "FANCY after") diff --git a/test/functional/fixtures/pack/foo/start/fancyplugin/filen.lua b/test/functional/fixtures/pack/foo/start/fancyplugin/filen.lua new file mode 100644 index 0000000000..34e4b9c95e --- /dev/null +++ b/test/functional/fixtures/pack/foo/start/fancyplugin/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "FANCY") diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index b95e563932..4196716799 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -19,7 +19,7 @@ static void flush_wait(void) static void help(void) { - puts("A simple implementation of a shell for testing termopen()."); + puts("Fake shell"); puts(""); puts("Usage:"); puts(" shell-test --help"); @@ -42,6 +42,8 @@ static void help(void) puts(" 96: foo bar"); puts(" shell-test INTERACT"); puts(" Prints \"interact $ \" to stderr, and waits for \"exit\" input."); + puts(" shell-test EXIT {code}"); + puts(" Exits immediately with exit code \"{code}\"."); } int main(int argc, char **argv) @@ -103,7 +105,6 @@ int main(int argc, char **argv) char input[256]; char cmd[100]; int arg; - int input_argc; while (1) { fprintf(stderr, "interact $ "); @@ -112,8 +113,7 @@ int main(int argc, char **argv) break; // EOF } - input_argc = sscanf(input, "%99s %d", cmd, &arg); - if(1 == input_argc) { + if(1 == sscanf(input, "%99s %d", cmd, &arg)) { arg = 0; } if (strcmp(cmd, "exit") == 0) { @@ -122,6 +122,15 @@ int main(int argc, char **argv) fprintf(stderr, "command not found: %s\n", cmd); } } + } else if (strcmp(argv[1], "EXIT") == 0) { + int code = 1; + if (argc >= 3) { + if (sscanf(argv[2], "%d", &code) != 1) { + fprintf(stderr, "Invalid exit code: %s\n", argv[2]); + return 2; + } + } + return code; } else { fprintf(stderr, "Unknown first argument: %s\n", argv[1]); return 3; diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 03ef441ef3..a26e883370 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -38,10 +38,16 @@ module.nvim_prog = ( module.nvim_set = ( 'set shortmess+=IS background=light noswapfile noautoindent startofline' ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' - ..' belloff= wildoptions-=pum noshowcmd noruler nomore redrawdebug=invalid') + ..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid') module.nvim_argv = { module.nvim_prog, '-u', 'NONE', '-i', 'NONE', - '--cmd', module.nvim_set, '--embed'} + '--cmd', module.nvim_set, + '--cmd', 'unmap Y', + '--cmd', 'unmap <C-L>', + '--cmd', 'iunmap <C-U>', + '--cmd', 'iunmap <C-W>', + '--embed'} + -- Directory containing nvim. module.nvim_dir = module.nvim_prog:gsub("[/\\][^/\\]+$", "") if module.nvim_dir == module.nvim_prog then @@ -416,7 +422,7 @@ end -- Builds an argument list for use in clear(). -- ---@see clear() for parameters. +---@see clear() for parameters. function module.new_argv(...) local args = {unpack(module.nvim_argv)} table.insert(args, '--headless') @@ -567,7 +573,7 @@ function module.buf_lines(bufnr) return module.exec_lua("return vim.api.nvim_buf_get_lines((...), 0, -1, false)", bufnr) end ---@see buf_lines() +---@see buf_lines() function module.curbuf_contents() module.poke_eventloop() -- Before inspecting the buffer, do whatever. return table.concat(module.curbuf('get_lines', 0, -1, true), '\n') @@ -779,19 +785,6 @@ function module.exec_lua(code, ...) return module.meths.exec_lua(code, {...}) end -function module.redir_exec(cmd) - module.meths.set_var('__redir_exec_cmd', cmd) - module.command([[ - redir => g:__redir_exec_output - silent! execute g:__redir_exec_cmd - redir END - ]]) - local ret = module.meths.get_var('__redir_exec_output') - module.meths.del_var('__redir_exec_output') - module.meths.del_var('__redir_exec_cmd') - return ret -end - function module.get_pathsep() return iswin() and '\\' or '/' end diff --git a/test/functional/insert/ctrl_o_spec.lua b/test/functional/insert/ctrl_o_spec.lua deleted file mode 100644 index 543d0a7d68..0000000000 --- a/test/functional/insert/ctrl_o_spec.lua +++ /dev/null @@ -1,54 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local expect = helpers.expect -local feed = helpers.feed -local insert = helpers.insert -local meths = helpers.meths - -describe('insert-mode Ctrl-O', function() - before_each(clear) - - it('enters command mode for one command', function() - feed('ihello world<C-o>') - feed(':let ctrlo = "test"<CR>') - feed('iii') - expect('hello worldiii') - eq(1, eval('ctrlo ==# "test"')) - end) - - it('re-enters insert mode at the end of the line when running startinsert', function() - -- #6962 - feed('ihello world<C-o>') - feed(':startinsert<CR>') - feed('iii') - expect('hello worldiii') - end) - - it('re-enters insert mode at the beginning of the line when running startinsert', function() - insert('hello world') - feed('0<C-o>') - feed(':startinsert<CR>') - feed('aaa') - expect('aaahello world') - end) - - it('re-enters insert mode in the middle of the line when running startinsert', function() - insert('hello world') - feed('bi<C-o>') - feed(':startinsert<CR>') - feed('ooo') - expect('hello oooworld') - end) - - it("doesn't cancel Ctrl-O mode when processing event", function() - feed('iHello World<c-o>') - eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event - eq(2, eval('1+1')) -- causes K_EVENT key - eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode - feed('dd') - eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode - expect('') -- executed the command - end) -end) diff --git a/test/functional/insert/ctrl_r_spec.lua b/test/functional/insert/ctrl_r_spec.lua deleted file mode 100644 index adc3c4b406..0000000000 --- a/test/functional/insert/ctrl_r_spec.lua +++ /dev/null @@ -1,19 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed = helpers.clear, helpers.feed -local expect, command = helpers.expect, helpers.command - -describe('insert-mode Ctrl-R', function() - before_each(clear) - - it('works', function() - command("let @@ = 'test'") - feed('i<C-r>"') - expect('test') - end) - - it('works with multi-byte text', function() - command("let @@ = 'påskägg'") - feed('i<C-r>"') - expect('påskägg') - end) -end) diff --git a/test/functional/legacy/007_ball_buffer_list_spec.lua b/test/functional/legacy/007_ball_buffer_list_spec.lua index a180e73301..d4e4547c43 100644 --- a/test/functional/legacy/007_ball_buffer_list_spec.lua +++ b/test/functional/legacy/007_ball_buffer_list_spec.lua @@ -8,6 +8,9 @@ describe(':ball', function() setup(clear) it('is working', function() + -- Must disable 'hidden' so that the BufReadPost autocmd is triggered + -- when Xxx2 is reloaded + feed_command('set nohidden') insert([[ start of test file Xxx this is a test @@ -18,7 +21,7 @@ describe(':ball', function() feed('gg') -- Write test file Xxx1 - feed('A1:.,/end of/w! Xxx1<cr>') + feed('A1<esc>:.,/end of/w! Xxx1<cr>') feed_command('sp Xxx1') feed_command('close') diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua index 939404cb5e..002f037d09 100644 --- a/test/functional/legacy/008_autocommands_spec.lua +++ b/test/functional/legacy/008_autocommands_spec.lua @@ -71,6 +71,9 @@ describe('autocommands that delete and unload buffers:', function() au BufUnload * call CloseAll() au VimLeave * call WriteToOut() ]]) + -- Must disable 'hidden' so that the BufUnload autocmd is triggered between + -- each :edit + command('set nohidden') command('silent! edit Xxx2') command('silent! edit Xxx1') command('silent! edit Makefile') -- an existing file diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index 7b6f2f63e9..0fa9290f3c 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -129,13 +129,11 @@ describe('file reading, writing and bufnew and filter autocommands', function() -- Will load Xtest.c. feed_command('e! foo.c') feed_command("au FileAppendPre *.out '[,']s/new/NEW/") - feed_command('au FileAppendPost *.out !cat Xtest.c >>test.out') + feed_command('au FileAppendPost *.out !cat Xtest.c >test.out') -- Append it to the output file. feed_command('w>>test.out') -- Discard all prompts and messages. feed('<C-L>') - -- Expect the decompressed file in the buffer. - feed_command('e test.out') expect([[ /* diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua index 48dd24db9e..f666e51469 100644 --- a/test/functional/legacy/012_directory_spec.lua +++ b/test/functional/legacy/012_directory_spec.lua @@ -80,6 +80,7 @@ describe("'directory' option", function() eq({ "Xtest1.swp", "Xtest3" }, ls_dir_sorted("Xtest2")) meths.set_option('directory', 'Xtest.je') + command('bdelete') command('edit Xtest2/Xtest3') eq(true, curbufmeths.get_option('swapfile')) poke_eventloop() diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index a4f5d6ac5b..d164d9c02f 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -6,7 +6,7 @@ local Screen = require('test.functional.ui.screen') local eval, clear, command = helpers.eval, helpers.clear, helpers.command local eq, neq = helpers.eq, helpers.neq local insert = helpers.insert -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err describe('063: Test for ":match", "matchadd()" and related functions', function() setup(clear) @@ -67,9 +67,7 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( -- matchdelete throws error and returns -1 on failure neq(true, pcall(function() eval('matchdelete(42)') end)) - eq('\nE803: ID not found: 42', - redir_exec("let r2 = matchdelete(42)")) - eq(-1, eval('r2')) + eq('Vim(let):E803: ID not found: 42', pcall_err(command, 'let r2 = matchdelete(42)')) -- Check that "clearmatches()" clears all matches defined by ":match" and -- "matchadd()". @@ -105,9 +103,8 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( -- Check that "setmatches()" will not add two matches with the same ID. The -- expected behaviour (for now) is to add the first match but not the -- second and to return -1. - eq('\nE801: ID already taken: 1', - redir_exec("let r1 = setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}, {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 10, 'id': 1}])")) - eq(-1, eval("r1")) + eq('Vim(let):E801: ID already taken: 1', + pcall_err(command, "let r1 = setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}, {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 10, 'id': 1}])")) eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 1}}, eval('getmatches()')) -- Check that "setmatches()" returns 0 if successful and otherwise -1. @@ -116,14 +113,11 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( eq(0,eval("setmatches([])")) eq(0,eval("setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}])")) command("call clearmatches()") - eq('\nE714: List required', redir_exec("let rf1 = setmatches(0)")) - eq(-1, eval('rf1')) - eq('\nE474: List item 0 is either not a dictionary or an empty one', - redir_exec("let rf2 = setmatches([0])")) - eq(-1, eval('rf2')) - eq('\nE474: List item 0 is missing one of the required keys', - redir_exec("let rf3 = setmatches([{'wrong key': 'wrong value'}])")) - eq(-1, eval('rf3')) + eq('Vim(let):E714: List required', pcall_err(command, 'let rf1 = setmatches(0)')) + eq('Vim(let):E474: List item 0 is either not a dictionary or an empty one', + pcall_err(command, 'let rf2 = setmatches([0])')) + eq('Vim(let):E474: List item 0 is missing one of the required keys', + pcall_err(command, "let rf3 = setmatches([{'wrong key': 'wrong value'}])")) -- Check that "matchaddpos()" positions matches correctly insert('abcdefghijklmnopq') diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 67c5750033..6a2e86ccb4 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -156,10 +156,12 @@ describe('argument list commands', function() eq({'a', 'b', 'a', 'c'}, eval('argv()')) command('0argedit x') eq({'x', 'a', 'b', 'a', 'c'}, eval('argv()')) + command('set nohidden') command('enew! | set modified') assert_fails('argedit y', 'E37:') command('argedit! y') eq({'x', 'y', 'y', 'a', 'b', 'a', 'c'}, eval('argv()')) + command('set hidden') command('%argd') command('argedit a b') eq({'a', 'b'}, eval('argv()')) diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua index 515d6d91b8..c2b22472bf 100644 --- a/test/functional/legacy/assert_spec.lua +++ b/test/functional/legacy/assert_spec.lua @@ -26,6 +26,14 @@ describe('assert function:', function() call('assert_beeps', 'normal 0') expected_errors({'command did not beep: normal 0'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, 'normal h'->assert_beeps()) + call assert_equal(1, 'normal 0'->assert_beeps()) + ]] + expected_errors({tmpname .. ' line 2: command did not beep: normal 0'}) + end) end) -- assert_equal({expected}, {actual}, [, {msg}]) @@ -133,6 +141,14 @@ describe('assert function:', function() call('assert_false', {}) expected_errors({'Expected False but got []'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, v:false->assert_false()) + call assert_equal(1, 123->assert_false()) + ]] + expected_errors({tmpname .. ' line 2: Expected False but got 123'}) + end) end) -- assert_true({actual}, [, {msg}]) @@ -148,6 +164,14 @@ describe('assert function:', function() eq(1, call('assert_true', 1.5)) expected_errors({'Expected True but got 1.5'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, v:true->assert_true()) + call assert_equal(1, 0->assert_true()) + ]] + expected_errors({tmpname .. ' line 2: Expected True but got 0'}) + end) end) describe('v:errors', function() @@ -223,6 +247,15 @@ describe('assert function:', function() call('assert_match', 'bar.*foo', 'foobar', 'wrong') expected_errors({"wrong: Pattern 'bar.*foo' does not match 'foobar'"}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'foobar'->assert_match('bar.*foo', 'wrong')) + ]] + expected_errors({ + tmpname .. " line 1: wrong: Pattern 'bar.*foo' does not match 'foobar'" + }) + end) end) -- assert_notmatch({pat}, {text}[, {msg}]) @@ -237,6 +270,13 @@ describe('assert function:', function() call('assert_notmatch', 'foo', 'foobar') expected_errors({"Pattern 'foo' does match 'foobar'"}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'foobar'->assert_notmatch('foo')) + ]] + expected_errors({tmpname .. " line 1: Pattern 'foo' does match 'foobar'"}) + end) end) -- assert_fails({cmd}, [, {error}]) @@ -267,6 +307,15 @@ describe('assert function:', function() eq(1, eval([[assert_fails('echo', '', 'echo command')]])) expected_errors({'command did not fail: echo command'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'echo'->assert_fails('', 'echo command')) + ]] + expected_errors({ + tmpname .. ' line 1: command did not fail: echo command' + }) + end) end) -- assert_inrange({lower}, {upper}, {actual}[, {msg}]) @@ -292,6 +341,15 @@ describe('assert function:', function() eq('Vim(call):E119: Not enough arguments for function: assert_inrange', exc_exec("call assert_inrange(1, 1)")) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, 5->assert_inrange(5, 7)) + call assert_equal(0, 7->assert_inrange(5, 7)) + call assert_equal(1, 8->assert_inrange(5, 7)) + ]] + expected_errors({tmpname .. ' line 3: Expected range 5 - 7, but got 8'}) + end) end) -- assert_report({msg}) @@ -302,6 +360,13 @@ describe('assert function:', function() command('call remove(v:errors, 0)') expected_empty() end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'also wrong'->assert_report()) + ]] + expected_errors({tmpname .. ' line 1: also wrong'}) + end) end) -- assert_exception({cmd}, [, {error}]) diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 0c7e43bf31..1914818215 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -3,7 +3,7 @@ local nvim = helpers.meths local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq local curbuf, buf = helpers.curbuf, helpers.bufmeths local curwin = helpers.curwin -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture local source, command = helpers.source, helpers.command local function declare_hook_function() @@ -102,13 +102,13 @@ local function get_new_window_number() local old_win = curwin() command('botright new') local new_win = curwin() - local new_winnr = redir_exec('echo winnr()') + local new_winnr = exec_capture('echo winnr()') command('wincmd p') -- move previous window neq(old_win, new_win) eq(old_win, curwin()) - return new_winnr:gsub('\n', '') + return new_winnr end describe('au OptionSet', function() diff --git a/test/functional/legacy/cdo_spec.lua b/test/functional/legacy/cdo_spec.lua index 5e46431cc1..8b3216cbfd 100644 --- a/test/functional/legacy/cdo_spec.lua +++ b/test/functional/legacy/cdo_spec.lua @@ -91,7 +91,8 @@ describe('cdo', function() exe "silent! 4,5" . XdoCmd call assert_equal([], l) - " Run commands from an unsaved buffer + " Run commands from an unsaved buffer when 'hidden' is unset + set nohidden let v:errmsg='' let l = [] enew @@ -108,6 +109,7 @@ describe('cdo', function() if subst_count != 1 || getline('.') != 'xLine1' call add(v:errors, 'Abort command on error test failed') endif + set hidden let l = [] exe "2,2" . Xdo . "! call add(l, GetRuler())" diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua index ee9bd29fc4..b5de5cd232 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -1,6 +1,7 @@ -- Test for various eval features. local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive 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 @@ -506,7 +507,7 @@ describe('eval', function() command("call setreg('0',x)") -- nvim didn't crash and "0 was emptied - eq(2, eval("1+1")) + assert_alive() eq({}, eval("getreg('0',1,1)")) -- x is a mutable list @@ -599,7 +600,6 @@ describe('eval', function() command([[call ErrExe('call setreg(1)')]]) command([[call ErrExe('call setreg(1, 2, 3, 4)')]]) command([=[call ErrExe('call setreg([], 2)')]=]) - command([[call ErrExe('call setreg(1, {})')]]) command([=[call ErrExe('call setreg(1, 2, [])')]=]) command([=[call ErrExe('call setreg("/", ["1", "2"])')]=]) command([=[call ErrExe('call setreg("=", ["1", "2"])')]=]) @@ -614,8 +614,6 @@ describe('eval', function() Vim(call):E118: Too many arguments for function: setreg Executing call setreg([], 2) Vim(call):E730: using List as a String - Executing call setreg(1, {}) - Vim(call):E731: using Dictionary as a String Executing call setreg(1, 2, []) Vim(call):E730: using List as a String Executing call setreg("/", ["1", "2"]) diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua index 50236e8617..d3ff86d349 100644 --- a/test/functional/legacy/fixeol_spec.lua +++ b/test/functional/legacy/fixeol_spec.lua @@ -23,8 +23,6 @@ describe('fixeol', function() it('is working', function() -- First write two test files – with and without trailing EOL. - -- Use Unix fileformat for consistency. - feed_command('set ff=unix') feed_command('enew!') feed('awith eol<esc>:w! XXEol<cr>') feed_command('enew!') @@ -40,7 +38,7 @@ describe('fixeol', function() feed_command('e! XXNoEol') feed('ostays without<esc>:set nofixeol<cr>') feed_command('w! XXTestNoEol') - feed_command('bwipe XXEol XXNoEol XXTestEol XXTestNoEol') + feed_command('bwipe! XXEol XXNoEol XXTestEol XXTestNoEol') feed_command('set fixeol') -- Append "END" to each file so that we can see what the last written char was. diff --git a/test/functional/legacy/listchars_spec.lua b/test/functional/legacy/listchars_spec.lua index cffb9fd376..dc6ccd3628 100644 --- a/test/functional/legacy/listchars_spec.lua +++ b/test/functional/legacy/listchars_spec.lua @@ -8,7 +8,7 @@ local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers describe("'listchars'", function() before_each(function() clear() - feed_command('set listchars&vi') + feed_command('set listchars=eol:$') end) -- luacheck: ignore 613 (Trailing whitespace in a string) diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index 0f2d77093a..d86caca0e9 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -168,6 +168,9 @@ describe('memory usage', function() end) it('releases memory when closing windows when folds exist', function() + if helpers.is_os('mac') then + pending('macOS memory compression causes flakiness') + end local pid = eval('getpid()') source([[ new diff --git a/test/functional/legacy/mksession_spec.lua b/test/functional/legacy/mksession_spec.lua index a2af891107..bca9cd833c 100644 --- a/test/functional/legacy/mksession_spec.lua +++ b/test/functional/legacy/mksession_spec.lua @@ -12,7 +12,7 @@ describe('mksession', function() end) it('supports "skiprtp" value', function() - command('set sessionoptions&vi') + command('set sessionoptions+=options') command('set rtp+=$HOME') command('set pp+=$HOME') command('mksession! Xtest_mks.out') diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua index 896554f7a3..fdf79d55b2 100644 --- a/test/functional/lua/api_spec.lua +++ b/test/functional/lua/api_spec.lua @@ -15,7 +15,7 @@ describe('luaeval(vim.api.…)', function() describe('nvim_buf_get_lines', function() it('works', function() funcs.setline(1, {"abc", "def", "a\nb", "ttt"}) - eq({{_TYPE={}, _VAL={'a\nb'}}}, + eq({'a\000b'}, funcs.luaeval('vim.api.nvim_buf_get_lines(1, 2, 3, false)')) end) end) @@ -23,7 +23,7 @@ describe('luaeval(vim.api.…)', function() it('works', function() funcs.setline(1, {"abc", "def", "a\nb", "ttt"}) eq(NIL, funcs.luaeval('vim.api.nvim_buf_set_lines(1, 1, 2, false, {"b\\0a"})')) - eq({'abc', {_TYPE={}, _VAL={'b\na'}}, {_TYPE={}, _VAL={'a\nb'}}, 'ttt'}, + eq({'abc', 'b\000a', 'a\000b', 'ttt'}, funcs.luaeval('vim.api.nvim_buf_get_lines(1, 0, 4, false)')) end) end) @@ -64,15 +64,18 @@ describe('luaeval(vim.api.…)', function() it('correctly converts from API objects', function() eq(1, funcs.luaeval('vim.api.nvim_eval("1")')) eq('1', funcs.luaeval([[vim.api.nvim_eval('"1"')]])) + eq('Blobby', funcs.luaeval('vim.api.nvim_eval("0z426c6f626279")')) eq({}, funcs.luaeval('vim.api.nvim_eval("[]")')) eq({}, funcs.luaeval('vim.api.nvim_eval("{}")')) eq(1, funcs.luaeval('vim.api.nvim_eval("1.0")')) + eq('\000', funcs.luaeval('vim.api.nvim_eval("0z00")')) eq(true, funcs.luaeval('vim.api.nvim_eval("v:true")')) eq(false, funcs.luaeval('vim.api.nvim_eval("v:false")')) eq(NIL, funcs.luaeval('vim.api.nvim_eval("v:null")')) eq(0, eval([[type(luaeval('vim.api.nvim_eval("1")'))]])) eq(1, eval([[type(luaeval('vim.api.nvim_eval("''1''")'))]])) + eq(1, eval([[type(luaeval('vim.api.nvim_eval("0zbeef")'))]])) eq(3, eval([[type(luaeval('vim.api.nvim_eval("[]")'))]])) eq(4, eval([[type(luaeval('vim.api.nvim_eval("{}")'))]])) eq(5, eval([[type(luaeval('vim.api.nvim_eval("1.0")'))]])) diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index a5b613f0b2..073927bf22 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -978,6 +978,22 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + it("visual paste", function() + local check_events= setup_eventcheck(verify, { "aaa {", "b", "}" }) + -- Setting up + feed[[jdd]] + check_events { + { "test1", "bytes", 1, 3, 1, 0, 6, 1, 0, 2, 0, 0, 0 }; + } + + -- Actually testing + feed[[v%p]] + check_events { + { "test1", "bytes", 1, 8, 0, 4, 4, 1, 1, 3, 0, 0, 0 }; + { "test1", "bytes", 1, 8, 0, 4, 4, 0, 0, 0, 2, 0, 3 }; + } + end) + it("nvim_buf_set_lines", function() local check_events = setup_eventcheck(verify, {"AAA", "BBB"}) diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index f2a1b7dede..2e0d0ea899 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -15,14 +15,14 @@ local command = helpers.command local exc_exec = helpers.exc_exec local pcall_err = helpers.pcall_err local write_file = helpers.write_file -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture local curbufmeths = helpers.curbufmeths before_each(clear) describe(':lua command', function() it('works', function() - eq('', redir_exec( + eq('', exec_capture( 'lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"TEST"})')) eq({'', 'TEST'}, curbufmeths.get_lines(0, 100, false)) source(dedent([[ @@ -67,18 +67,19 @@ describe(':lua command', function() eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) end) it('preserves global and not preserves local variables', function() - eq('', redir_exec('lua gvar = 42')) - eq('', redir_exec('lua local lvar = 100500')) + eq('', exec_capture('lua gvar = 42')) + eq('', exec_capture('lua local lvar = 100500')) eq(NIL, funcs.luaeval('lvar')) eq(42, funcs.luaeval('gvar')) end) it('works with long strings', function() local s = ('x'):rep(100500) - eq('\nE5107: Error loading lua [string ":lua"]:1: unfinished string near \'<eof>\'', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s})'):format(s))) + eq('Vim(lua):E5107: Error loading lua [string ":lua"]:0: unfinished string near \'<eof>\'', + pcall_err(command, ('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s})'):format(s))) eq({''}, curbufmeths.get_lines(0, -1, false)) - eq('', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s))) + eq('', exec_capture(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s))) eq({'', s}, curbufmeths.get_lines(0, -1, false)) end) @@ -144,34 +145,34 @@ end) describe(':luado command', function() it('works', function() curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) - eq('', redir_exec('luado lines = (lines or {}) lines[#lines + 1] = {linenr, line}')) + eq('', exec_capture('luado lines = (lines or {}) lines[#lines + 1] = {linenr, line}')) eq({'ABC', 'def', 'gHi'}, curbufmeths.get_lines(0, -1, false)) eq({{1, 'ABC'}, {2, 'def'}, {3, 'gHi'}}, funcs.luaeval('lines')) -- Automatic transformation of numbers - eq('', redir_exec('luado return linenr')) + eq('', exec_capture('luado return linenr')) eq({'1', '2', '3'}, curbufmeths.get_lines(0, -1, false)) - eq('', redir_exec('luado return ("<%02x>"):format(line:byte())')) + eq('', exec_capture('luado return ("<%02x>"):format(line:byte())')) eq({'<31>', '<32>', '<33>'}, curbufmeths.get_lines(0, -1, false)) end) it('stops processing lines when suddenly out of lines', function() curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) - eq('', redir_exec('2,$luado runs = ((runs or 0) + 1) vim.api.nvim_command("%d")')) + eq('', exec_capture('2,$luado runs = ((runs or 0) + 1) vim.api.nvim_command("%d")')) eq({''}, curbufmeths.get_lines(0, -1, false)) eq(1, funcs.luaeval('runs')) end) it('works correctly when changing lines out of range', function() curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) - eq('\nE322: line number out of range: 1 past the end\nE320: Cannot find line 2', - redir_exec('2,$luado vim.api.nvim_command("%d") return linenr')) + eq('Vim(luado):E322: line number out of range: 1 past the end', + pcall_err(command, '2,$luado vim.api.nvim_command("%d") return linenr')) eq({''}, curbufmeths.get_lines(0, -1, false)) end) it('fails on errors', function() - eq([[Vim(luado):E5109: Error loading lua: [string ":luado"]:1: unexpected symbol near ')']], - exc_exec('luado ()')) - eq([[Vim(luado):E5111: Error calling lua: [string ":luado"]:1: attempt to perform arithmetic on global 'liness' (a nil value)]], - exc_exec('luado return liness + 1')) + eq([[Vim(luado):E5109: Error loading lua: [string ":luado"]:0: unexpected symbol near ')']], + pcall_err(command, 'luado ()')) + eq([[Vim(luado):E5111: Error calling lua: [string ":luado"]:0: attempt to perform arithmetic on global 'liness' (a nil value)]], + pcall_err(command, 'luado return liness + 1')) end) it('works with NULL errors', function() eq([=[Vim(luado):E5111: Error calling lua: [NULL]]=], @@ -179,17 +180,18 @@ describe(':luado command', function() end) it('fails in sandbox when needed', function() curbufmeths.set_lines(0, 1, false, {"ABC", "def", "gHi"}) - eq('\nE48: Not allowed in sandbox: sandbox luado runs = (runs or 0) + 1', - redir_exec('sandbox luado runs = (runs or 0) + 1')) + eq('Vim(luado):E48: Not allowed in sandbox', + pcall_err(command, 'sandbox luado runs = (runs or 0) + 1')) eq(NIL, funcs.luaeval('runs')) end) it('works with long strings', function() local s = ('x'):rep(100500) - eq('\nE5109: Error loading lua: [string ":luado"]:1: unfinished string near \'<eof>\'', redir_exec(('luado return "%s'):format(s))) + eq('Vim(luado):E5109: Error loading lua: [string ":luado"]:0: unfinished string near \'<eof>\'', + pcall_err(command, ('luado return "%s'):format(s))) eq({''}, curbufmeths.get_lines(0, -1, false)) - eq('', redir_exec(('luado return "%s"'):format(s))) + eq('', exec_capture(('luado return "%s"'):format(s))) eq({s}, curbufmeths.get_lines(0, -1, false)) end) end) @@ -207,7 +209,7 @@ describe(':luafile', function() vim.api.nvim_buf_set_lines(1, 2, 3, false, {"TTSE"}) vim.api.nvim_buf_set_lines(1, 3, 4, false, {"STTE"}) ]]) - eq('', redir_exec('luafile ' .. fname)) + eq('', exec_capture('luafile ' .. fname)) eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false)) end) diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua new file mode 100644 index 0000000000..9397af9d9f --- /dev/null +++ b/test/functional/lua/diagnostic_spec.lua @@ -0,0 +1,1147 @@ +local helpers = require('test.functional.helpers')(after_each) + +local NIL = helpers.NIL +local command = helpers.command +local clear = helpers.clear +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local nvim = helpers.nvim + +describe('vim.diagnostic', function() + before_each(function() + clear() + + exec_lua [[ + require('vim.diagnostic') + + function make_diagnostic(msg, x1, y1, x2, y2, severity, source) + return { + lnum = x1, + col = y1, + end_lnum = x2, + end_col = y2, + message = msg, + severity = severity, + source = source, + } + end + + function make_error(msg, x1, y1, x2, y2, source) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source) + end + + function make_warning(msg, x1, y1, x2, y2, source) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source) + end + + function make_info(msg, x1, y1, x2, y2, source) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source) + end + + function make_hint(msg, x1, y1, x2, y2, source) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source) + end + + function count_diagnostics(bufnr, severity, namespace) + return #vim.diagnostic.get(bufnr, {severity = severity, namespace = namespace}) + end + + function count_extmarks(bufnr, namespace) + return #vim.api.nvim_buf_get_extmarks(bufnr, namespace, 0, -1, {}) + end + ]] + + exec_lua([[ + diagnostic_ns = vim.api.nvim_create_namespace("diagnostic_spec") + other_ns = vim.api.nvim_create_namespace("other_namespace") + diagnostic_bufnr = vim.api.nvim_create_buf(true, false) + local lines = {"1st line of text", "2nd line of text", "wow", "cool", "more", "lines"} + vim.fn.bufload(diagnostic_bufnr) + vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, 1, false, lines) + return diagnostic_bufnr + ]]) + end) + + after_each(function() + clear() + end) + + it('creates highlight groups', function() + command('runtime plugin/diagnostic.vim') + eq({ + 'DiagnosticError', + 'DiagnosticFloatingError', + 'DiagnosticFloatingHint', + 'DiagnosticFloatingInfo', + 'DiagnosticFloatingWarn', + 'DiagnosticHint', + 'DiagnosticInfo', + 'DiagnosticSignError', + 'DiagnosticSignHint', + 'DiagnosticSignInfo', + 'DiagnosticSignWarn', + 'DiagnosticUnderlineError', + 'DiagnosticUnderlineHint', + 'DiagnosticUnderlineInfo', + 'DiagnosticUnderlineWarn', + 'DiagnosticVirtualTextError', + 'DiagnosticVirtualTextHint', + 'DiagnosticVirtualTextInfo', + 'DiagnosticVirtualTextWarn', + 'DiagnosticWarn', + }, exec_lua([[return vim.fn.getcompletion('Diagnostic', 'highlight')]])) + end) + + it('retrieves diagnostics from all buffers and namespaces', function() + local result = exec_lua [[ + vim.diagnostic.set(diagnostic_ns, 1, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 2, 1, 2, 1), + }) + vim.diagnostic.set(other_ns, 2, { + make_error('Diagnostic #3', 3, 1, 3, 1), + }) + return vim.diagnostic.get() + ]] + eq(3, #result) + eq(2, exec_lua([[return #vim.tbl_filter(function(d) return d.bufnr == 1 end, ...)]], result)) + eq('Diagnostic #1', result[1].message) + end) + + it('saves and count a single error', function() + eq(1, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + }) + return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns) + ]]) + end) + + it('saves and count multiple errors', function() + eq(2, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 2, 1, 2, 1), + }) + return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns) + ]]) + end) + + it('saves and count from multiple namespaces', function() + eq({1, 1, 2}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic From Server 1', 1, 1, 1, 1), + }) + vim.diagnostic.set(other_ns, diagnostic_bufnr, { + make_error('Diagnostic From Server 2', 1, 1, 1, 1), + }) + return { + -- First namespace + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + -- Second namespace + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, other_ns), + -- All namespaces + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR), + } + ]]) + end) + + it('saves and count from multiple namespaces with respect to severity', function() + eq({3, 0, 3}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), + make_error('Diagnostic From Server 1:2', 2, 2, 2, 2), + make_error('Diagnostic From Server 1:3', 2, 3, 3, 2), + }) + vim.diagnostic.set(other_ns, diagnostic_bufnr, { + make_warning('Warning From Server 2', 3, 3, 3, 3), + }) + return { + -- Namespace 1 + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + -- Namespace 2 + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, other_ns), + -- All namespaces + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR), + } + ]]) + end) + + it('handles one namespace clearing highlights while the other still has highlights', function() + -- 1 Error (1) + -- 1 Warning (2) + -- 1 Warning (2) + 1 Warning (1) + -- 2 highlights and 2 underlines (since error) + -- 1 highlight + 1 underline + local all_highlights = {1, 1, 2, 4, 2} + eq(all_highlights, exec_lua [[ + local ns_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local ns_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + + return { + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } + ]]) + + -- Clear diagnostics from namespace 1, and make sure we have the right amount of stuff for namespace 2 + eq({1, 1, 2, 0, 2}, exec_lua [[ + vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + return { + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } + ]]) + + -- Show diagnostics from namespace 1 again + eq(all_highlights, exec_lua([[ + vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + return { + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } + ]])) + end) + + it('does not display diagnostics when disabled', function() + eq({0, 2}, exec_lua [[ + local ns_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local ns_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + + vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + + return { + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } + ]]) + + eq({4, 0}, exec_lua [[ + vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + vim.diagnostic.disable(diagnostic_bufnr, other_ns) + + return { + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } + ]]) + end) + + describe('reset()', function() + it('diagnostic count is 0 and displayed diagnostics are 0 after call', function() + -- 1 Error (1) + -- 1 Warning (2) + -- 1 Warning (2) + 1 Warning (1) + -- 2 highlights and 2 underlines (since error) + -- 1 highlight + 1 underline + local all_highlights = {1, 1, 2, 4, 2} + eq(all_highlights, exec_lua [[ + local ns_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local ns_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) + + return { + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } + ]]) + + -- Reset diagnostics from namespace 1 + exec_lua([[ vim.diagnostic.reset(diagnostic_ns) ]]) + + -- Make sure we have the right diagnostic count + eq({0, 1, 1, 0, 2} , exec_lua [[ + local diagnostic_count = {} + vim.wait(100, function () diagnostic_count = { + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } end ) + return diagnostic_count + ]]) + + -- Reset diagnostics from namespace 2 + exec_lua([[ vim.diagnostic.reset(other_ns) ]]) + + -- Make sure we have the right diagnostic count + eq({0, 0, 0, 0, 0}, exec_lua [[ + local diagnostic_count = {} + vim.wait(100, function () diagnostic_count = { + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), + count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN), + count_extmarks(diagnostic_bufnr, diagnostic_ns), + count_extmarks(diagnostic_bufnr, other_ns), + } end ) + return diagnostic_count + ]]) + + end) + end) + + describe('get_next_pos()', function() + it('can find the next pos with only one namespace', function() + eq({1, 1}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + return vim.diagnostic.get_next_pos() + ]]) + end) + + it('can find next pos with two errors', function() + eq({4, 4}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 4, 4, 4, 4), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 1}) + return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } + ]]) + end) + + it('can cycle when position is past error', function() + eq({1, 1}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 1}) + return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } + ]]) + end) + + it('will not cycle when wrap is off', function() + eq(false, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 1}) + return vim.diagnostic.get_next_pos { namespace = diagnostic_ns, wrap = false } + ]]) + end) + + it('can cycle even from the last line', function() + eq({4, 4}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #2', 4, 4, 4, 4), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {vim.api.nvim_buf_line_count(0), 1}) + return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns } + ]]) + end) + end) + + describe('get_prev_pos()', function() + it('can find the prev pos with only one namespace', function() + eq({1, 1}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 1}) + return vim.diagnostic.get_prev_pos() + ]]) + end) + + it('can find prev pos with two errors', function() + eq({1, 1}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 4, 4, 4, 4), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 1}) + return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns } + ]]) + end) + + it('can cycle when position is past error', function() + eq({4, 4}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #2', 4, 4, 4, 4), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 1}) + return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns } + ]]) + end) + + it('respects wrap parameter', function() + eq(false, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #2', 4, 4, 4, 4), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 1}) + return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns, wrap = false} + ]]) + end) + end) + + describe('get()', function() + it('returns an empty table when no diagnostics are present', function() + eq({}, exec_lua [[return vim.diagnostic.get(diagnostic_bufnr, {namespace=diagnostic_ns})]]) + end) + + it('returns all diagnostics when no severity is supplied', function() + eq(2, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 1, 1, 2, 5), + }) + + return #vim.diagnostic.get(diagnostic_bufnr) + ]]) + end) + + it('returns only requested diagnostics when severity is supplied', function() + eq({2, 3, 2}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 1, 1, 2, 5), + make_info("Ignored information", 1, 1, 2, 5), + make_hint("Here's a hint", 1, 1, 2, 5), + }) + + return { + #vim.diagnostic.get(diagnostic_bufnr, { severity = {min=vim.diagnostic.severity.WARN} }), + #vim.diagnostic.get(diagnostic_bufnr, { severity = {max=vim.diagnostic.severity.WARN} }), + #vim.diagnostic.get(diagnostic_bufnr, { + severity = { + min=vim.diagnostic.severity.INFO, + max=vim.diagnostic.severity.WARN, + } + }), + } + ]]) + end) + + it('allows filtering by line', function() + eq(1, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 1, 1, 2, 5), + make_info("Ignored information", 1, 1, 2, 5), + make_error("Error On Other Line", 2, 1, 1, 5), + }) + + return #vim.diagnostic.get(diagnostic_bufnr, {lnum = 2}) + ]]) + end) + end) + + describe('config()', function() + it('can use functions for config values', function() + exec_lua [[ + vim.diagnostic.config({ + virtual_text = function() return true end, + }, diagnostic_ns) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Delayed Diagnostic', 4, 4, 4, 4), + }) + ]] + + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + + -- Now, don't enable virtual text. + -- We should have one less extmark displayed. + exec_lua [[ + vim.diagnostic.config({ + virtual_text = function() return false end, + }, diagnostic_ns) + ]] + + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + end) + + it('allows filtering by severity', function() + local get_extmark_count_with_severity = function(min_severity) + return exec_lua([[ + vim.diagnostic.config({ + underline = false, + virtual_text = { + severity = {min=...}, + }, + }) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_warning('Delayed Diagnostic', 4, 4, 4, 4), + }) + + return count_extmarks(diagnostic_bufnr, diagnostic_ns) + ]], min_severity) + end + + -- No messages with Error or higher + eq(0, get_extmark_count_with_severity("ERROR")) + + -- But now we don't filter it + eq(1, get_extmark_count_with_severity("WARN")) + eq(1, get_extmark_count_with_severity("HINT")) + end) + + it('allows sorting by severity', function() + exec_lua [[ + vim.diagnostic.config({ + underline = false, + signs = true, + virtual_text = true, + }) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_warning('Warning', 4, 4, 4, 4), + make_error('Error', 4, 4, 4, 4), + make_info('Info', 4, 4, 4, 4), + }) + + function get_virt_text_and_signs(severity_sort) + vim.diagnostic.config({ + severity_sort = severity_sort, + }) + + local virt_text = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true})[1][4].virt_text + + local virt_texts = {} + for i = 2, #virt_text do + table.insert(virt_texts, (string.gsub(virt_text[i][2], "DiagnosticVirtualText", ""))) + end + + local signs = {} + for _, v in ipairs(vim.fn.sign_getplaced(diagnostic_bufnr, {group = "*"})[1].signs) do + table.insert(signs, (string.gsub(v.name, "DiagnosticSign", ""))) + end + + return {virt_texts, signs} + end + ]] + + local result = exec_lua [[return get_virt_text_and_signs(false)]] + + -- Virt texts are defined lowest priority to highest, signs from + -- highest to lowest + eq({'Warn', 'Error', 'Info'}, result[1]) + eq({'Info', 'Error', 'Warn'}, result[2]) + + result = exec_lua [[return get_virt_text_and_signs(true)]] + eq({'Info', 'Warn', 'Error'}, result[1]) + eq({'Error', 'Warn', 'Info'}, result[2]) + + result = exec_lua [[return get_virt_text_and_signs({ reverse = true })]] + eq({'Error', 'Warn', 'Info'}, result[1]) + eq({'Info', 'Warn', 'Error'}, result[2]) + end) + + it('can show diagnostic sources in virtual text', function() + local result = exec_lua [[ + local diagnostics = { + make_error('Some error', 0, 0, 0, 0, 'source x'), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { + underline = false, + virtual_text = { + prefix = '', + source = 'always', + } + }) + + local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + local virt_text = extmarks[1][4].virt_text[2][1] + return virt_text + ]] + eq(' source x: Some error', result) + + result = exec_lua [[ + vim.diagnostic.config({ + underline = false, + virtual_text = { + prefix = '', + source = 'if_many', + } + }, diagnostic_ns) + + local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + local virt_text = extmarks[1][4].virt_text[2][1] + return virt_text + ]] + eq(' Some error', result) + + result = exec_lua [[ + local diagnostics = { + make_error('Some error', 0, 0, 0, 0, 'source x'), + make_error('Another error', 1, 1, 1, 1, 'source y'), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { + underline = false, + virtual_text = { + prefix = '', + source = 'if_many', + } + }) + + local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + local virt_text = {extmarks[1][4].virt_text[2][1], extmarks[2][4].virt_text[2][1]} + return virt_text + ]] + eq(' source x: Some error', result[1]) + eq(' source y: Another error', result[2]) + end) + + it('supports a format function for diagnostic messages', function() + local result = exec_lua [[ + vim.diagnostic.config({ + underline = false, + virtual_text = { + prefix = '', + format = function(diagnostic) + if diagnostic.severity == vim.diagnostic.severity.ERROR then + return string.format("🔥 %s", diagnostic.message) + end + return string.format("👀 %s", diagnostic.message) + end, + } + }) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_warning('Warning', 0, 0, 0, 0), + make_error('Error', 1, 0, 1, 0), + }) + + local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + return {extmarks[1][4].virt_text, extmarks[2][4].virt_text} + ]] + eq(" 👀 Warning", result[1][2][1]) + eq(" 🔥 Error", result[2][2][1]) + end) + + it('includes source for formatted diagnostics', function() + local result = exec_lua [[ + vim.diagnostic.config({ + underline = false, + virtual_text = { + prefix = '', + source = 'always', + format = function(diagnostic) + if diagnostic.severity == vim.diagnostic.severity.ERROR then + return string.format("🔥 %s", diagnostic.message) + end + return string.format("👀 %s", diagnostic.message) + end, + } + }) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_warning('Warning', 0, 0, 0, 0, 'some_linter'), + make_error('Error', 1, 0, 1, 0, 'another_linter'), + }) + + local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true}) + return {extmarks[1][4].virt_text, extmarks[2][4].virt_text} + ]] + eq(" some_linter: 👀 Warning", result[1][2][1]) + eq(" another_linter: 🔥 Error", result[2][2][1]) + end) + end) + + describe('set()', function() + it('can perform updates after insert_leave', function() + exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] + nvim("input", "o") + eq({mode='i', blocking=false}, nvim("get_mode")) + + -- Save the diagnostics + exec_lua [[ + vim.diagnostic.config({ + update_in_insert = false, + }) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Delayed Diagnostic', 4, 4, 4, 4), + }) + ]] + + -- No diagnostics displayed yet. + eq({mode='i', blocking=false}, nvim("get_mode")) + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + + nvim("input", "<esc>") + eq({mode='n', blocking=false}, nvim("get_mode")) + + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + end) + + it('does not perform updates when not needed', function() + exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] + nvim("input", "o") + eq({mode='i', blocking=false}, nvim("get_mode")) + + -- Save the diagnostics + exec_lua [[ + vim.diagnostic.config({ + update_in_insert = false, + virtual_text = true, + }) + + -- Count how many times we call display. + SetVirtualTextOriginal = vim.diagnostic._set_virtual_text + + DisplayCount = 0 + vim.diagnostic._set_virtual_text = function(...) + DisplayCount = DisplayCount + 1 + return SetVirtualTextOriginal(...) + end + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Delayed Diagnostic', 4, 4, 4, 4), + }) + ]] + + -- No diagnostics displayed yet. + eq({mode='i', blocking=false}, nvim("get_mode")) + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + eq(0, exec_lua [[return DisplayCount]]) + + nvim("input", "<esc>") + eq({mode='n', blocking=false}, nvim("get_mode")) + + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + eq(1, exec_lua [[return DisplayCount]]) + + -- Go in and out of insert mode one more time. + nvim("input", "o") + eq({mode='i', blocking=false}, nvim("get_mode")) + + nvim("input", "<esc>") + eq({mode='n', blocking=false}, nvim("get_mode")) + + -- Should not have set the virtual text again. + eq(1, exec_lua [[return DisplayCount]]) + end) + + it('never sets virtual text, in combination with insert leave', function() + exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] + nvim("input", "o") + eq({mode='i', blocking=false}, nvim("get_mode")) + + -- Save the diagnostics + exec_lua [[ + vim.diagnostic.config({ + update_in_insert = false, + virtual_text = false, + }) + + -- Count how many times we call display. + SetVirtualTextOriginal = vim.diagnostic._set_virtual_text + + DisplayCount = 0 + vim.diagnostic._set_virtual_text = function(...) + DisplayCount = DisplayCount + 1 + return SetVirtualTextOriginal(...) + end + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Delayed Diagnostic', 4, 4, 4, 4), + }) + ]] + + -- No diagnostics displayed yet. + eq({mode='i', blocking=false}, nvim("get_mode")) + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(0, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + eq(0, exec_lua [[return DisplayCount]]) + + nvim("input", "<esc>") + eq({mode='n', blocking=false}, nvim("get_mode")) + + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(1, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + eq(0, exec_lua [[return DisplayCount]]) + + -- Go in and out of insert mode one more time. + nvim("input", "o") + eq({mode='i', blocking=false}, nvim("get_mode")) + + nvim("input", "<esc>") + eq({mode='n', blocking=false}, nvim("get_mode")) + + -- Should not have set the virtual text still. + eq(0, exec_lua [[return DisplayCount]]) + end) + + it('can perform updates while in insert mode, if desired', function() + exec_lua [[vim.api.nvim_set_current_buf(diagnostic_bufnr)]] + nvim("input", "o") + eq({mode='i', blocking=false}, nvim("get_mode")) + + -- Save the diagnostics + exec_lua [[ + vim.diagnostic.config({ + update_in_insert = true, + }) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Delayed Diagnostic', 4, 4, 4, 4), + }) + ]] + + -- Diagnostics are displayed, because the user wanted them that way! + eq({mode='i', blocking=false}, nvim("get_mode")) + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + + nvim("input", "<esc>") + eq({mode='n', blocking=false}, nvim("get_mode")) + + eq(1, exec_lua [[return count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns)]]) + eq(2, exec_lua [[return count_extmarks(diagnostic_bufnr, diagnostic_ns)]]) + end) + + it('can set diagnostics without displaying them', function() + eq(0, exec_lua [[ + vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), + }) + return count_extmarks(diagnostic_bufnr, diagnostic_ns) + ]]) + + eq(2, exec_lua [[ + vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + return count_extmarks(diagnostic_bufnr, diagnostic_ns) + ]]) + end) + + it('can set display options', function() + eq(0, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), + }, { virtual_text = false, underline = false }) + return count_extmarks(diagnostic_bufnr, diagnostic_ns) + ]]) + + eq(1, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), + }, { virtual_text = true, underline = false }) + return count_extmarks(diagnostic_bufnr, diagnostic_ns) + ]]) + end) + end) + + describe('show_line_diagnostics()', function() + it('creates floating window and returns popup bufnr and winnr if current line contains diagnostics', function() + -- Two lines: + -- Diagnostic: + -- 1. <msg> + eq(2, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics() + return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + ]]) + end) + + it('only reports diagnostics from the current buffer when bufnr is omitted #15710', function() + eq(2, exec_lua [[ + local other_bufnr = vim.api.nvim_create_buf(true, false) + local buf_1_diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + local buf_2_diagnostics = { + make_warning("Some warning", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, buf_1_diagnostics) + vim.diagnostic.set(other_ns, other_bufnr, buf_2_diagnostics) + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics() + return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + ]]) + end) + + it('allows filtering by namespace', function() + eq(2, exec_lua [[ + local ns_1_diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + local ns_2_diagnostics = { + make_warning("Some warning", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diagnostics) + vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diagnostics) + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics({namespace = diagnostic_ns}) + return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + ]]) + end) + + it('creates floating window and returns popup bufnr and winnr without header, if requested', function() + -- One line (since no header): + -- 1. <msg> + eq(1, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics {show_header = false} + return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + ]]) + end) + + it('clamps diagnostic line numbers within the valid range', function() + eq(1, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 6, 0, 6, 0), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics({show_header = false}, diagnostic_bufnr, 5) + return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + ]]) + end) + + it('can show diagnostic source', function() + exec_lua [[vim.api.nvim_win_set_buf(0, diagnostic_bufnr)]] + + eq({"1. Syntax error"}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3, "source x"), + } + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics { + show_header = false, + source = "if_many", + } + local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + + eq({"1. source x: Syntax error"}, exec_lua [[ + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics { + show_header = false, + source = "always", + } + local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + + eq({"1. source x: Syntax error", "2. source y: Another error"}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3, "source x"), + make_error("Another error", 0, 1, 0, 3, "source y"), + } + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics { + show_header = false, + source = "if_many", + } + local lines = vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + end) + end) + + describe('set_signs()', function() + -- TODO(tjdevries): Find out why signs are not displayed when set from Lua...?? + pending('sets signs by default', function() + exec_lua [[ + vim.diagnostic.config({ + update_in_insert = true, + signs = true, + }) + + local diagnostics = { + make_error('Delayed Diagnostic', 1, 1, 1, 2), + make_error('Delayed Diagnostic', 3, 3, 3, 3), + } + + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + + vim.diagnostic._set_signs(diagnostic_ns, diagnostic_bufnr, diagnostics) + -- return vim.fn.sign_getplaced() + ]] + + nvim("input", "o") + nvim("input", "<esc>") + + -- TODO(tjdevries): Find a way to get the signs to display in the test... + eq(nil, exec_lua [[ + return im.fn.sign_getplaced()[1].signs + ]]) + end) + end) + + describe('setloclist()', function() + it('sets diagnostics in lnum order', function() + local loc_list = exec_lua [[ + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Farther Diagnostic', 4, 4, 4, 4), + make_error('Lower Diagnostic', 1, 1, 1, 1), + }) + + vim.diagnostic.setloclist() + + return vim.fn.getloclist(0) + ]] + + assert(loc_list[1].lnum < loc_list[2].lnum) + end) + + it('sets diagnostics in lnum order, regardless of namespace', function() + local loc_list = exec_lua [[ + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Lower Diagnostic', 1, 1, 1, 1), + }) + + vim.diagnostic.set(other_ns, diagnostic_bufnr, { + make_warning('Farther Diagnostic', 4, 4, 4, 4), + }) + + vim.diagnostic.setloclist() + + return vim.fn.getloclist(0) + ]] + + assert(loc_list[1].lnum < loc_list[2].lnum) + end) + end) + + describe('match()', function() + it('matches a string', function() + local msg = "ERROR: george.txt:19:84:Two plus two equals five" + local diagnostic = { + severity = exec_lua [[return vim.diagnostic.severity.ERROR]], + lnum = 18, + col = 83, + end_lnum = 18, + end_col = 83, + message = "Two plus two equals five", + } + eq(diagnostic, exec_lua([[ + return vim.diagnostic.match(..., "^(%w+): [^:]+:(%d+):(%d+):(.+)$", {"severity", "lnum", "col", "message"}) + ]], msg)) + end) + + it('returns nil if the pattern fails to match', function() + eq(NIL, exec_lua [[ + local msg = "The answer to life, the universe, and everything is" + return vim.diagnostic.match(msg, "This definitely will not match", {}) + ]]) + end) + + it('respects default values', function() + local msg = "anna.txt:1:Happy families are all alike" + local diagnostic = { + severity = exec_lua [[return vim.diagnostic.severity.INFO]], + lnum = 0, + col = 0, + end_lnum = 0, + end_col = 0, + message = "Happy families are all alike", + } + eq(diagnostic, exec_lua([[ + return vim.diagnostic.match(..., "^[^:]+:(%d+):(.+)$", {"lnum", "message"}, nil, {severity = vim.diagnostic.severity.INFO}) + ]], msg)) + end) + + it('accepts a severity map', function() + local msg = "46:FATAL:Et tu, Brute?" + local diagnostic = { + severity = exec_lua [[return vim.diagnostic.severity.ERROR]], + lnum = 45, + col = 0, + end_lnum = 45, + end_col = 0, + message = "Et tu, Brute?", + } + eq(diagnostic, exec_lua([[ + return vim.diagnostic.match(..., "^(%d+):(%w+):(.+)$", {"lnum", "severity", "message"}, {FATAL = vim.diagnostic.severity.ERROR}) + ]], msg)) + end) + end) + + describe('toqflist() and fromqflist()', function() + it('works', function() + local result = exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Error 1', 0, 1, 0, 1), + make_error('Error 2', 1, 1, 1, 1), + make_warning('Warning', 2, 2, 2, 2), + }) + + local diagnostics = vim.diagnostic.get(diagnostic_bufnr) + vim.fn.setqflist(vim.diagnostic.toqflist(diagnostics)) + local list = vim.fn.getqflist() + local new_diagnostics = vim.diagnostic.fromqflist(list) + + -- Remove namespace since it isn't present in the return value of + -- fromlist() + for _, v in ipairs(diagnostics) do + v.namespace = nil + end + + return {diagnostics, new_diagnostics} + ]] + eq(result[1], result[2]) + end) + end) +end) diff --git a/test/functional/lua/highlight_spec.lua b/test/functional/lua/highlight_spec.lua new file mode 100644 index 0000000000..50eecb5d09 --- /dev/null +++ b/test/functional/lua/highlight_spec.lua @@ -0,0 +1,25 @@ +local helpers = require('test.functional.helpers')(after_each) +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local eval = helpers.eval +local command = helpers.command +local clear = helpers.clear + +describe('vim.highlight.on_yank', function() + + before_each(function() + clear() + end) + + it('does not show errors even if buffer is wiped before timeout', function() + command('new') + exec_lua[[ + vim.highlight.on_yank({timeout = 10, on_macro = true, event = {operator = "y", regtype = "v"}}) + vim.cmd('bwipeout!') + ]] + helpers.sleep(10) + helpers.feed('<cr>') -- avoid hang if error message exists + eq('', eval('v:errmsg')) + end) + +end) diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua new file mode 100644 index 0000000000..fbb21bfd57 --- /dev/null +++ b/test/functional/lua/json_spec.lua @@ -0,0 +1,133 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local NIL = helpers.NIL +local exec_lua = helpers.exec_lua +local eq = helpers.eq + +describe('vim.json.decode function', function() + before_each(function() + clear() + end) + + it('parses null, true, false', function() + eq(NIL, exec_lua([[return vim.json.decode('null')]])) + eq(true, exec_lua([[return vim.json.decode('true')]])) + eq(false, exec_lua([[return vim.json.decode('false')]])) + end) + + it('parses integer numbers', function() + eq(100000, exec_lua([[return vim.json.decode('100000')]])) + eq(-100000, exec_lua([[return vim.json.decode('-100000')]])) + eq(100000, exec_lua([[return vim.json.decode(' 100000 ')]])) + eq(-100000, exec_lua([[return vim.json.decode(' -100000 ')]])) + eq(0, exec_lua([[return vim.json.decode('0')]])) + eq(0, exec_lua([[return vim.json.decode('-0')]])) + end) + + it('parses floating-point numbers', function() + -- This behavior differs from vim.fn.json_decode, which return '100000.0' + eq('100000', exec_lua([[return tostring(vim.json.decode('100000.0'))]])) + eq(100000.5, exec_lua([[return vim.json.decode('100000.5')]])) + eq(-100000.5, exec_lua([[return vim.json.decode('-100000.5')]])) + eq(-100000.5e50, exec_lua([[return vim.json.decode('-100000.5e50')]])) + eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e50')]])) + eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e+50')]])) + eq(-100000.5e-50, exec_lua([[return vim.json.decode('-100000.5e-50')]])) + eq(100000.5e-50, exec_lua([[return vim.json.decode('100000.5e-50')]])) + eq(100000e-50, exec_lua([[return vim.json.decode('100000e-50')]])) + eq(0.5, exec_lua([[return vim.json.decode('0.5')]])) + eq(0.005, exec_lua([[return vim.json.decode('0.005')]])) + eq(0.005, exec_lua([[return vim.json.decode('0.00500')]])) + eq(0.5, exec_lua([[return vim.json.decode('0.00500e+002')]])) + eq(0.00005, exec_lua([[return vim.json.decode('0.00500e-002')]])) + + eq(-0.0, exec_lua([[return vim.json.decode('-0.0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e+0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e-0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e-0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e-2')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e+2')]])) + + eq(0.0, exec_lua([[return vim.json.decode('0.0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e+0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e-0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e-0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e-2')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e+2')]])) + end) + + it('parses containers', function() + eq({1}, exec_lua([[return vim.json.decode('[1]')]])) + eq({NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]])) + eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]])) + eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, + exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]])) + end) + + it('parses strings properly', function() + eq('\n', exec_lua([=[return vim.json.decode([["\n"]])]=])) + eq('', exec_lua([=[return vim.json.decode([[""]])]=])) + eq('\\/"\t\b\n\r\f', exec_lua([=[return vim.json.decode([["\\\/\"\t\b\n\r\f"]])]=])) + eq('/a', exec_lua([=[return vim.json.decode([["\/a"]])]=])) + -- Unicode characters: 2-byte, 3-byte + eq('«',exec_lua([=[return vim.json.decode([["«"]])]=])) + eq('ફ',exec_lua([=[return vim.json.decode([["ફ"]])]=])) + end) + + it('parses surrogate pairs properly', function() + eq('\240\144\128\128', exec_lua([[return vim.json.decode('"\\uD800\\uDC00"')]])) + end) + + it('accepts all spaces in every position where space may be put', function() + local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t' + local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s) + eq({key={'val', 'val2'}, key2=1}, exec_lua([[return vim.json.decode(...)]], str)) + end) + +end) + +describe('vim.json.encode function', function() + before_each(function() + clear() + end) + + it('dumps strings', function() + eq('"Test"', exec_lua([[return vim.json.encode('Test')]])) + eq('""', exec_lua([[return vim.json.encode('')]])) + eq('"\\t"', exec_lua([[return vim.json.encode('\t')]])) + eq('"\\n"', exec_lua([[return vim.json.encode('\n')]])) + -- vim.fn.json_encode return \\u001B + eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]])) + eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]])) + end) + + it('dumps numbers', function() + eq('0', exec_lua([[return vim.json.encode(0)]])) + eq('10', exec_lua([[return vim.json.encode(10)]])) + eq('-10', exec_lua([[return vim.json.encode(-10)]])) + end) + + it('dumps floats', function() + eq('10.5', exec_lua([[return vim.json.encode(10.5)]])) + eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]])) + eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]])) + end) + + it('dumps lists', function() + eq('[]', exec_lua([[return vim.json.encode({})]])) + eq('[[]]', exec_lua([[return vim.json.encode({{}})]])) + eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]])) + end) + + it('dumps dictionaries', function() + eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]])) + eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]])) + end) + + it('dumps vim.NIL', function() + eq('null', exec_lua([[return vim.json.encode(vim.NIL)]])) + end) + +end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 2ec48777fd..255e99032f 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -63,11 +63,10 @@ describe('luaeval()', function() eq('\n[[...@0]]', funcs.execute('echo luaeval("l")')) end) end) - describe('strings', function() - it('are successfully converted to special dictionaries', function() + describe('strings with NULs', function() + it('are successfully converted to blobs', function() command([[let s = luaeval('"\0"')]]) - eq({_TYPE={}, _VAL={'\n'}}, meths.get_var('s')) - eq(1, funcs.eval('s._TYPE is v:msgpack_types.binary')) + eq('\000', meths.get_var('s')) end) it('are successfully converted to special dictionaries in table keys', function() @@ -76,13 +75,10 @@ describe('luaeval()', function() eq(1, funcs.eval('d._TYPE is v:msgpack_types.map')) eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string')) end) - it('are successfully converted to special dictionaries from a list', + it('are successfully converted to blobs from a list', function() command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]]) - eq({'abc', {_TYPE={}, _VAL={'a\nb'}}, {_TYPE={}, _VAL={'c\nd'}}, 'def'}, - meths.get_var('l')) - eq(1, funcs.eval('l[1]._TYPE is v:msgpack_types.binary')) - eq(1, funcs.eval('l[2]._TYPE is v:msgpack_types.binary')) + eq({'abc', 'a\000b', 'c\000d', 'def'}, meths.get_var('l')) end) end) @@ -100,9 +96,9 @@ describe('luaeval()', function() eq(1, eval('type(luaeval("\'test\'"))')) eq('', funcs.luaeval('""')) - eq({_TYPE={}, _VAL={'\n'}}, funcs.luaeval([['\0']])) - eq({_TYPE={}, _VAL={'\n', '\n'}}, funcs.luaeval([['\0\n\0']])) - eq(1, eval([[luaeval('"\0\n\0"')._TYPE is v:msgpack_types.binary]])) + eq('\000', funcs.luaeval([['\0']])) + eq('\000\n\000', funcs.luaeval([['\0\n\0']])) + eq(10, eval([[type(luaeval("'\\0\\n\\0'"))]])) eq(true, funcs.luaeval('true')) eq(false, funcs.luaeval('false')) @@ -122,12 +118,11 @@ describe('luaeval()', function() local level = 30 eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s)) - eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}, + eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, '\000\n\000\000'}}}, funcs.luaeval([[{['\0\n\0']='\0\n\0\0'}]])) eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._TYPE is v:msgpack_types.map]])) eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][0]._TYPE is v:msgpack_types.string]])) - eq(1, eval([[luaeval('{["\0\n\0"]="\0\n\0\0"}')._VAL[0][1]._TYPE is v:msgpack_types.binary]])) - eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, {_TYPE={}, _VAL={'\n', '\n\n'}}}}}}}, + eq({nested={{_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n', '\n'}}, '\000\n\000\000'}}}}}, funcs.luaeval([[{nested={{['\0\n\0']='\0\n\0\0'}}}]])) end) @@ -175,8 +170,8 @@ describe('luaeval()', function() end it('correctly passes special dictionaries', function() - eq({'binary', {'\n', '\n'}}, luaevalarg(sp('binary', '["\\n", "\\n"]'))) - eq({'binary', {'\n', '\n'}}, luaevalarg(sp('string', '["\\n", "\\n"]'))) + eq({0, '\000\n\000'}, luaevalarg(sp('binary', '["\\n", "\\n"]'))) + eq({0, '\000\n\000'}, luaevalarg(sp('string', '["\\n", "\\n"]'))) eq({0, true}, luaevalarg(sp('boolean', 1))) eq({0, false}, luaevalarg(sp('boolean', 0))) eq({0, NIL}, luaevalarg(sp('nil', 0))) @@ -458,6 +453,9 @@ describe('v:lua', function() function mymod.crashy() nonexistent() end + function mymod.whatis(value) + return type(value) .. ": " .. tostring(value) + end function mymod.omni(findstart, base) if findstart == 1 then return 5 @@ -476,11 +474,28 @@ describe('v:lua', function() eq(true, exec_lua([[return _G.val == vim.NIL]])) eq(NIL, eval('v:lua.mymod.noisy("eval")')) eq("hey eval", meths.get_current_line()) + eq("string: abc", eval('v:lua.mymod.whatis(0z616263)')) + eq("string: ", eval('v:lua.mymod.whatis(v:_null_blob)')) 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 when called as a method', function() + eq(123, eval('110->v:lua.foo(13)')) + eq(true, exec_lua([[return _G.val == nil]])) + + eq(321, eval('300->v:lua.foo(21, "boop")')) + eq("boop", exec_lua([[return _G.val]])) + + eq(NIL, eval('"there"->v:lua.mymod.noisy()')) + eq("hey there", meths.get_current_line()) + eq({5, 10, 15, 20}, eval('[[1], [2, 3], [4]]->v:lua.vim.tbl_flatten()->map({_, v -> v * 5})')) + + eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)", + pcall_err(eval, '"huh?"->v:lua.mymod.crashy()')) + end) + it('works in :call', function() command(":call v:lua.mymod.noisy('command')") eq("hey command", meths.get_current_line()) @@ -518,8 +533,15 @@ describe('v:lua', function() eq("Vim:E15: Invalid expression: v:['lua'].foo()", pcall_err(eval, "v:['lua'].foo()")) eq("Vim(call):E461: Illegal variable name: v:['lua']", pcall_err(command, "call v:['lua'].baar()")) + eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "v:lua()")) eq("Vim(let):E46: Cannot change read-only variable \"v:['lua']\"", pcall_err(command, "let v:['lua'] = 'xx'")) eq("Vim(let):E46: Cannot change read-only variable \"v:lua\"", pcall_err(command, "let v:lua = 'xx'")) + + eq("Vim:E107: Missing parentheses: v:lua.func", pcall_err(eval, "'bad'->v:lua.func")) + eq("Vim:E274: No white space allowed before parenthesis", pcall_err(eval, "'bad'->v:lua.func ()")) + eq("Vim:E107: Missing parentheses: v:lua", pcall_err(eval, "'bad'->v:lua")) + eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "'bad'->v:lua()")) + eq("Vim:E15: Invalid expression: v:lua.()", pcall_err(eval, "'bad'->v:lua.()")) end) end) diff --git a/test/functional/lua/mpack_spec.lua b/test/functional/lua/mpack_spec.lua new file mode 100644 index 0000000000..ef693f01f3 --- /dev/null +++ b/test/functional/lua/mpack_spec.lua @@ -0,0 +1,23 @@ +-- Test suite for testing interactions with API bindings +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local exec_lua = helpers.exec_lua + +describe('lua vim.mpack', function() + before_each(clear) + it('can pack vim.NIL', function() + eq({true, true, true, true}, exec_lua [[ + local var = vim.mpack.unpack(vim.mpack.pack({33, vim.NIL, 77})) + return {var[1]==33, var[2]==vim.NIL, var[3]==77, var[4]==nil} + ]]) + end) + + it('can pack vim.empty_dict()', function() + eq({{{}, "foo", {}}, true, false}, exec_lua [[ + local var = vim.mpack.unpack(vim.mpack.pack({{}, "foo", vim.empty_dict()})) + return {var, vim.tbl_islist(var[1]), vim.tbl_islist(var[3])} + ]]) + end) +end) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 267c759faf..636479b81f 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -11,8 +11,9 @@ local meths = helpers.meths local iswin = helpers.iswin local command = helpers.command local write_file = helpers.write_file -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture local exec_lua = helpers.exec_lua +local pcall_err = helpers.pcall_err local screen @@ -36,11 +37,11 @@ describe('print', function() write_file(fname, 'print("abc")') eq('\nabc', funcs.execute('luafile ' .. fname)) - eq('\nabc', redir_exec('lua print("abc")')) - eq('\nabc', redir_exec('luado print("abc")')) - eq('\nabc', redir_exec('call luaeval("print(\'abc\')")')) + eq('abc', exec_capture('lua print("abc")')) + eq('abc', exec_capture('luado print("abc")')) + eq('abc', exec_capture('call luaeval("print(\'abc\')")')) write_file(fname, 'print("abc")') - eq('\nabc', redir_exec('luafile ' .. fname)) + eq('abc', exec_capture('luafile ' .. fname)) end) it('handles errors in __tostring', function() write_file(fname, [[ @@ -51,30 +52,30 @@ describe('print', function() v_abcerr = setmetatable({}, meta_abcerr) v_tblout = setmetatable({}, meta_tblout) ]]) - eq('', redir_exec('luafile ' .. fname)) + eq('', exec_capture('luafile ' .. fname)) -- TODO(bfredl): these look weird, print() should not use "E5114:" style errors.. - eq('\nE5108: Error executing lua E5114: Error while converting print argument #2: [NULL]', - redir_exec('lua print("foo", v_nilerr, "bar")')) - eq('\nE5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc', - redir_exec('lua print("foo", v_abcerr, "bar")')) - eq('\nE5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>', - redir_exec('lua print("foo", v_tblout, "bar")')) + eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: [NULL]', + pcall_err(command, 'lua print("foo", v_nilerr, "bar")')) + eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:0: abc', + pcall_err(command, 'lua print("foo", v_abcerr, "bar")')) + eq('Vim(lua):E5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>', + pcall_err(command, 'lua print("foo", v_tblout, "bar")')) end) it('prints strings with NULs and NLs correctly', function() meths.set_option('more', true) - eq('\nabc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT\n', - redir_exec([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\n")]])) - eq('\nabc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT^@', - redir_exec([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\0")]])) - eq('\nT^@', redir_exec([[lua print("T\0")]])) - eq('\nT\n', redir_exec([[lua print("T\n")]])) + eq('abc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT\n', + exec_capture([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\n")]])) + eq('abc ^@ def\nghi^@^@^@jkl\nTEST\n\n\nT^@', + exec_capture([[lua print("abc \0 def\nghi\0\0\0jkl\nTEST\n\n\nT\0")]])) + eq('T^@', exec_capture([[lua print("T\0")]])) + eq('T\n', exec_capture([[lua print("T\n")]])) end) it('prints empty strings correctly', function() -- Regression: first test used to crash - eq('', redir_exec('lua print("")')) - eq('\n def', redir_exec('lua print("", "def")')) - eq('\nabc ', redir_exec('lua print("abc", "")')) - eq('\nabc def', redir_exec('lua print("abc", "", "def")')) + eq('', exec_capture('lua print("")')) + eq(' def', exec_capture('lua print("", "def")')) + eq('abc ', exec_capture('lua print("abc", "")')) + eq('abc def', exec_capture('lua print("abc", "", "def")')) end) it('defers printing in luv event handlers', function() exec_lua([[ @@ -97,7 +98,7 @@ describe('print', function() vim.api.nvim_command("sleep 1m") -- force deferred event processing end ]], (iswin() and "timeout 1") or "sleep 0.1") - eq('\nvery slow\nvery fast',redir_exec('lua test()')) + eq('very slow\nvery fast', exec_capture('lua test()')) end) end) diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua new file mode 100644 index 0000000000..94f1b5840b --- /dev/null +++ b/test/functional/lua/ui_spec.lua @@ -0,0 +1,46 @@ +local helpers = require('test.functional.helpers')(after_each) +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local clear = helpers.clear + +describe('vim.ui', function() + before_each(function() + clear() + end) + + + describe('select', function() + it('can select an item', function() + local result = exec_lua[[ + local items = { + { name = 'Item 1' }, + { name = 'Item 2' }, + } + local opts = { + format_item = function(entry) + return entry.name + end + } + local selected + local cb = function(item) + selected = item + end + -- inputlist would require input and block the test; + local choices + vim.fn.inputlist = function(x) + choices = x + return 1 + end + vim.ui.select(items, opts, cb) + vim.wait(100, function() return selected ~= nil end) + return {selected, choices} + ]] + eq({ name = 'Item 1' }, result[1]) + eq({ + 'Select one of:', + '1: Item 1', + '2: Item 2', + }, result[2]) + end) + end) +end) diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index f782769935..052a8a1ecd 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -53,12 +53,18 @@ describe('URI methods', function() describe('uri to filepath', function() describe('decode Unix file path', function() - it('file path includes only ascii charactors', function() + it('file path includes only ascii characters', function() exec_lua("uri = 'file:///Foo/Bar/Baz.txt'") eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)")) end) + it('local file path without hostname', function() + exec_lua("uri = 'file:/Foo/Bar/Baz.txt'") + + eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)")) + end) + it('file path including white space', function() exec_lua("uri = 'file:///Foo%20/Bar/Baz.txt'") @@ -85,6 +91,15 @@ describe('URI methods', function() eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case)) end) + it('local file path without hostname', function() + local test_case = [[ + local uri = 'file:/C:/Foo/Bar/Baz.txt' + return vim.uri_to_fname(uri) + ]] + + eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case)) + end) + it('file path includes only ascii charactors with encoded colon character', function() local test_case = [[ local uri = 'file:///C%3A/Foo/Bar/Baz.txt' @@ -125,6 +140,12 @@ describe('URI methods', function() return vim.uri_to_fname('JDT://content/%5C/') ]]) end) + + it('uri_to_fname returns non-file scheme URI without authority unchanged', function() + eq('zipfile:/path/to/archive.zip%3A%3Afilename.txt', exec_lua [[ + return vim.uri_to_fname('zipfile:/path/to/archive.zip%3A%3Afilename.txt') + ]]) + end) end) describe('decode URI without scheme', function() @@ -146,5 +167,14 @@ describe('URI methods', function() ]], uri) eq(uri, exec_lua(test_case)) end) + + it('uri_to_bufnr & uri_from_bufnr returns original uri for non-file uris without authority', function() + local uri = 'zipfile:/path/to/archive.zip%3A%3Afilename.txt' + local test_case = string.format([[ + local uri = '%s' + return vim.uri_from_bufnr(vim.uri_to_bufnr(uri)) + ]], uri) + eq(uri, exec_lua(test_case)) + end) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index eff838aea3..a066cfbc10 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -6,6 +6,7 @@ local funcs = helpers.funcs local meths = helpers.meths local dedent = helpers.dedent local command = helpers.command +local insert = helpers.insert local clear = helpers.clear local eq = helpers.eq local ok = helpers.ok @@ -17,6 +18,7 @@ local matches = helpers.matches local source = helpers.source local NIL = helpers.NIL local retry = helpers.retry +local next_msg = helpers.next_msg before_each(clear) @@ -603,6 +605,31 @@ describe('lua stdlib', function() return vim.tbl_islist(c) and count == 0 ]])) + eq(exec_lua([[ + local a = { a = { b = 1 } } + local b = { a = {} } + return vim.tbl_deep_extend("force", a, b) + ]]), {a = {b = 1}}) + + eq(exec_lua([[ + local a = { a = 123 } + local b = { a = { b = 1} } + return vim.tbl_deep_extend("force", a, b) + ]]), {a = {b = 1}}) + + ok(exec_lua([[ + local a = { a = {[2] = 3} } + local b = { a = {[3] = 3} } + local c = vim.tbl_deep_extend("force", a, b) + return vim.deep_equal(c, {a = {[3] = 3}}) + ]])) + + eq(exec_lua([[ + local a = { a = { b = 1} } + local b = { a = 123 } + return vim.tbl_deep_extend("force", a, b) + ]]), {a = 123 }) + eq('Error executing lua: vim/shared.lua:0: invalid "behavior": nil', pcall_err(exec_lua, [[ return vim.tbl_deep_extend() @@ -712,7 +739,7 @@ describe('lua stdlib', function() eq({NIL, NIL}, exec_lua([[return vim.fn.Nilly()]])) -- error handling - eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]])) + eq({false, 'Vim:E897: List or Blob required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]])) end) it('vim.fn should error when calling API function', function() @@ -1332,12 +1359,12 @@ describe('lua stdlib', function() it('should work for key-value pair options', function() local listchars = exec_lua [[ - vim.opt.listchars = "tab:>~,space:_" + vim.opt.listchars = "tab:> ,space:_" return vim.opt.listchars:get() ]] eq({ - tab = ">~", + tab = "> ", space = "_", }, listchars) end) @@ -1855,7 +1882,7 @@ describe('lua stdlib', function() end) it('vim.region', function() - helpers.insert(helpers.dedent( [[ + insert(helpers.dedent( [[ text tααt tααt text text tαxt txtα tex text tαxt tαxt @@ -1863,65 +1890,67 @@ 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 ]]) + describe('vim.on_key', function() + it('tracks keystrokes', function() + insert([[hello world ]]) exec_lua [[ - KeysPressed = {} + keys = {} - vim.register_keystroke_callback(function(buf) + vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) end) ]] - helpers.insert([[next 🤦 lines å ]]) + insert([[next 🤦 lines å ]]) -- It has escape in the keys pressed - eq('inext 🤦 lines å <ESC>', exec_lua [[return table.concat(KeysPressed, '')]]) + eq('inext 🤦 lines å <ESC>', exec_lua [[return table.concat(keys, '')]]) end) - it('should allow removing trackers.', function() - helpers.insert([[hello world]]) + it('allows removing on_key listeners', function() + insert([[hello world]]) exec_lua [[ - KeysPressed = {} + keys = {} - return vim.register_keystroke_callback(function(buf) + return vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) end, vim.api.nvim_create_namespace("logger")) ]] - helpers.insert([[next lines]]) + insert([[next lines]]) - exec_lua("vim.register_keystroke_callback(nil, vim.api.nvim_create_namespace('logger'))") + eq(1, exec_lua('return vim.on_key()')) + exec_lua("vim.on_key(nil, vim.api.nvim_create_namespace('logger'))") + eq(0, exec_lua('return vim.on_key()')) - helpers.insert([[more lines]]) + insert([[more lines]]) -- It has escape in the keys pressed - eq('inext lines<ESC>', exec_lua [[return table.concat(KeysPressed, '')]]) + eq('inext lines<ESC>', exec_lua [[return table.concat(keys, '')]]) end) - it('should not call functions that error again.', function() - helpers.insert([[hello world]]) + it('skips any function that caused an error', function() + insert([[hello world]]) exec_lua [[ - KeysPressed = {} + keys = {} - return vim.register_keystroke_callback(function(buf) + return vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) if buf == 'l' then error("Dumb Error") @@ -1929,35 +1958,30 @@ describe('lua stdlib', function() end) ]] - helpers.insert([[next lines]]) - helpers.insert([[more lines]]) + insert([[next lines]]) + insert([[more lines]]) -- Only the first letter gets added. After that we remove the callback - eq('inext l', exec_lua [[ return table.concat(KeysPressed, '') ]]) + eq('inext l', exec_lua [[ return table.concat(keys, '') ]]) end) - it('should process mapped keys, not unmapped keys', function() + it('processes mapped keys, not unmapped keys', function() exec_lua [[ - KeysPressed = {} + keys = {} vim.cmd("inoremap hello world") - vim.register_keystroke_callback(function(buf) + vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) end) ]] + insert("hello") - helpers.insert("hello") - - local next_status = exec_lua [[ - return table.concat(KeysPressed, '') - ]] - - eq("iworld<ESC>", next_status) + eq('iworld<ESC>', exec_lua[[return table.concat(keys, '')]]) end) end) @@ -2153,6 +2177,24 @@ describe('lua stdlib', function() end) end) + describe('vim.schedule_wrap', function() + it('preserves argument lists', function() + exec_lua [[ + local fun = vim.schedule_wrap(function(kling, klang, klonk) + vim.rpcnotify(1, 'mayday_mayday', {a=kling, b=klang, c=klonk}) + end) + fun("BOB", nil, "MIKE") + ]] + eq({'notification', 'mayday_mayday', {{a='BOB', c='MIKE'}}}, next_msg()) + + -- let's gooooo + exec_lua [[ + vim.schedule_wrap(function(...) vim.rpcnotify(1, 'boogalo', select('#', ...)) end)(nil,nil,nil,nil) + ]] + eq({'notification', 'boogalo', {4}}, next_msg()) + end) + end) + describe('vim.api.nvim_buf_call', function() it('can access buf options', function() local buf1 = meths.get_current_buf() diff --git a/test/functional/lua/xdiff_spec.lua b/test/functional/lua/xdiff_spec.lua new file mode 100644 index 0000000000..4f28f84c01 --- /dev/null +++ b/test/functional/lua/xdiff_spec.lua @@ -0,0 +1,112 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local pcall_err = helpers.pcall_err + +describe('xdiff bindings', function() + before_each(function() + clear() + end) + + describe('can diff text', function() + before_each(function() + exec_lua[[ + a1 = 'Hello\n' + b1 = 'Helli\n' + + a2 = 'Hello\nbye\nfoo\n' + b2 = 'Helli\nbye\nbar\nbaz\n' + ]] + end) + + it('with no callback', function() + + eq( + table.concat({ + '@@ -1 +1 @@', + '-Hello', + '+Helli', + '' + }, '\n'), + exec_lua("return vim.diff(a1, b1)") + ) + + eq( + table.concat({ + '@@ -1 +1 @@', + '-Hello', + '+Helli', + '@@ -3 +3,2 @@', + '-foo', + '+bar', + '+baz', + '' + }, '\n'), + exec_lua("return vim.diff(a2, b2)") + ) + + end) + + it('with callback', function() + exec_lua([[on_hunk = function(sa, ca, sb, cb) + exp[#exp+1] = {sa, ca, sb, cb} + end]]) + + eq({{1, 1, 1, 1}}, exec_lua[[ + exp = {} + assert(vim.diff(a1, b1, {on_hunk = on_hunk}) == nil) + return exp + ]]) + + eq({{1, 1, 1, 1}, {3, 1, 3, 2}}, exec_lua[[ + exp = {} + assert(vim.diff(a2, b2, {on_hunk = on_hunk}) == nil) + return exp + ]]) + + -- gives higher precedence to on_hunk over result_type + eq({{1, 1, 1, 1}, {3, 1, 3, 2}}, exec_lua[[ + exp = {} + assert(vim.diff(a2, b2, {on_hunk = on_hunk, result_type='indices'}) == nil) + return exp + ]]) + end) + + it('with error callback', function() + exec_lua([[on_hunk = function(sa, ca, sb, cb) + error('ERROR1') + end]]) + + eq([[Error executing lua: [string "<nvim>"]:0: error running function on_hunk: [string "<nvim>"]:0: ERROR1]], + pcall_err(exec_lua, [[vim.diff(a1, b1, {on_hunk = on_hunk})]])) + end) + + it('with hunk_lines', function() + eq({{1, 1, 1, 1}}, + exec_lua([[return vim.diff(a1, b1, {result_type = 'indices'})]])) + + eq({{1, 1, 1, 1}, {3, 1, 3, 2}}, + exec_lua([[return vim.diff(a2, b2, {result_type = 'indices'})]])) + end) + + end) + + it('can handle bad args', function() + eq([[Error executing lua: [string "<nvim>"]:0: Expected at least 2 arguments]], + pcall_err(exec_lua, [[vim.diff('a')]])) + + eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'diff' (expected string)]], + pcall_err(exec_lua, [[vim.diff(1, 2)]])) + + eq([[Error executing lua: [string "<nvim>"]:0: bad argument #3 to 'diff' (expected table)]], + pcall_err(exec_lua, [[vim.diff('a', 'b', true)]])) + + eq([[Error executing lua: [string "<nvim>"]:0: unexpected key: bad_key]], + pcall_err(exec_lua, [[vim.diff('a', 'b', { bad_key = true })]])) + + eq([[Error executing lua: [string "<nvim>"]:0: on_hunk is not a function]], + pcall_err(exec_lua, [[vim.diff('a', 'b', { on_hunk = true })]])) + + end) +end) diff --git a/test/functional/normal/meta_key_spec.lua b/test/functional/normal/meta_key_spec.lua deleted file mode 100644 index 9f9fad67d2..0000000000 --- a/test/functional/normal/meta_key_spec.lua +++ /dev/null @@ -1,22 +0,0 @@ -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 eb5e284385..6620c9acef 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local meths = helpers.meths local command = helpers.command local clear = helpers.clear @@ -354,13 +355,13 @@ describe('XDG-based defaults', function() .. ',' .. 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', + 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', + eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/undo//', (meths.get_option('undodir')):gsub('\\', '/')) - eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/view', + eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/view//', (meths.get_option('viewdir')):gsub('\\', '/')) end) end) @@ -404,13 +405,13 @@ describe('XDG-based defaults', function() .. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_HOME/nvim/after' ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) - eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup'), + 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'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'), meths.get_option('undodir'):gsub('\\', '/')) - eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'), meths.get_option('viewdir'):gsub('\\', '/')) meths.command('set all&') eq(('$XDG_DATA_HOME/nvim' @@ -424,13 +425,13 @@ describe('XDG-based defaults', function() .. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_HOME/nvim/after' ):gsub('\\', '/'), (meths.get_option('runtimepath')):gsub('\\', '/')) - eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup'), + 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'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'), meths.get_option('undodir'):gsub('\\', '/')) - eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'), meths.get_option('viewdir'):gsub('\\', '/')) end) end) @@ -483,13 +484,13 @@ describe('XDG-based defaults', function() .. ',\\,-\\,-\\,' .. path_sep ..'nvim' .. path_sep ..'after' .. ',\\, \\, \\,' .. path_sep ..'nvim' .. path_sep ..'after' ), meths.get_option('runtimepath')) - eq('.,\\,=\\,=\\,' .. path_sep .. data_dir .. '' .. path_sep ..'backup', + eq('.,\\,=\\,=\\,' .. path_sep .. data_dir .. '' .. path_sep ..'backup' .. (path_sep):rep(2), 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', + eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'undo' .. (path_sep):rep(2), meths.get_option('undodir')) - eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'view', + eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'view' .. (path_sep):rep(2), meths.get_option('viewdir')) end) end) @@ -510,8 +511,7 @@ describe('stdpath()', function() eq(datadir, funcs.fnamemodify(funcs.stdpath('data'), ':t')) eq('table', type(funcs.stdpath('config_dirs'))) eq('table', type(funcs.stdpath('data_dirs'))) - -- Check for crash. #8393 - eq(2, eval('1+1')) + assert_alive() -- Check for crash. #8393 end) context('returns a String', function() diff --git a/test/functional/options/tabstop_spec.lua b/test/functional/options/tabstop_spec.lua index dc3ba38438..e34f678650 100644 --- a/test/functional/options/tabstop_spec.lua +++ b/test/functional/options/tabstop_spec.lua @@ -1,9 +1,8 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear = helpers.clear local feed = helpers.feed -local eq = helpers.eq -local eval = helpers.eval describe("'tabstop' option", function() before_each(function() @@ -18,6 +17,6 @@ describe("'tabstop' option", function() -- Set 'tabstop' to a very high value. -- Use feed(), not command(), to provoke crash. feed(':set tabstop=3000000000<CR>') - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua index e09d93f7cc..c8b75e65fc 100644 --- a/test/functional/plugin/lsp/codelens_spec.lua +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -11,7 +11,7 @@ describe('vim.lsp.codelens', function() after_each(helpers.clear) it('on_codelens_stores_and_displays_lenses', function() - local fake_uri = "file://fake/uri" + local fake_uri = "file:///fake/uri" local bufnr = exec_lua([[ fake_uri = ... local bufnr = vim.uri_to_bufnr(fake_uri) @@ -32,7 +32,7 @@ describe('vim.lsp.codelens', function() command = { title = 'Lens1', command = 'Dummy' } }, } - vim.lsp.codelens.on_codelens(nil, 'textDocument/codeLens', lenses, 1, bufnr) + vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr}) ]], bufnr) local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr) @@ -58,5 +58,33 @@ describe('vim.lsp.codelens', function() ]], bufnr) eq({[1] = {'Lens1', 'LspCodeLens'}}, virtual_text_chunks) + + end) + it('codelens uses client commands', function() + local fake_uri = "file:///fake/uri" + local cmd = exec_lua([[ + fake_uri = ... + local bufnr = vim.uri_to_bufnr(fake_uri) + vim.fn.bufload(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'One line'}) + local lenses = { + { + range = { + start = { line = 0, character = 0, }, + ['end'] = { line = 0, character = 8 } + }, + command = { title = 'Lens1', command = 'Dummy' } + }, + } + vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr}) + local cmd_called = nil + vim.lsp.commands['Dummy'] = function(command) + cmd_called = command + end + vim.api.nvim_set_current_buf(bufnr) + vim.lsp.codelens.run() + return cmd_called + ]], fake_uri) + eq({ command = 'Dummy', title = 'Lens1' }, cmd) end) end) diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua index 962028e7e1..243ad6bdb8 100644 --- a/test/functional/plugin/lsp/diagnostic_spec.lua +++ b/test/functional/plugin/lsp/diagnostic_spec.lua @@ -9,7 +9,10 @@ describe('vim.lsp.diagnostic', function() local fake_uri before_each(function() - clear() + clear {env={ + NVIM_LUA_NOTRACK="1"; + VIMRUNTIME=os.getenv"VIMRUNTIME"; + }} exec_lua [[ require('vim.lsp') @@ -44,12 +47,12 @@ describe('vim.lsp.diagnostic', function() count_of_extmarks_for_client = function(bufnr, client_id) return #vim.api.nvim_buf_get_extmarks( - bufnr, vim.lsp.diagnostic._get_diagnostic_namespace(client_id), 0, -1, {} + bufnr, vim.lsp.diagnostic.get_namespace(client_id), 0, -1, {} ) end ]] - fake_uri = "file://fake/uri" + fake_uri = "file:///fake/uri" exec_lua([[ fake_uri = ... @@ -86,39 +89,6 @@ describe('vim.lsp.diagnostic', function() eq(2, #result[1]) eq('Diagnostic #1', result[1][1].message) end) - it('Can convert diagnostic to quickfix items format', function() - local bufnr = exec_lua([[ - local fake_uri = ... - return vim.uri_to_bufnr(fake_uri) - ]], fake_uri) - local result = exec_lua([[ - local bufnr = ... - vim.lsp.diagnostic.save( - { - make_error('Diagnostic #1', 1, 1, 1, 1), - make_error('Diagnostic #2', 2, 1, 2, 1), - }, bufnr, 1 - ) - return vim.lsp.util.diagnostics_to_items(vim.lsp.diagnostic.get_all()) - ]], bufnr) - local expected = { - { - bufnr = bufnr, - col = 2, - lnum = 2, - text = 'Diagnostic #1', - type = 'E' - }, - { - bufnr = bufnr, - col = 2, - lnum = 3, - text = 'Diagnostic #2', - type = 'E' - }, - } - eq(expected, result) - end) it('should be able to save and count a single client error', function() eq(1, exec_lua [[ vim.lsp.diagnostic.save( @@ -205,8 +175,8 @@ describe('vim.lsp.diagnostic', function() make_warning("Warning 1", 2, 1, 2, 5), } - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_1_diags }, 1) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_2_diags }, 2) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1}) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2}) return { vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), @@ -218,7 +188,7 @@ describe('vim.lsp.diagnostic', function() -- Clear diagnostics from server 1, and make sure we have the right amount of stuff for client 2 eq({1, 1, 2, 0, 2}, exec_lua [[ - vim.lsp.diagnostic.clear(diagnostic_bufnr, 1) + vim.lsp.diagnostic.disable(diagnostic_bufnr, 1) return { vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), @@ -230,7 +200,7 @@ describe('vim.lsp.diagnostic', function() -- Show diagnostics from server 1 again eq(all_highlights, exec_lua([[ - vim.lsp.diagnostic.display(nil, diagnostic_bufnr, 1) + vim.lsp.diagnostic.enable(diagnostic_bufnr, 1) return { vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), @@ -241,6 +211,38 @@ describe('vim.lsp.diagnostic', function() ]])) end) + it('should not display diagnostics when disabled', function() + eq({0, 2}, exec_lua [[ + local server_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local server_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1}) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2}) + + vim.lsp.diagnostic.disable(diagnostic_bufnr, 1) + + return { + count_of_extmarks_for_client(diagnostic_bufnr, 1), + count_of_extmarks_for_client(diagnostic_bufnr, 2), + } + ]]) + + eq({4, 0}, exec_lua [[ + vim.lsp.diagnostic.enable(diagnostic_bufnr, 1) + vim.lsp.diagnostic.disable(diagnostic_bufnr, 2) + + return { + count_of_extmarks_for_client(diagnostic_bufnr, 1), + count_of_extmarks_for_client(diagnostic_bufnr, 2), + } + ]]) + end) + describe('reset', function() it('diagnostic count is 0 and displayed diagnostics are 0 after call', function() -- 1 Error (1) @@ -258,8 +260,8 @@ describe('vim.lsp.diagnostic', function() make_warning("Warning 1", 2, 1, 2, 5), } - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_1_diags }, 1) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_2_diags }, 2) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1}) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2}) return { vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), @@ -426,6 +428,32 @@ describe('vim.lsp.diagnostic', function() end) end) end) + + it('maintains LSP information when translating diagnostics', function() + local result = exec_lua [[ + local diagnostics = { + make_error("Error 1", 1, 1, 1, 5), + } + + diagnostics[1].code = 42 + diagnostics[1].tags = {"foo", "bar"} + diagnostics[1].data = "Hello world" + + vim.lsp.diagnostic.on_publish_diagnostics(nil, { + uri = fake_uri, + diagnostics = diagnostics, + }, {client_id=1}) + + return { + vim.diagnostic.get(diagnostic_bufnr, {lnum=1})[1], + vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1)[1], + } + ]] + eq({code = 42, tags = {"foo", "bar"}, data = "Hello world"}, result[1].user_data.lsp) + eq(42, result[2].code) + eq({"foo", "bar"}, result[2].tags) + eq("Hello world", result[2].data) + end) end) describe("vim.lsp.diagnostic.get_line_diagnostics", function() @@ -435,14 +463,14 @@ describe('vim.lsp.diagnostic', function() it('should return all diagnostics when no severity is supplied', function() eq(2, exec_lua [[ - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 5), make_error("Error On Other Line", 2, 1, 1, 5), } - }, 1) + }, {client_id=1}) return #vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1) ]]) @@ -450,7 +478,7 @@ describe('vim.lsp.diagnostic', function() it('should return only requested diagnostics when severity_limit is supplied', function() eq(2, exec_lua [[ - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error("Error 1", 1, 1, 1, 5), @@ -458,7 +486,7 @@ describe('vim.lsp.diagnostic', function() make_information("Ignored information", 1, 1, 2, 5), make_error("Error On Other Line", 2, 1, 1, 5), } - }, 1) + }, {client_id=1}) return #vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1, { severity_limit = "Warning" }) ]]) @@ -470,12 +498,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { virtual_text = function() return true end, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -487,12 +515,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { virtual_text = function() return false end, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -509,12 +537,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { update_in_insert = false, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -543,20 +571,20 @@ describe('vim.lsp.diagnostic', function() }) -- Count how many times we call display. - SetVirtualTextOriginal = vim.lsp.diagnostic.set_virtual_text + SetVirtualTextOriginal = vim.diagnostic._set_virtual_text DisplayCount = 0 - vim.lsp.diagnostic.set_virtual_text = function(...) + vim.diagnostic._set_virtual_text = function(...) DisplayCount = DisplayCount + 1 return SetVirtualTextOriginal(...) end - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -605,12 +633,12 @@ describe('vim.lsp.diagnostic', function() return SetVirtualTextOriginal(...) end - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -647,12 +675,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { update_in_insert = true, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -677,17 +705,17 @@ describe('vim.lsp.diagnostic', function() }, }) - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) return vim.api.nvim_buf_get_extmarks( diagnostic_bufnr, - vim.lsp.diagnostic._get_diagnostic_namespace(1), + vim.lsp.diagnostic.get_namespace(1), 0, -1, { details = true } @@ -714,17 +742,17 @@ describe('vim.lsp.diagnostic', function() end, }) - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) return vim.api.nvim_buf_get_extmarks( diagnostic_bufnr, - vim.lsp.diagnostic._get_diagnostic_namespace(1), + vim.lsp.diagnostic.get_namespace(1), 0, -1, { details = true } @@ -747,12 +775,12 @@ describe('vim.lsp.diagnostic', function() }, }) - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_warning('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) return count_of_extmarks_for_client(diagnostic_bufnr, 1) @@ -766,6 +794,40 @@ describe('vim.lsp.diagnostic', function() eq(1, get_extmark_count_with_severity("Warning")) eq(1, get_extmark_count_with_severity("Hint")) end) + + it('correctly handles UTF-16 offsets', function() + local line = "All 💼 and no 🎉 makes Jack a dull 👦" + local result = exec_lua([[ + local line = ... + local client_id = vim.lsp.start_client { + cmd_env = { + NVIM_LUA_NOTRACK = "1"; + }; + cmd = { + vim.v.progpath, '-es', '-u', 'NONE', '--headless' + }; + offset_encoding = "utf-16"; + } + + vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, -1, false, {line}) + + vim.lsp.diagnostic.on_publish_diagnostics(nil, { + uri = fake_uri, + diagnostics = { + make_error('UTF-16 Diagnostic', 0, 7, 0, 8), + } + }, {client_id=client_id} + ) + + local diags = vim.diagnostic.get(diagnostic_bufnr) + vim.lsp.stop_client(client_id) + vim.lsp._vim_exit_handler() + return diags + ]], line) + eq(1, #result) + eq(exec_lua([[return vim.str_byteindex(..., 7, true)]], line), result[1].col) + eq(exec_lua([[return vim.str_byteindex(..., 8, true)]], line), result[1].end_col) + end) end) describe('lsp.util.show_line_diagnostics', function() @@ -838,10 +900,10 @@ describe('vim.lsp.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = diagnostics - }, 1 + }, {client_id=1} ) vim.lsp.diagnostic.set_signs(diagnostics, diagnostic_bufnr, 1) @@ -863,13 +925,13 @@ describe('vim.lsp.diagnostic', function() local loc_list = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Farther Diagnostic', 4, 4, 4, 4), make_error('Lower Diagnostic', 1, 1, 1, 1), } - }, 1 + }, {client_id=1} ) vim.lsp.diagnostic.set_loclist() @@ -884,20 +946,20 @@ describe('vim.lsp.diagnostic', function() local loc_list = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Lower Diagnostic', 1, 1, 1, 1), } - }, 1 + }, {client_id=1} ) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_warning('Farther Diagnostic', 4, 4, 4, 4), } - }, 2 + }, {client_id=2} ) vim.lsp.diagnostic.set_loclist() diff --git a/test/functional/plugin/lsp/snippet_spec.lua b/test/functional/plugin/lsp/snippet_spec.lua new file mode 100644 index 0000000000..4e127743eb --- /dev/null +++ b/test/functional/plugin/lsp/snippet_spec.lua @@ -0,0 +1,152 @@ +local helpers = require('test.functional.helpers')(after_each) +local snippet = require('vim.lsp._snippet') + +local eq = helpers.eq +local exec_lua = helpers.exec_lua + +describe('vim.lsp._snippet', function() + before_each(helpers.clear) + after_each(helpers.clear) + + local parse = function(...) + return exec_lua('return require("vim.lsp._snippet").parse(...)', ...) + end + + it('should parse only text', function() + eq({ + type = snippet.NodeType.SNIPPET, + children = { + { + type = snippet.NodeType.TEXT, + raw = 'TE\\$\\}XT', + esc = 'TE$}XT' + } + } + }, parse('TE\\$\\}XT')) + end) + + it('should parse tabstop', function() + eq({ + type = snippet.NodeType.SNIPPET, + children = { + { + type = snippet.NodeType.TABSTOP, + tabstop = 1, + }, + { + type = snippet.NodeType.TABSTOP, + tabstop = 2, + } + } + }, parse('$1${2}')) + end) + + it('should parse placeholders', function() + eq({ + type = snippet.NodeType.SNIPPET, + children = { + { + type = snippet.NodeType.PLACEHOLDER, + tabstop = 1, + children = { + { + type = snippet.NodeType.PLACEHOLDER, + tabstop = 2, + children = { + { + type = snippet.NodeType.TEXT, + raw = 'TE\\$\\}XT', + esc = 'TE$}XT' + }, + { + type = snippet.NodeType.TABSTOP, + tabstop = 3, + }, + { + type = snippet.NodeType.TABSTOP, + tabstop = 1, + transform = { + type = snippet.NodeType.TRANSFORM, + pattern = 'regex', + option = 'i', + format = { + { + type = snippet.NodeType.FORMAT, + capture_index = 1, + modifier = 'upcase' + } + } + }, + }, + { + type = snippet.NodeType.TEXT, + raw = 'TE\\$\\}XT', + esc = 'TE$}XT' + }, + } + } + } + }, + } + }, parse('${1:${2:TE\\$\\}XT$3${1/regex/${1:/upcase}/i}TE\\$\\}XT}}')) + end) + + it('should parse variables', function() + eq({ + type = snippet.NodeType.SNIPPET, + children = { + { + type = snippet.NodeType.VARIABLE, + name = 'VAR', + }, + { + type = snippet.NodeType.VARIABLE, + name = 'VAR', + }, + { + type = snippet.NodeType.VARIABLE, + name = 'VAR', + children = { + { + type = snippet.NodeType.TABSTOP, + tabstop = 1, + } + } + }, + { + type = snippet.NodeType.VARIABLE, + name = 'VAR', + transform = { + type = snippet.NodeType.TRANSFORM, + pattern = 'regex', + format = { + { + type = snippet.NodeType.FORMAT, + capture_index = 1, + modifier = 'upcase', + } + } + } + }, + } + }, parse('$VAR${VAR}${VAR:$1}${VAR/regex/${1:/upcase}/}')) + end) + + it('should parse choice', function() + eq({ + type = snippet.NodeType.SNIPPET, + children = { + { + type = snippet.NodeType.CHOICE, + tabstop = 1, + items = { + ',', + '|' + } + } + } + }, parse('${1|\\,,\\||}')) + end) + +end) + diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 508a9f2aed..572573a3a6 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -6,6 +6,7 @@ local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq +local matches = helpers.matches local pcall_err = helpers.pcall_err local pesc = helpers.pesc local insert = helpers.insert @@ -14,6 +15,7 @@ local retry = helpers.retry local NIL = helpers.NIL local read_file = require('test.helpers').read_file local write_file = require('test.helpers').write_file +local isCI = helpers.isCI -- Use these to get access to a coroutine so that I can run async tests and use -- yield. @@ -216,7 +218,7 @@ describe('LSP', function() it('should run correctly', function() local expected_handlers = { - {NIL, "test", {}, 1}; + {NIL, {}, {method="test", client_id=1}}; } test_rpc_server { test_name = "basic_init"; @@ -241,7 +243,7 @@ describe('LSP', function() it('should fail', function() local expected_handlers = { - {NIL, "test", {}, 1}; + {NIL, {}, {method="test", client_id=1}}; } test_rpc_server { test_name = "basic_init"; @@ -262,15 +264,15 @@ describe('LSP', function() end) it('should succeed with manual shutdown', function() - if 'openbsd' == helpers.uname() then - pending('hangs the build on openbsd #14028, re-enable with freeze timeout #14204') + if isCI() then + pending('hangs the build on CI #14028, re-enable with freeze timeout #14204') return elseif helpers.skip_fragile(pending) then return end local expected_handlers = { - {NIL, "shutdown", {}, 1, NIL}; - {NIL, "test", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="test", client_id=1}}; } test_rpc_server { test_name = "basic_init"; @@ -292,12 +294,12 @@ describe('LSP', function() it('client should return settings via workspace/configuration handler', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "workspace/configuration", { items = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, { items = { { section = "testSetting1" }; { section = "testSetting2" }; - }}, 1}; - {NIL, "start", {}, 1}; + }}, { method="workspace/configuration", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -309,9 +311,9 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'start' then + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'start' then exec_lua([=[ local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID) client.config.settings = { @@ -319,13 +321,13 @@ describe('LSP', function() testSetting2 = false; }]=]) end - if method == 'workspace/configuration' then - local result = exec_lua([=[ + if ctx.method == 'workspace/configuration' then + local server_result = exec_lua([=[ local method, params = ... - return require'vim.lsp.handlers'['workspace/configuration'](err, method, params, TEST_RPC_CLIENT_ID)]=], method, params) - client.notify('workspace/configuration', result) + return require'vim.lsp.handlers'['workspace/configuration'](err, params, {method=method, client_id=TEST_RPC_CLIENT_ID})]=], ctx.method, result) + client.notify('workspace/configuration', server_result) end - if method == 'shutdown' then + if ctx.method == 'shutdown' then client.stop() end end; @@ -335,19 +337,19 @@ describe('LSP', function() clear_notrace() fake_lsp_server_setup('workspace/configuration no settings') eq({ NIL, NIL, }, exec_lua [[ - local params = { + local result = { items = { {section = 'foo'}, {section = 'bar'}, } } - return vim.lsp.handlers['workspace/configuration'](nil, nil, params, TEST_RPC_CLIENT_ID) + return vim.lsp.handlers['workspace/configuration'](nil, result, {client_id=TEST_RPC_CLIENT_ID}) ]]) end) it('should verify capabilities sent', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "basic_check_capabilities"; @@ -371,7 +373,7 @@ describe('LSP', function() it('client.supports_methods() should validate capabilities', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "capabilities_for_client_supports_method"; @@ -405,7 +407,7 @@ describe('LSP', function() it('should call unsupported_method when trying to call an unsupported method', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "capabilities_for_client_supports_method"; @@ -413,7 +415,8 @@ describe('LSP', function() exec_lua([=[ BUFFER = vim.api.nvim_get_current_buf() lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID) - vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method) + vim.lsp.handlers['textDocument/typeDefinition'] = function(err, result, ctx) + local method = ctx.method vim.lsp._last_lsp_handler = { err = err; method = method } end vim.lsp._unsupported_method = function(method) @@ -446,7 +449,7 @@ describe('LSP', function() it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "capabilities_for_client_supports_method"; @@ -479,8 +482,8 @@ describe('LSP', function() it('should not send didOpen if the buffer closes before init', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; } local client test_rpc_server { @@ -511,9 +514,9 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -522,9 +525,9 @@ describe('LSP', function() it('should check the body sent attaching before init', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -554,12 +557,12 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -568,9 +571,9 @@ describe('LSP', function() it('should check the body sent attaching after init', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -597,12 +600,12 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -611,9 +614,9 @@ describe('LSP', function() it('should check the body and didChange full', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -640,8 +643,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "boop"; @@ -649,8 +652,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -659,9 +662,9 @@ describe('LSP', function() it('should check the body and didChange full with noeol', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -689,8 +692,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "boop"; @@ -698,8 +701,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -708,9 +711,9 @@ describe('LSP', function() it('should check the body and didChange incremental', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -738,8 +741,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "123boop"; @@ -747,8 +750,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -758,9 +761,9 @@ describe('LSP', function() -- TODO(askhan) we don't support full for now, so we can disable these tests. pending('should check the body and didChange incremental normal mode editing', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -787,13 +790,13 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then helpers.command("normal! 1Go") client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -802,9 +805,9 @@ describe('LSP', function() it('should check the body and didChange full with 2 changes', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -831,8 +834,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "321"; @@ -843,8 +846,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -853,9 +856,9 @@ describe('LSP', function() it('should check the body and didChange full lifecycle', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -882,8 +885,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result,ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "321"; @@ -895,8 +898,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -907,9 +910,9 @@ describe('LSP', function() describe("parsing tests", function() it('should handle invalid content-length correctly', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -924,22 +927,22 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") end; } end) it('should not trim vim.NIL from the end of a list', function() local expected_handlers = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "workspace/executeCommand", { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL,{ arguments = { "EXTRACT_METHOD", {metadata = {}}, 3, 0, 6123, NIL }, command = "refactor.perform", title = "EXTRACT_METHOD" - }, 1}; - {NIL, "start", {}, 1}; + }, {method="workspace/executeCommand", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -963,9 +966,9 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler") - if method == 'finish' then + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -1014,31 +1017,6 @@ describe('LSP', function() } end - it('highlight groups', function() - eq({ - 'LspDiagnosticsDefaultError', - 'LspDiagnosticsDefaultHint', - 'LspDiagnosticsDefaultInformation', - 'LspDiagnosticsDefaultWarning', - 'LspDiagnosticsFloatingError', - 'LspDiagnosticsFloatingHint', - 'LspDiagnosticsFloatingInformation', - 'LspDiagnosticsFloatingWarning', - 'LspDiagnosticsSignError', - 'LspDiagnosticsSignHint', - 'LspDiagnosticsSignInformation', - 'LspDiagnosticsSignWarning', - 'LspDiagnosticsUnderlineError', - 'LspDiagnosticsUnderlineHint', - 'LspDiagnosticsUnderlineInformation', - 'LspDiagnosticsUnderlineWarning', - 'LspDiagnosticsVirtualTextError', - 'LspDiagnosticsVirtualTextHint', - 'LspDiagnosticsVirtualTextInformation', - 'LspDiagnosticsVirtualTextWarning', - }, exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]])) - end) - describe('apply_text_edits', function() before_each(function() insert(dedent([[ @@ -1088,6 +1066,30 @@ describe('LSP', function() 'å å ɧ 汉语 ↥ 🤦 🦄'; }, buf_lines(1)) end) + it('applies complex edits (reversed range)', function() + local edits = { + make_edit(0, 0, 0, 0, {"", "12"}); + make_edit(0, 0, 0, 0, {"3", "foo"}); + make_edit(0, 1, 0, 1, {"bar", "123"}); + make_edit(0, #"First line of text", 0, #"First ", {"guy"}); + make_edit(1, #'Second', 1, 0, {"baz"}); + make_edit(2, #"Third", 2, #'Th', {"e next"}); + make_edit(3, #"Fourth", 3, #'', {"another line of text", "before this"}); + make_edit(3, #"Fourth line of text", 3, #'Fourth', {"!"}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + ''; + '123'; + 'fooFbar'; + '123irst guy'; + 'baz line of text'; + 'The next line of text'; + 'another line of text'; + 'before this!'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + end) it('applies non-ASCII characters edits', function() local edits = { make_edit(4, 3, 4, 4, {"ä"}); @@ -1116,6 +1118,86 @@ describe('LSP', function() }, buf_lines(1)) end) + describe('cursor position', function() + it('don\'t fix the cursor if the range contains the cursor', function() + funcs.nvim_win_set_cursor(0, { 2, 6 }) + local edits = { + make_edit(1, 0, 1, 19, 'Second line of text') + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + 'First line of text'; + 'Second line of text'; + 'Third line of text'; + 'Fourth line of text'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + eq({ 2, 6 }, funcs.nvim_win_get_cursor(0)) + end) + + it('fix the cursor to the valid column if the content was removed', function() + funcs.nvim_win_set_cursor(0, { 2, 6 }) + local edits = { + make_edit(1, 0, 1, 19, '') + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + 'First line of text'; + ''; + 'Third line of text'; + 'Fourth line of text'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + eq({ 2, 0 }, funcs.nvim_win_get_cursor(0)) + end) + + it('fix the cursor row', function() + funcs.nvim_win_set_cursor(0, { 3, 0 }) + local edits = { + make_edit(1, 0, 2, 0, '') + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + 'First line of text'; + 'Third line of text'; + 'Fourth line of text'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + eq({ 2, 0 }, funcs.nvim_win_get_cursor(0)) + end) + + it('fix the cursor col', function() + funcs.nvim_win_set_cursor(0, { 2, 11 }) + local edits = { + make_edit(1, 7, 1, 11, '') + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + 'First line of text'; + 'Second of text'; + 'Third line of text'; + 'Fourth line of text'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + eq({ 2, 7 }, funcs.nvim_win_get_cursor(0)) + end) + + it('fix the cursor row and col', function() + funcs.nvim_win_set_cursor(0, { 2, 12 }) + local edits = { + make_edit(0, 11, 1, 12, '') + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1) + eq({ + 'First line of text'; + 'Third line of text'; + 'Fourth line of text'; + 'å å ɧ 汉语 ↥ 🤦 🦄'; + }, buf_lines(1)) + eq({ 1, 11 }, funcs.nvim_win_get_cursor(0)) + end) + end) + describe('with LSP end line after what Vim considers to be the end line', function() it('applies edits when the last linebreak is considered a new line', function() local edits = { @@ -1149,14 +1231,14 @@ describe('LSP', function() make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄") }, textDocument = { - uri = "file://fake/uri"; + uri = "file:///fake/uri"; version = editVersion } } end before_each(function() target_bufnr = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"1st line of text", "2nd line of 语text"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) return bufnr @@ -1223,7 +1305,7 @@ describe('LSP', function() label = nil; edit = {}; } - return vim.lsp.handlers['workspace/applyEdit'](nil, nil, apply_edit) + return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit) ]]) end) end) @@ -1236,7 +1318,7 @@ describe('LSP', function() make_edit(row, 0, row, 1000, new_line) }, textDocument = { - uri = "file://fake/uri"; + uri = "file:///fake/uri"; version = editVersion } } @@ -1254,7 +1336,7 @@ describe('LSP', function() before_each(function() local ret = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = { "Original Line #1", "Original Line #2" @@ -1441,8 +1523,10 @@ describe('LSP', function() { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} }, -- nested snippet tokens { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} }, + -- braced tabstop + { label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, textEdit={} }, -- plain text - { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} }, + { label='foocar', sortText="k", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} }, } local completion_list_items = {items=completion_list} local expected = { @@ -1454,8 +1538,9 @@ describe('LSP', function() { 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', insertTextFormat=2, 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\\}', insertTextFormat=2, 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\\}', insertTextFormat=2, 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={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar()', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, 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="k", 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)) @@ -1534,19 +1619,19 @@ describe('LSP', function() it('Convert Location[] to items', function() local expected = { { - filename = 'fake/uri', + filename = '/fake/uri', lnum = 1, col = 3, text = 'testing' }, } local actual = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"testing", "123"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) local locations = { { - uri = 'file://fake/uri', + uri = 'file:///fake/uri', range = { start = { line = 0, character = 2 }, ['end'] = { line = 0, character = 3 }, @@ -1560,14 +1645,14 @@ describe('LSP', function() it('Convert LocationLink[] to items', function() local expected = { { - filename = 'fake/uri', + filename = '/fake/uri', lnum = 1, col = 3, text = 'testing' }, } local actual = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"testing", "123"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) local locations = { @@ -1781,14 +1866,14 @@ describe('LSP', function() local expected = { { col = 1, - filename = 'test_a', + filename = '/test_a', kind = 'File', lnum = 2, text = '[File] TestA' }, { col = 1, - filename = 'test_b', + filename = '/test_b', kind = 'Module', lnum = 4, text = '[Module] TestB' @@ -1811,7 +1896,7 @@ describe('LSP', function() line = 2 } }, - uri = "file://test_a" + uri = "file:///test_a" }, contanerName = "TestAContainer" }, @@ -1830,7 +1915,7 @@ describe('LSP', function() line = 4 } }, - uri = "file://test_b" + uri = "file:///test_b" }, contanerName = "TestBContainer" } @@ -1869,7 +1954,7 @@ describe('LSP', function() before_each(function() target_bufnr = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) return bufnr @@ -1878,7 +1963,7 @@ describe('LSP', function() local location = function(start_line, start_char, end_line, end_char) return { - uri = "file://fake/uri", + uri = "file:///fake/uri", range = { start = { line = start_line, character = start_char }, ["end"] = { line = end_line, character = end_char }, @@ -1903,7 +1988,7 @@ describe('LSP', function() it('jumps to a LocationLink', function() local pos = jump({ - targetUri = "file://fake/uri", + targetUri = "file:///fake/uri", targetSelectionRange = { start = { line = 0, character = 4 }, ["end"] = { line = 0, character = 4 }, @@ -1937,6 +2022,83 @@ describe('LSP', function() end) end) + describe('lsp.util.make_floating_popup_options', function() + before_each(function() + exec_lua [[ + local bufnr = vim.uri_to_bufnr("file:///fake/uri") + local winheight = vim.fn.winheight(0) + for i = 1, winheight do + vim.api.nvim_buf_set_lines(bufnr, 0, 0, false, {''}) + end + vim.api.nvim_win_set_buf(0, bufnr) + vim.api.nvim_win_set_cursor(0, {winheight, 0}) + ]] + end) + + local function popup_row(opts) + return exec_lua([[ + return vim.lsp.util.make_floating_popup_options(...).row + ]], 2, 2, opts) + end + + local err_pattern = "^Error executing lua: %.%.%./util%.lua:0: invalid floating preview border: .*%. :help vim%.api%.nvim_open_win%(%)$" + + it('calculates default border height correctly', function() + eq(0, popup_row()) + end) + + it('calculates string border height correctly', function() + eq(0, popup_row({border = 'none'})) + eq(-2, popup_row({border = 'single'})) + eq(-2, popup_row({border = 'double'})) + eq(-2, popup_row({border = 'rounded'})) + eq(-2, popup_row({border = 'solid'})) + eq(-1, popup_row({border = 'shadow'})) + end) + + it('error on invalid string border', function() + matches(err_pattern, pcall_err(popup_row, {border = ''})) + matches(err_pattern, pcall_err(popup_row, {border = 'invalid'})) + end) + + it('error on invalid array border length', function() + matches(err_pattern, pcall_err(popup_row, {border = {}})) + matches(err_pattern, pcall_err(popup_row, {border = {'', '', ''}})) + matches(err_pattern, pcall_err(popup_row, {border = {'', '', '', '', ''}})) + end) + + it('error on invalid array border member type', function() + matches(err_pattern, pcall_err(popup_row, {border = {0}})) + end) + + it('calculates 8-array border height correctly', function() + eq(0, popup_row({border = {'', '', '', '', '', '', '', ''}})) + eq(-2, popup_row({border = {'', '~', '', '~', '', '~', '', '~'}})) + eq(-1, popup_row({border = {'', '', '', '~', '', '~', '', ''}})) + eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}}})) + eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}, '', ''}})) + end) + + it('calculates 4-array border height correctly', function() + eq(0, popup_row({border = {'', '', '', ''}})) + eq(-2, popup_row({border = {'', '~', '', '~'}})) + eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}}})) + eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', ''}})) + end) + + it('calculates 2-array border height correctly', function() + eq(0, popup_row({border = {'', ''}})) + eq(-2, popup_row({border = {'', '~'}})) + eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}}})) + end) + + it('calculates 1-array border height correctly', function() + eq(0, popup_row({border = {''}})) + eq(-2, popup_row({border = {'~'}})) + eq(-2, popup_row({border = {{'~', 'NormalFloat'}}})) + end) + end) + describe('lsp.util._make_floating_popup_size', function() before_each(function() exec_lua [[ contents = @@ -1955,6 +2117,12 @@ describe('LSP', function() end) end) + describe('lsp.util.trim.trim_empty_lines', function() + it('properly trims empty lines', function() + eq({{"foo", "bar"}}, exec_lua[[ return vim.lsp.util.trim_empty_lines({{ "foo", "bar" }, nil}) ]]) + end) + end) + describe('lsp.util.get_effective_tabstop', function() local function test_tabstop(tabsize, softtabstop) exec_lua(string.format([[ @@ -1973,7 +2141,7 @@ describe('LSP', function() describe('vim.lsp.buf.outgoing_calls', function() it('does nothing for an empty response', function() local qflist_count = exec_lua([=[ - require'vim.lsp.handlers'['callHierarchy/outgoingCalls']() + require'vim.lsp.handlers'['callHierarchy/outgoingCalls'](nil, nil, {}, nil) return #vim.fn.getqflist() ]=]) eq(0, qflist_count) @@ -2020,14 +2188,16 @@ describe('LSP', function() } } } local handler = require'vim.lsp.handlers'['callHierarchy/outgoingCalls'] - handler(nil, nil, rust_analyzer_response) + handler(nil, rust_analyzer_response, {}) return vim.fn.getqflist() ]=]) local expected = { { bufnr = 2, col = 5, + end_col = 0, lnum = 4, + end_lnum = 0, module = "", nr = 0, pattern = "", @@ -2044,7 +2214,7 @@ describe('LSP', function() describe('vim.lsp.buf.incoming_calls', function() it('does nothing for an empty response', function() local qflist_count = exec_lua([=[ - require'vim.lsp.handlers'['callHierarchy/incomingCalls']() + require'vim.lsp.handlers'['callHierarchy/incomingCalls'](nil, nil, {}) return #vim.fn.getqflist() ]=]) eq(0, qflist_count) @@ -2092,14 +2262,16 @@ describe('LSP', function() } } local handler = require'vim.lsp.handlers'['callHierarchy/incomingCalls'] - handler(nil, nil, rust_analyzer_response) + handler(nil, rust_analyzer_response, {}) return vim.fn.getqflist() ]=]) local expected = { { bufnr = 2, col = 5, + end_col = 0, lnum = 4, + end_lnum = 0, module = "", nr = 0, pattern = "", @@ -2112,4 +2284,149 @@ describe('LSP', function() eq(expected, qflist) end) end) + + describe('vim.lsp.buf.rename', function() + for _, test in ipairs({ + { + it = "does not attempt to rename on nil response", + name = "prepare_rename_nil", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + }, + { + it = "handles prepareRename placeholder response", + name = "prepare_rename_placeholder", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + expected_text = "placeholder", -- see fake lsp response + }, + { + it = "handles range response", + name = "prepare_rename_range", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + expected_text = "line", -- see test case and fake lsp response + }, + { + it = "handles error", + name = "prepare_rename_error", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + expected_text = "two", -- see test case + }, + }) do + it(test.it, function() + local client + test_rpc_server { + test_name = test.name; + on_init = function(_client) + client = _client + eq(true, client.resolved_capabilities().rename) + end; + on_setup = function() + exec_lua([=[ + local bufnr = vim.api.nvim_get_current_buf() + lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID) + vim.lsp._stubs = {} + vim.fn.input = function(prompt, text) + vim.lsp._stubs.input_prompt = prompt + vim.lsp._stubs.input_text = text + return 'renameto' -- expect this value in fake lsp + end + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'', 'this is line two'}) + vim.fn.cursor(2, 13) -- the space between "line" and "two" + ]=]) + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_handler = function(err, result, ctx) + -- Don't compare & assert params, they're not relevant for the testcase + -- This allows us to be lazy and avoid declaring them + ctx.params = nil + + eq(table.remove(test.expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'start' then + exec_lua("vim.lsp.buf.rename()") + end + if ctx.method == 'shutdown' then + if test.expected_text then + eq("New Name: ", exec_lua("return vim.lsp._stubs.input_prompt")) + eq(test.expected_text, exec_lua("return vim.lsp._stubs.input_text")) + end + client.stop() + end + end; + } + end) + end + end) + + describe('vim.lsp.buf.code_action', function() + it('Calls client side command if available', function() + local client + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; + } + test_rpc_server { + test_name = 'code_action_with_resolve', + on_init = function(client_) + client = client_ + end, + on_setup = function() + end, + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end, + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}) + if ctx.method == 'start' then + exec_lua([[ + vim.lsp.commands['dummy1'] = function(cmd) + vim.lsp.commands['dummy2'] = function() + end + end + local bufnr = vim.api.nvim_get_current_buf() + vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID) + vim.fn.inputlist = function() + return 1 + end + vim.lsp.buf.code_action() + ]]) + elseif ctx.method == 'shutdown' then + eq('function', exec_lua[[return type(vim.lsp.commands['dummy2'])]]) + client.stop() + end + end + } + end) + end) + describe('vim.lsp.commands', function() + it('Accepts only string keys', function() + matches( + '.*The key for commands in `vim.lsp.commands` must be a string', + pcall_err(exec_lua, 'vim.lsp.commands[1] = function() end') + ) + end) + it('Accepts only function values', function() + matches( + '.*Command added to `vim.lsp.commands` must be a function', + pcall_err(exec_lua, 'vim.lsp.commands.dummy = 10') + ) + end) + end) end) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 5663f248bf..a4d78682ad 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -2161,6 +2161,12 @@ describe('plugin/shada.vim', function() reset() wshada('\004\000\009\147\000\196\002ab\196\001a') wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + + -- Need to set nohidden so that the buffer containing 'fname' is not unloaded + -- after loading 'fname_tmp', otherwise the '++opt not supported' test below + -- won't work since the BufReadCmd autocmd won't be triggered. + nvim_command('set nohidden') + nvim_command('edit ' .. fname) eq({ 'History entry with timestamp ' .. epoch .. ':', diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index e5e21f11a6..986db96a18 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -653,14 +653,12 @@ describe('clipboard (with fake clipboard.vim)', function() '', '', 'E121: Undefined variable: doesnotexist', - 'E15: Invalid expression: doesnotexist', }, 'v'}, eval("g:test_clip['*']")) feed_command(':echo "Howdy!"') eq({{ '', '', 'E121: Undefined variable: doesnotexist', - 'E15: Invalid expression: doesnotexist', '', 'Howdy!', }, 'v'}, eval("g:test_clip['*']")) diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index d254edc7d5..d100db8de2 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local eval, command, feed = helpers.eval, helpers.command, helpers.feed local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert local expect, write_file = helpers.expect, helpers.write_file @@ -116,6 +117,6 @@ describe('python3 provider', function() feed_command("exe 'split' tempname()") feed_command("bwipeout!") feed_command('help help') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index 2729d8dfa2..fba96100fc 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -1,10 +1,10 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear = helpers.clear 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 @@ -107,7 +107,7 @@ describe('ruby provider', function() helpers.add_builddir_to_rtp() command([=[autocmd BufDelete * ruby VIM::evaluate('expand("<afile>")')]=]) feed_command('help help') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/shada/errors_spec.lua b/test/functional/shada/errors_spec.lua index 77a41caec7..ebfd73cf85 100644 --- a/test/functional/shada/errors_spec.lua +++ b/test/functional/shada/errors_spec.lua @@ -342,6 +342,11 @@ describe('ShaDa error handling', function() eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 has wrong variable name type', exc_exec(sdrcmd())) end) + it('fails on variable item with BIN value and type value != VAR_TYPE_BLOB', function() + wshada('\006\000\007\147\196\001\065\196\000\000') + eq('Vim(rshada):E575: Error while reading ShaDa file: variable entry at position 0 has wrong variable type', exc_exec(sdrcmd())) + end) + it('fails on replacement item with NIL value', function() wshada('\003\000\001\192') eq('Vim(rshada):E575: Error while reading ShaDa file: sub string entry at position 0 is not an array', exc_exec(sdrcmd())) diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua index 9291f5e100..84cc34c7cc 100644 --- a/test/functional/shada/history_spec.lua +++ b/test/functional/shada/history_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local nvim_command, funcs, meths, nvim_feed, eq = helpers.command, helpers.funcs, helpers.meths, helpers.feed, helpers.eq -local eval = helpers.eval +local assert_alive = helpers.assert_alive local shada_helpers = require('test.functional.shada.helpers') local reset, clear = shada_helpers.reset, shada_helpers.clear @@ -244,7 +244,7 @@ describe('ShaDa support code', function() nvim_command('wshada') nvim_command('set shada=\'10,:0') nvim_command('wshada') - eq(2, eval('1+1')) -- check nvim still running + assert_alive() end) end) diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index e319fd9e6b..153a1c346f 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local meths, curwinmeths, curbufmeths, nvim_command, funcs, eq = helpers.meths, helpers.curwinmeths, helpers.curbufmeths, helpers.command, helpers.funcs, helpers.eq -local exc_exec, redir_exec = helpers.exc_exec, helpers.redir_exec +local exc_exec, exec_capture = helpers.exc_exec, helpers.exec_capture local shada_helpers = require('test.functional.shada.helpers') local reset, clear = shada_helpers.reset, shada_helpers.clear @@ -144,10 +144,10 @@ describe('ShaDa support code', function() nvim_command('normal! gg') nvim_command('enew') nvim_command('normal! gg') - local saved = redir_exec('jumps') + local saved = exec_capture('jumps') nvim_command('qall') reset() - eq(saved, redir_exec('jumps')) + eq(saved, exec_capture('jumps')) end) it('when dumping jump list also dumps current position', function() diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 22f2b8348d..2d44b0a950 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local nvim_command, funcs, curbufmeths, eq = helpers.command, helpers.funcs, helpers.curbufmeths, helpers.eq -local exc_exec, redir_exec = helpers.exc_exec, helpers.redir_exec +local exc_exec, exec_capture = helpers.exc_exec, helpers.exec_capture local shada_helpers = require('test.functional.shada.helpers') local reset, clear, get_shada_rw = @@ -910,14 +910,13 @@ describe('ShaDa jumps support code', function() .. '\008\007\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'f\161l\002') eq(0, exc_exec(sdrcmd())) eq('', curbufmeths.get_name()) - eq('\n' - .. ' jump line col file/text\n' + eq( ' jump line col file/text\n' .. ' 5 2 0 ' .. mock_file_path .. 'c\n' .. ' 4 2 0 ' .. mock_file_path .. 'd\n' .. ' 3 3 0 ' .. mock_file_path .. 'd\n' .. ' 2 2 0 ' .. mock_file_path .. 'e\n' .. ' 1 2 0 ' .. mock_file_path .. 'f\n' - .. '>', redir_exec('jumps')) + .. '>', exec_capture('jumps')) end) it('merges jumps when writing', function() @@ -1001,14 +1000,13 @@ describe('ShaDa changes support code', function() .. '\011\004\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161l\005' .. '\011\008\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161l\004') eq(0, exc_exec(sdrcmd())) - eq('\n' - .. 'change line col text\n' + eq( 'change line col text\n' .. ' 5 1 0 0\n' .. ' 4 2 0 1\n' .. ' 3 5 0 4\n' .. ' 2 3 0 2\n' .. ' 1 4 0 3\n' - .. '>', redir_exec('changes')) + .. '>', exec_capture('changes')) end) it('merges changes when writing', function() diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua index cc0e7fa537..854add1363 100644 --- a/test/functional/shada/variables_spec.lua +++ b/test/functional/shada/variables_spec.lua @@ -1,7 +1,7 @@ -- ShaDa variables saving/reading support local helpers = require('test.functional.helpers')(after_each) -local meths, funcs, nvim_command, eq = - helpers.meths, helpers.funcs, helpers.command, helpers.eq +local meths, funcs, nvim_command, eq, eval = + helpers.meths, helpers.funcs, helpers.command, helpers.eq, helpers.eval local shada_helpers = require('test.functional.shada.helpers') local reset, clear = shada_helpers.reset, shada_helpers.clear @@ -30,10 +30,12 @@ describe('ShaDa support code', function() else meths.set_var(varname, varval) end + local vartype = eval('type(g:' .. varname .. ')') -- Exit during `reset` is not a regular exit: it does not write shada -- automatically nvim_command('qall') reset('set shada+=!') + eq(vartype, eval('type(g:' .. varname .. ')')) eq(varval, meths.get_var(varname)) end) end @@ -47,6 +49,8 @@ describe('ShaDa support code', function() autotest('false', 'FALSEVAR', false) autotest('null', 'NULLVAR', 'v:null', true) autotest('ext', 'EXTVAR', '{"_TYPE": v:msgpack_types.ext, "_VAL": [2, ["", ""]]}', true) + autotest('blob', 'BLOBVAR', '0z12ab34cd', true) + autotest('blob (with NULs)', 'BLOBVARNULS', '0z004e554c7300', true) it('does not read back variables without `!` in &shada', function() meths.set_var('STRVAR', 'foo') diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index c61bf108cb..103ae59b8e 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') +local assert_alive = helpers.assert_alive local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local poke_eventloop = helpers.poke_eventloop local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source @@ -7,6 +8,7 @@ local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file local command= helpers.command local exc_exec = helpers.exc_exec +local matches = helpers.matches describe(':terminal buffer', function() local screen @@ -255,8 +257,23 @@ describe(':terminal buffer', function() command('bdelete!') end) - it('handles wqall', function() + it('requires bang (!) to close a running job #15402', function() eq('Vim(wqall):E948: Job still running', exc_exec('wqall')) + for _, cmd in ipairs({ 'bdelete', '%bdelete', 'bwipeout', 'bunload' }) do + matches('^Vim%('..cmd:gsub('%%', '')..'%):E89: term://.*tty%-test.* will be killed %(add %! to override%)$', + exc_exec(cmd)) + end + command('call jobstop(&channel)') + assert(0 >= eval('jobwait([&channel], 1000)[0]')) + command('bdelete') + end) + + it('stops running jobs with :quit', function() + -- Open in a new window to avoid terminating the nvim instance + command('split') + command('terminal') + command('set nohidden') + command('quit') end) it('does not segfault when pasting empty buffer #13955', function() @@ -284,7 +301,7 @@ describe('No heap-buffer-overflow when using', function() feed('$') -- Let termopen() modify the buffer feed_command('call termopen("echo")') - eq(2, eval('1+1')) -- check nvim still running + assert_alive() feed_command('bdelete!') end) end) @@ -294,6 +311,6 @@ describe('No heap-buffer-overflow when', function() feed_command('set nowrap') feed_command('autocmd TermOpen * startinsert') feed_command('call feedkeys("4000ai\\<esc>:terminal!\\<cr>")') - eq(2, eval('1+1')) + assert_alive() end) end) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 4b512605e1..707c355069 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive 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 @@ -215,7 +216,7 @@ describe(':terminal (with fake shell)', function() -- handler), :terminal cleanup is pending on the main-loop. -- This write should be ignored (not crash, #5445). feed('iiYYYYYYY') - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('works with findfile()', function() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 8a5dd7ef18..f7520b14d4 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -8,12 +8,12 @@ local helpers = require('test.functional.helpers')(after_each) local uname = helpers.uname local thelpers = require('test.functional.terminal.helpers') local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local eq = helpers.eq local feed_command = helpers.feed_command local feed_data = thelpers.feed_data local clear = helpers.clear local command = helpers.command -local eval = helpers.eval local nvim_dir = helpers.nvim_dir local retry = helpers.retry local nvim_prog = helpers.nvim_prog @@ -82,7 +82,7 @@ describe('TUI', function() command('call jobresize(b:terminal_job_id, 1, 4)') screen:try_resize(57, 17) command('call jobresize(b:terminal_job_id, 57, 17)') - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('accepts resize while pager is active', function() diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index 03bd336aec..188afa1e84 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') +local assert_alive = helpers.assert_alive local clear = helpers.clear local feed, nvim = helpers.feed, helpers.nvim local feed_command = helpers.feed_command @@ -33,7 +34,7 @@ describe(':terminal', function() command('vsplit foo') eq(3, eval("winnr('$')")) feed('ZQ') -- Close split, should not crash. #7538 - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('does not change size on WinEnter', function() diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 05e0c5fe2c..175525b3f2 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -570,4 +570,47 @@ describe('treesitter highlighting', function() ]]} screen:expect{ unchanged=true } end) + + it("supports highlighting with priority", function() + if pending_c_parser(pending) then return end + + insert([[ + int x = INT_MAX; + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + #define foo void main() { \ + return 42; \ + } + ]]) + + exec_lua [[ + local parser = vim.treesitter.get_parser(0, "c") + test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query..'\n((translation_unit) @Error (set! "priority" 101))\n'}}) + ]] + -- expect everything to have Error highlight + screen:expect{grid=[[ + {12:int}{8: x = INT_MAX;} | + {8:#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))}| + {8:#define foo void main() { \} | + {8: return 42; \} | + {8: }} | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], attr_ids={ + [1] = {bold = true, foreground = Screen.colors.Blue1}; + [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}; + -- bold will not be overwritten at the moment + [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Grey100}; + }} + end) end) diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua new file mode 100644 index 0000000000..21c287644e --- /dev/null +++ b/test/functional/treesitter/node_spec.lua @@ -0,0 +1,62 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local insert = helpers.insert +local pending_c_parser = helpers.pending_c_parser + +before_each(clear) + +local function lua_eval(lua_expr) + return exec_lua("return " .. lua_expr) +end + +describe('treesitter node API', function() + clear() + + if pending_c_parser(pending) then + return + end + + it('can move between siblings', function() + insert([[ + int main(int x, int y, int z) { + return x + y * z + } + ]]) + + exec_lua([[ + query = require"vim.treesitter.query" + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + root = tree:root() + lang = vim.treesitter.inspect_language('c') + + function node_text(node) + return query.get_node_text(node, 0) + end + ]]) + + exec_lua 'node = root:descendant_for_range(0, 11, 0, 16)' + eq('int x', lua_eval('node_text(node)')) + + exec_lua 'node = node:next_sibling()' + eq(',', lua_eval('node_text(node)')) + + exec_lua 'node = node:next_sibling()' + eq('int y', lua_eval('node_text(node)')) + + exec_lua 'node = node:prev_sibling()' + eq(',', lua_eval('node_text(node)')) + + exec_lua 'node = node:prev_sibling()' + eq('int x', lua_eval('node_text(node)')) + + exec_lua 'node = node:next_named_sibling()' + eq('int y', lua_eval('node_text(node)')) + + exec_lua 'node = node:prev_named_sibling()' + eq('int x', lua_eval('node_text(node)')) + end) +end) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index d2f9148e8f..ffaa4141c4 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -10,9 +10,11 @@ local pending_c_parser = helpers.pending_c_parser before_each(clear) describe('treesitter parser API', function() + clear() + if pending_c_parser(pending) then return end it('parses buffer', function() - if helpers.pending_win32(pending) or pending_c_parser(pending) then return end + if helpers.pending_win32(pending) then return end insert([[ int main() { @@ -103,8 +105,6 @@ void ui_refresh(void) }]] it('allows to iterate over nodes children', function() - if pending_c_parser(pending) then return end - insert(test_text); local res = exec_lua([[ @@ -127,8 +127,6 @@ void ui_refresh(void) end) it('allows to get a child by field', function() - if pending_c_parser(pending) then return end - insert(test_text); local res = exec_lua([[ @@ -162,8 +160,6 @@ void ui_refresh(void) ]] it("supports runtime queries", function() - if pending_c_parser(pending) then return end - local ret = exec_lua [[ return require"vim.treesitter.query".get_query("c", "highlights").captures[1] ]] @@ -172,8 +168,6 @@ void ui_refresh(void) end) it('support query and iter by capture', function() - if pending_c_parser(pending) then return end - insert(test_text) local res = exec_lua([[ @@ -203,8 +197,6 @@ void ui_refresh(void) end) it('support query and iter by match', function() - if pending_c_parser(pending) then return end - insert(test_text) local res = exec_lua([[ @@ -236,8 +228,6 @@ void ui_refresh(void) end) it('can match special regex characters like \\ * + ( with `vim-match?`', function() - if pending_c_parser(pending) then return end - insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') local res = exec_lua([[ @@ -271,8 +261,6 @@ void ui_refresh(void) end) it('supports builtin query predicate any-of?', function() - if pending_c_parser(pending) then return end - insert([[ #include <stdio.h> @@ -330,8 +318,6 @@ void ui_refresh(void) end) it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() - if pending_c_parser(pending) then return end - insert('char* astring = "Hello World!";') local res = exec_lua([[ @@ -407,8 +393,6 @@ void ui_refresh(void) it('allows to set simple ranges', function() - if pending_c_parser(pending) then return end - insert(test_text) local res = exec_lua [[ @@ -450,8 +434,6 @@ void ui_refresh(void) eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) end) it("allows to set complex ranges", function() - if pending_c_parser() then return end - insert(test_text) local res = exec_lua [[ @@ -646,6 +628,19 @@ int x = INT_MAX; {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) }, get_ranges()) end) + it("should list all directives", function() + local res_list = exec_lua[[ + local query = require'vim.treesitter.query' + + local list = query.list_directives() + + table.sort(list) + + return list + ]] + + eq({ 'offset!', 'set!' }, res_list) + end) end) end) diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index af709cd521..16ed3b9486 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -858,8 +858,8 @@ describe('Buffer highlighting', function() it('works with cursorline', function() command("set cursorline") - screen:expect([[ - {14:^1 + 2 }{15:=}{16: 3}{14: }| + screen:expect{grid=[[ + {14:^1 + 2 }{3:=}{2: 3}{14: }| 3 + {11:ERROR:} invalid syntax | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| @@ -867,32 +867,32 @@ describe('Buffer highlighting', function() {1:~ }| {1:~ }| | - ]]) + ]]} feed('j') - screen:expect([[ + screen:expect{grid=[[ 1 + 2 {3:=}{2: 3} | - {14:^3 + }{11:ERROR:}{14: invalid syntax }| + {14:^3 + }{11:ERROR:} invalid syntax{14: }| 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| x = 4 | {1:~ }| {1:~ }| | - ]]) + ]]} feed('j') - screen:expect([[ + screen:expect{grid=[[ 1 + 2 {3:=}{2: 3} | 3 + {11:ERROR:} invalid syntax | {14:^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}| - {14:, 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s}| + {14:, 5, 5, 5, 5, 5, 5, }Lorem ipsum dolor s| x = 4 | {1:~ }| {1:~ }| | - ]]) + ]]} end) it('works with color column', function() @@ -910,11 +910,11 @@ describe('Buffer highlighting', function() command("set colorcolumn=9") screen:expect{grid=[[ - ^1 + 2 {3:=}{2: }{17:3} | + ^1 + 2 {3:=}{2: 3} | 3 + {11:ERROR:} invalid syntax | 5, 5, 5,{18: }5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| - x = 4 {12:暗}{19:x}{12:事} | + x = 4 {12:暗x事} | {1:~ }| {1:~ }| | diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 0ea8bab957..ad23402ff9 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -14,6 +14,8 @@ local function new_screen(opt) [3] = {bold = true, reverse = true}, [4] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [5] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [6] = {foreground = Screen.colors.Magenta}, + [7] = {bold = true, foreground = Screen.colors.Brown}, }) return screen end @@ -267,7 +269,7 @@ local function test_cmdline(linegrid) special = {'"', true}, }, { firstc = "=", - content = {{"1"}, {"+"}, {"2"}}, + content = {{"1", 6}, {"+", 7}, {"2", 6}}, pos = 3, }} diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index f75f700fb5..9c035c728b 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -212,10 +212,10 @@ describe('ui/cursor', function() if m.blinkwait then m.blinkwait = 700 end end if m.hl_id then - m.hl_id = 56 + m.hl_id = 58 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 57 end + if m.id_lm then m.id_lm = 59 end end -- Assert the new expectation. diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 98aafd8757..8074f91215 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -562,7 +562,7 @@ end]] {5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count = unpack(item) | {5:i}{12:c}{11:ombining color} {13:nil} {5:then} | {11:replacing color}d_cell | - {5:e}{8:bl}{14:endy}{15:i}{14:text}{15:o}{14:-}{15:o}{14:h}{7:ere} | + {5:e}{8:bl}{7:endy}{10: }{7:text}{10: }{7:-}{10: }{7:here} | {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} | {11:replacing color} line[colpos] | cell.text = text | @@ -697,4 +697,604 @@ end]] | ]]} end) + + it('can have virtual text which combines foreground and backround groups', function() + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {background = tonumber('0x123456'), foreground = tonumber('0xbbbbbb')}; + [3] = {background = tonumber('0x123456'), foreground = tonumber('0xcccccc')}; + [4] = {background = tonumber('0x234567'), foreground = tonumber('0xbbbbbb')}; + [5] = {background = tonumber('0x234567'), foreground = tonumber('0xcccccc')}; + [6] = {bold = true, foreground = tonumber('0xcccccc'), background = tonumber('0x234567')}; + } + + exec [[ + hi BgOne guibg=#123456 + hi BgTwo guibg=#234567 + hi FgEin guifg=#bbbbbb + hi FgZwei guifg=#cccccc + hi VeryBold gui=bold + ]] + + meths.buf_set_extmark(0, ns, 0, 0, { virt_text={ + {'a', {'BgOne', 'FgEin'}}; + {'b', {'BgOne', 'FgZwei'}}; + {'c', {'BgTwo', 'FgEin'}}; + {'d', {'BgTwo', 'FgZwei'}}; + {'X', {'BgTwo', 'FgZwei', 'VeryBold'}}; + }}) + + screen:expect{grid=[[ + ^ {2:a}{3:b}{4:c}{5:d}{6:X} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('does not crash when deleting a cleared buffer #15212', function() + exec_lua [[ + ns = vim.api.nvim_create_namespace("myplugin") + vim.api.nvim_buf_set_extmark(0, ns, 0, 0, {virt_text = {{"a"}}, end_col = 0}) + ]] + screen:expect{grid=[[ + ^ a | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + exec_lua [[ + vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) + vim.cmd("bdelete") + ]] + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + helpers.assert_alive() + end) +end) + +describe('decorations: virtual lines', function() + local screen, ns + before_each(function() + clear() + screen = Screen.new(50, 12) + screen:attach() + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {foreground = Screen.colors.Cyan4}; + [3] = {background = Screen.colors.Yellow1}; + [4] = {bold = true}; + [5] = {background = Screen.colors.Yellow, foreground = Screen.colors.Blue}; + [6] = {foreground = Screen.colors.Blue}; + [7] = {foreground = Screen.colors.SlateBlue}; + [8] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue}; + [9] = {foreground = Screen.colors.Brown}; + } + + ns = meths.create_namespace 'test' + end) + + local example_text = [[ +if (h->n_buckets < new_n_buckets) { // expand + khkey_t *new_keys = (khkey_t *)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); + h->keys = new_keys; + if (kh_is_map && val_size) { + char *new_vals = krealloc( h->vals_buf, new_n_buckets * val_size); + h->vals_buf = new_vals; + } +}]] + + it('works with one line', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 33, { + virt_lines={ {{">> ", "NonText"}, {"krealloc", "Identifier"}, {": change the size of an allocation"}}}; + virt_lines_above=true; + }) + + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + {1:>> }{2:krealloc}: change the size of an allocation | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + feed '/krealloc<cr>' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + {1:>> }{2:krealloc}: change the size of an allocation | + khkey_t *new_keys = (khkey_t *){3:^krealloc}((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + /krealloc | + ]]} + + -- virtual line remains anchored to the extmark + feed 'i<cr>' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {1:>> }{2:krealloc}: change the size of an allocation | + {3:^krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + {4:-- INSERT --} | + ]]} + + feed '<esc>3+' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {1:>> }{2:krealloc}: change the size of an allocation | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + | + ]]} + + meths.buf_set_extmark(0, ns, 5, 0, { + virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unneccesary?", "Comment"}} }; + }) + -- TODO: what about the cursor?? + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buck^ets * val_size); | + {5:^^ REVIEW:}{6: new_vals variable seems unneccesary?} | + h->vals_buf = new_vals; | + } | + | + ]]} + + meths.buf_clear_namespace(0, ns, 0, -1) + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buck^ets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + end) + + + it('works with text at the beginning of the buffer', function() + insert(example_text) + feed 'gg' + + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + {1:~ }| + | + ]]} + + meths.buf_set_extmark(0, ns, 0, 0, { + virt_lines={ + {{"refactor(khash): ", "Special"}, {"take size of values as parameter"}}; + {{"Author: Dev Devsson, "}, {"Tue Aug 31 10:13:37 2021", "Comment"}}; + }; + virt_lines_above=true; + right_gravity=false; + }) + + -- placing virt_text on topline does not automatically cause a scroll + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + {1:~ }| + | + ]], unchanged=true} + + feed '<c-b>' + screen:expect{grid=[[ + {7:refactor(khash): }take size of values as parameter | + Author: Dev Devsson, {6:Tue Aug 31 10:13:37 2021} | + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + | + ]]} + end) + + it('works with text et the end of the buffer', function() + insert(example_text) + feed 'G' + + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + {1:~ }| + | + ]]} + + local id = meths.buf_set_extmark(0, ns, 7, 0, { + virt_lines={{{"Grugg"}}}; + right_gravity=false; + }) + + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + Grugg | + | + ]]} + + meths.buf_del_extmark(0, ns, id) + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + {1:~ }| + | + ]]} + end) + + it('works with a block scrolling up', function() + screen:try_resize(30, 7) + insert("aa\nbb\ncc\ndd\nee\nff\ngg\nhh") + feed 'gg' + + meths.buf_set_extmark(0, ns, 6, 0, { + virt_lines={ + {{"they see me"}}; + {{"scrolling", "Special"}}; + {{"they"}}; + {{"hatin'", "Special"}}; + }; + }) + + screen:expect{grid=[[ + ^aa | + bb | + cc | + dd | + ee | + ff | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^bb | + cc | + dd | + ee | + ff | + gg | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^cc | + dd | + ee | + ff | + gg | + they see me | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^dd | + ee | + ff | + gg | + they see me | + {7:scrolling} | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^ee | + ff | + gg | + they see me | + {7:scrolling} | + they | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^ff | + gg | + they see me | + {7:scrolling} | + they | + {7:hatin'} | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^gg | + they see me | + {7:scrolling} | + they | + {7:hatin'} | + hh | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + they see me | + {7:scrolling} | + they | + {7:hatin'} | + ^hh | + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + {7:scrolling} | + they | + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + they | + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('works with sign and numbercolumns', function() + insert(example_text) + feed 'gg' + command 'set number signcolumn=yes' + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + {8: }{9: 6 } h->vals_buf = new_vals; | + {8: }{9: 7 } } | + | + ]]} + + meths.buf_set_extmark(0, ns, 2, 0, { + virt_lines={ + {{"Some special", "Special"}}; + {{"remark about codes", "Comment"}}; + }; + }) + + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {8: }{9: }{7:Some special} | + {8: }{9: }{6:remark about codes} | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + | + ]]} + + meths.buf_set_extmark(0, ns, 2, 0, { + virt_lines={ + {{"Some special", "Special"}}; + {{"remark about codes", "Comment"}}; + }; + virt_lines_leftcol=true; + }) + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {7:Some special} | + {6:remark about codes} | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + | + ]]} + end) + + + it('works with hard tabs', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 0, { + virt_lines={ {{">>", "NonText"}, {"\tvery\ttabby", "Identifier"}, {"text\twith\ttabs"}}}; + }) + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + {1:>>}{2: very tabby}text with tabs | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + command 'set tabstop=4' + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + {1:>>}{2: very tabby}text with tabs | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + end) + end) diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index a8d9fb02fc..df750a1a68 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -1057,7 +1057,7 @@ it('diff updates line numbers below filler lines', function() vnew call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b']) windo diffthis - setlocal number rnu foldcolumn=0 + setlocal number rnu cursorline cursorlineopt=number foldcolumn=0 ]]) screen:expect([[ {1: }a {3:│}{10:1 }^a | @@ -1109,7 +1109,7 @@ it('diff updates line numbers below filler lines', function() {3:[No Name] [+] }{7:[No Name] [+] }| | ]]) - command("set signcolumn number tgc cursorline") + command("set signcolumn number tgc cursorline cursorlineopt=number,line") command("hi CursorLineNr guibg=red") screen:expect{grid=[[ {1: }a {3:│}{11: 2 }a | @@ -1128,3 +1128,72 @@ it('diff updates line numbers below filler lines', function() signcolumn=auto | ]]} end) + +it('Align the filler lines when changing text in diff mode', function() + clear() + local screen = Screen.new(40, 20) + screen:attach() + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray}; + [2] = {background = Screen.colors.LightCyan, foreground = Screen.colors.Blue1, bold = true}; + [3] = {reverse = true}; + [4] = {background = Screen.colors.LightBlue}; + [5] = {background = Screen.colors.LightMagenta}; + [6] = {background = Screen.colors.Red, bold = true}; + [7] = {foreground = Screen.colors.Blue1, bold = true}; + [8] = {reverse = true, bold = true}; + }) + source([[ + call setline(1, range(1, 15)) + vnew + call setline(1, range(9, 15)) + windo diffthis + wincmd h + exe "normal Gl5\<C-E>" + ]]) + screen:expect{grid=[[ + {1: }{2:------------------}{3:│}{1: }{4:6 }| + {1: }{2:------------------}{3:│}{1: }{4:7 }| + {1: }{2:------------------}{3:│}{1: }{4:8 }| + {1: }9 {3:│}{1: }9 | + {1: }10 {3:│}{1: }10 | + {1: }11 {3:│}{1: }11 | + {1: }12 {3:│}{1: }12 | + {1: }13 {3:│}{1: }13 | + {1: }14 {3:│}{1: }14 | + {1:- }1^5 {3:│}{1:- }15 | + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {8:[No Name] [+] }{3:[No Name] [+] }| + | + ]]} + feed('ax<Esc>') + screen:expect{grid=[[ + {1: }{2:------------------}{3:│}{1: }{4:6 }| + {1: }{2:------------------}{3:│}{1: }{4:7 }| + {1: }{2:------------------}{3:│}{1: }{4:8 }| + {1: }9 {3:│}{1: }9 | + {1: }10 {3:│}{1: }10 | + {1: }11 {3:│}{1: }11 | + {1: }12 {3:│}{1: }12 | + {1: }13 {3:│}{1: }13 | + {1: }14 {3:│}{1: }14 | + {1: }{5:15}{6:^x}{5: }{3:│}{1: }{5:15 }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {8:[No Name] [+] }{3:[No Name] [+] }| + | + ]]} +end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 61ed0a65b0..ccf5f963d1 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -74,7 +74,8 @@ describe('float window', function() funcs.win_execute(win, 'bwipe!') end) - it('win_execute() call commands that not allowed' , function() + it("win_execute() call commands that are not allowed when 'hidden' is not set" , function() + command('set nohidden') local buf = meths.create_buf(false, false) meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'}) local win = meths.open_win(buf, true, {relative='win', width=16, height=1, row=0, col=10}) @@ -152,6 +153,132 @@ describe('float window', function() eq(10, width) end) + it('opened with correct position', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local opts = { + width = 10, + height = 10, + col = 7, + row = 9, + relative = 'editor', + style = 'minimal' + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(9, pos[1]) + eq(7, pos[2]) + end) + + it('opened with correct position relative to the cursor', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local opts = { + width = 10, + height = 10, + col = 7, + row = 9, + relative = 'cursor', + style = 'minimal' + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(9, pos[1]) + eq(7, pos[2]) + end) + + it('opened with correct position relative to another window', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local par_opts = { + width = 50, + height = 50, + col = 7, + row = 9, + relative = 'editor', + style = 'minimal' + } + + local par_win_id = vim.api.nvim_open_win(bufnr, false, par_opts) + + local opts = { + width = 10, + height = 10, + col = 7, + row = 9, + relative = 'win', + style = 'minimal', + win = par_win_id + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(18, pos[1]) + eq(14, pos[2]) + end) + + + it('opened with correct position relative to another relative window', function() + local pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + + local root_opts = { + width = 50, + height = 50, + col = 7, + row = 9, + relative = 'editor', + style = 'minimal' + } + + local root_win_id = vim.api.nvim_open_win(bufnr, false, root_opts) + + local par_opts = { + width = 20, + height = 20, + col = 2, + row = 3, + relative = 'win', + win = root_win_id, + style = 'minimal' + } + + local par_win_id = vim.api.nvim_open_win(bufnr, false, par_opts) + + local opts = { + width = 10, + height = 10, + col = 3, + row = 2, + relative = 'win', + win = par_win_id, + style = 'minimal' + } + + local win_id = vim.api.nvim_open_win(bufnr, false, opts) + + return vim.api.nvim_win_get_position(win_id) + ]]) + + eq(14, pos[1]) + eq(12, pos[2]) + end) + + local function with_ext_multigrid(multigrid) local screen before_each(function() @@ -620,6 +747,134 @@ describe('float window', function() end end) + it("would not break 'minimal' style with signcolumn=auto:[min]-[max]", function() + command('set number') + command('set signcolumn=auto:1-3') + command('set colorcolumn=1') + command('set cursorline') + command('set foldcolumn=1') + command('hi NormalFloat guibg=#333333') + feed('ix<cr>y<cr><esc>gg') + local win = meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10, style='minimal'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {15:x }| + {15:y }| + {15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect{grid=[[ + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {15:x } | + {0:~ }{15:y }{0: }| + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]} + end + + command('sign define piet1 text=𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄ texthl=Search') + command('sign place 1 line=1 name=piet1 buffer=1') + -- signcolumn=auto:1-3 still works if there actually are signs + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x }| + {19: }{15:y }| + {19: }{15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + + else + screen:expect([[ + {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x } | + {0:~ }{19: }{15:y }{0: }| + {0:~ }{19: }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]) + end + command('sign unplace 1 buffer=1') + + local buf = meths.create_buf(false, true) + meths.win_set_buf(win, buf) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {15: }| + {15: }| + {15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect([[ + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {15: } | + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]) + end + end) + it('can have border', function() local buf = meths.create_buf(false, false) meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', @@ -653,8 +908,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -696,8 +951,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -739,8 +994,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -782,8 +1037,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -826,8 +1081,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -867,8 +1122,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -908,8 +1163,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -958,8 +1213,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 6, curline = 5, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 6, curline = 5, curcol = 0, linecount = 6}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -1009,8 +1264,8 @@ describe('float window', function() ]], float_pos={ [4] = { { id = 1001 }, "NW", 1, 0, 0, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -1067,8 +1322,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 0, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 0, linecount = 3}; }} else screen:expect{grid=[[ @@ -1126,8 +1381,8 @@ describe('float window', function() [5] = { { id = 1002 }, "NW", 1, 0, 5, true, 50 }, [6] = { { id = -1 }, "NW", 5, 4, 0, false, 100 } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3, linecount=3}; }} else screen:expect{grid=[[ @@ -1576,6 +1831,7 @@ describe('float window', function() botline = 3, curline = 0, curcol = 3, + linecount = 2, win = { id = 1000 } }, [4] = { @@ -1583,6 +1839,7 @@ describe('float window', function() botline = 3, curline = 0, curcol = 3, + linecount = 2, win = { id = 1001 } }, [5] = { @@ -1590,6 +1847,7 @@ describe('float window', function() botline = 2, curline = 0, curcol = 0, + linecount = 1, win = { id = 1002 } } }} @@ -4907,7 +5165,7 @@ describe('float window', function() ]]) end - eq(2, eval('1+1')) + assert_alive() end) it("o (:only) non-float", function() @@ -6210,8 +6468,8 @@ describe('float window', function() ]], float_pos={ [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -6267,10 +6525,10 @@ describe('float window', function() [5] = { { id = 1002 }, "NW", 1, 3, 8, true }; [6] = { { id = 1003 }, "NW", 1, 4, 10, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; }} else screen:expect{grid=[[ @@ -6315,8 +6573,8 @@ describe('float window', function() ]], float_pos={ [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -6372,10 +6630,10 @@ describe('float window', function() [5] = { { id = 1002 }, "NW", 1, 4, 10, true }; [6] = { { id = 1003 }, "NW", 1, 3, 8, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -6435,10 +6693,10 @@ describe('float window', function() [5] = {{id = 1002}, "NW", 1, 2, 6, true, 50}; [6] = {{id = 1003}, "NW", 1, 3, 7, true, 40}; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 8883ad8270..249686234c 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -85,7 +85,7 @@ describe("folded lines", function() end) it("highlighting with relative line numbers", function() - command("set relativenumber foldmethod=marker") + command("set relativenumber cursorline cursorlineopt=number foldmethod=marker") feed_command("set foldcolumn=2") funcs.setline(1, '{{{1') funcs.setline(2, 'line 1') diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 8992ee27ce..c00d30fe32 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -789,7 +789,7 @@ describe("'listchars' highlight", function() [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {background=Screen.colors.Grey90}, [2] = {foreground=Screen.colors.Red}, - [3] = {foreground=Screen.colors.Green1}, + [3] = {foreground=Screen.colors.X11Green, background=Screen.colors.Red1}, }) feed_command('highlight clear ModeMsg') feed_command('highlight Whitespace guifg=#FF0000') @@ -912,6 +912,97 @@ describe('CursorLine highlight', function() ]]) end) + it("'cursorlineopt' screenline", function() + local screen = Screen.new(20,5) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.Black, background = Screen.colors.White}; + [2] = {foreground = Screen.colors.Yellow}; + [3] = {foreground = Screen.colors.Red, background = Screen.colors.Green}; + [4] = {foreground = Screen.colors.Green, background = Screen.colors.Red}; + }) + screen:attach() + + feed_command('set wrap cursorline cursorlineopt=screenline') + feed_command('set showbreak=>>>') + feed_command('highlight clear NonText') + feed_command('highlight clear CursorLine') + feed_command('highlight NonText guifg=Yellow gui=NONE') + feed_command('highlight LineNr guifg=Red guibg=Green gui=NONE') + feed_command('highlight CursorLine guifg=Black guibg=White gui=NONE') + feed_command('highlight CursorLineNr guifg=Green guibg=Red gui=NONE') + + feed('30iø<esc>o<esc>30ia<esc>') + + -- CursorLine should not apply to 'showbreak' when 'cursorlineopt' contains "screenline" + screen:expect([[ + øøøøøøøøøøøøøøøøøøøø| + {2:>>>}øøøøøøøøøø | + aaaaaaaaaaaaaaaaaaaa| + {2:>>>}{1:aaaaaaaaa^a }| + | + ]]) + feed('gk') + screen:expect([[ + øøøøøøøøøøøøøøøøøøøø| + {2:>>>}øøøøøøøøøø | + {1:aaaaaaaaaaaa^aaaaaaaa}| + {2:>>>}aaaaaaaaaa | + | + ]]) + feed('k') + screen:expect([[ + {1:øøøøøøøøøøøø^øøøøøøøø}| + {2:>>>}øøøøøøøøøø | + aaaaaaaaaaaaaaaaaaaa| + {2:>>>}aaaaaaaaaa | + | + ]]) + + -- CursorLineNr should not apply to line number when 'cursorlineopt' does not contain "number" + feed_command('set relativenumber numberwidth=2') + screen:expect([[ + {3:0 }{1:øøøøøøøøøøøø^øøøøøø}| + {3: }{2:>>>}øøøøøøøøøøøø | + {3:1 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + + -- CursorLineNr should apply to line number when 'cursorlineopt' contains "number" + feed_command('set cursorlineopt+=number') + screen:expect([[ + {4:0 }{1:øøøøøøøøøøøø^øøøøøø}| + {3: }{2:>>>}øøøøøøøøøøøø | + {3:1 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + feed('gj') + screen:expect([[ + {4:0 }øøøøøøøøøøøøøøøøøø| + {3: }{2:>>>}{1:øøøøøøøøø^øøø }| + {3:1 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + feed('gj') + screen:expect([[ + {3:1 }øøøøøøøøøøøøøøøøøø| + {3: }{2:>>>}øøøøøøøøøøøø | + {4:0 }{1:aaaaaaaaaaaa^aaaaaa}| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + feed('gj') + screen:expect([[ + {3:1 }øøøøøøøøøøøøøøøøøø| + {3: }{2:>>>}øøøøøøøøøøøø | + {4:0 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}{1:aaaaaaaaa^aaa }| + | + ]]) + end) + it('always updated. vim-patch:8.1.0849', function() local screen = Screen.new(50,5) screen:set_default_attr_ids({ @@ -1201,6 +1292,75 @@ describe("MsgSeparator highlight and msgsep fillchar", function() end) end) +describe("'number' and 'relativenumber' highlight", function() + before_each(clear) + + it('LineNr, LineNrAbove and LineNrBelow', function() + local screen = Screen.new(20,10) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.Red}, + [2] = {foreground = Screen.colors.Blue}, + [3] = {foreground = Screen.colors.Green}, + }) + screen:attach() + command('set number relativenumber') + command('call setline(1, range(50))') + command('highlight LineNr guifg=Red') + feed('4j') + screen:expect([[ + {1: 4 }0 | + {1: 3 }1 | + {1: 2 }2 | + {1: 1 }3 | + {1:5 }^4 | + {1: 1 }5 | + {1: 2 }6 | + {1: 3 }7 | + {1: 4 }8 | + | + ]]) + command('highlight LineNrAbove guifg=Blue') + screen:expect([[ + {2: 4 }0 | + {2: 3 }1 | + {2: 2 }2 | + {2: 1 }3 | + {1:5 }^4 | + {1: 1 }5 | + {1: 2 }6 | + {1: 3 }7 | + {1: 4 }8 | + | + ]]) + command('highlight LineNrBelow guifg=Green') + screen:expect([[ + {2: 4 }0 | + {2: 3 }1 | + {2: 2 }2 | + {2: 1 }3 | + {1:5 }^4 | + {3: 1 }5 | + {3: 2 }6 | + {3: 3 }7 | + {3: 4 }8 | + | + ]]) + feed('3j') + screen:expect([[ + {2: 7 }0 | + {2: 6 }1 | + {2: 5 }2 | + {2: 4 }3 | + {2: 3 }4 | + {2: 2 }5 | + {2: 1 }6 | + {1:8 }^7 | + {3: 1 }8 | + | + ]]) + end) +end) + describe("'winhighlight' highlight", function() local screen diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 712c1f377a..b6e2f2311f 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -1487,6 +1487,29 @@ describe("inccommand=nosplit", function() ]]) eq(eval('v:null'), eval('v:exiting')) end) + + it("does not break bar-separated command #8796", function() + source([[ + function! F() + if v:false | return | endif + endfun + ]]) + command('call timer_start(10, {-> F()}, {"repeat":-1})') + feed(':%s/') + sleep(20) -- Allow some timer activity. + screen:expect([[ + Inc substitution on | + two lines | + Inc substitution on | + two lines | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :%s/^ | + ]]) + end) end) describe(":substitute, 'inccommand' with a failing expression", function() diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 7bca741ae3..d3fe38ef52 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1384,4 +1384,128 @@ describe('ui/mouse/input', function() end) -- level 3 - wrapped end) + + it('getmousepos works correctly', function() + local winwidth = meths.get_option('winwidth') + -- Set winwidth=1 so that window sizes don't change. + meths.set_option('winwidth', 1) + command('tabedit') + local tabpage = meths.get_current_tabpage() + insert('hello') + command('vsplit') + local opts = { + relative='editor', + width=12, + height=1, + col=8, + row=1, + anchor='NW', + style='minimal', + border='single', + focusable=1 + } + local float = meths.open_win(meths.get_current_buf(), false, opts) + command('redraw') + local lines = meths.get_option('lines') + local columns = meths.get_option('columns') + + -- Test that screenrow and screencol are set properly for all positions. + for row = 0, lines - 1 do + for col = 0, columns - 1 do + -- Skip the X button that would close the tab. + if row ~= 0 or col ~= columns - 1 then + meths.input_mouse('left', 'press', '', 0, row, col) + meths.set_current_tabpage(tabpage) + local mousepos = funcs.getmousepos() + eq(row + 1, mousepos.screenrow) + eq(col + 1, mousepos.screencol) + -- All other values should be 0 when clicking on the command line. + if row == lines - 1 then + eq(0, mousepos.winid) + eq(0, mousepos.winrow) + eq(0, mousepos.wincol) + eq(0, mousepos.line) + eq(0, mousepos.column) + end + end + end + end + + -- Test that mouse position values are properly set for the floating window + -- with a border. 1 is added to the height and width to account for the + -- border. + for win_row = 0, opts.height + 1 do + for win_col = 0, opts.width + 1 do + local row = win_row + opts.row + local col = win_col + opts.col + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(float.id, mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = 0 + local column = 0 + if win_row > 0 and win_row < opts.height + 1 + and win_col > 0 and win_col < opts.width + 1 then + -- Because of border, win_row and win_col don't need to be + -- incremented by 1. + line = math.min(win_row, funcs.line('$')) + column = math.min(win_col, #funcs.getline(line) + 1) + end + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + + -- Test that mouse position values are properly set for the floating + -- window, after removing the border. + opts.border = 'none' + meths.win_set_config(float, opts) + command('redraw') + for win_row = 0, opts.height - 1 do + for win_col = 0, opts.width - 1 do + local row = win_row + opts.row + local col = win_col + opts.col + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(float.id, mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = math.min(win_row + 1, funcs.line('$')) + local column = math.min(win_col + 1, #funcs.getline(line) + 1) + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + + -- Test that mouse position values are properly set for ordinary windows. + -- Set the float to be unfocusable instead of closing, to additionally test + -- that getmousepos does not consider unfocusable floats. (see discussion + -- in PR #14937 for details). + opts.focusable = false + meths.win_set_config(float, opts) + command('redraw') + for nr = 1, 2 do + for win_row = 0, funcs.winheight(nr) - 1 do + for win_col = 0, funcs.winwidth(nr) - 1 do + local row = win_row + funcs.win_screenpos(nr)[1] - 1 + local col = win_col + funcs.win_screenpos(nr)[2] - 1 + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(funcs.win_getid(nr), mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = math.min(win_row + 1, funcs.line('$')) + local column = math.min(win_col + 1, #funcs.getline(line) + 1) + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + end + + -- Restore state and release mouse. + command('tabclose!') + meths.set_option('winwidth', winwidth) + meths.input_mouse('left', 'release', '', 0, 0, 0) + end) end) diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index 719e2ee82a..4e5e9c3a71 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -34,6 +34,7 @@ describe('ext_multigrid', function() [17] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta}, [18] = {bold = true, foreground = Screen.colors.Magenta}, [19] = {foreground = Screen.colors.Brown}, + [20] = {background = Screen.colors.LightGrey}, }) end) @@ -2034,6 +2035,66 @@ describe('ext_multigrid', function() ]]} end) + it('supports mouse drag with mouse=a', function() + command('set mouse=a') + command('vsplit') + command('wincmd l') + command('split') + command('enew') + feed('ifoo\nbar<esc>') + + meths.input_mouse('left', 'press', '', 5, 0, 0) + poke_eventloop() + meths.input_mouse('left', 'drag', '', 5, 1, 2) + + screen:expect{grid=[[ + ## grid 1 + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}{11:[No Name] [+] }| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + {12:[No Name] [No Name] }| + [3:-----------------------------------------------------]| + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 + {20:foo} | + {20:ba}^r | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + end) + it('has viewport information', function() screen:try_resize(48, 8) screen:expect{grid=[[ @@ -2056,7 +2117,7 @@ describe('ext_multigrid', function() ## grid 3 | ]], win_viewport={ - [2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0} + [2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1} }} insert([[ Lorem ipsum dolor sit amet, consectetur @@ -2091,7 +2152,7 @@ describe('ext_multigrid', function() ## grid 3 | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7}, + [2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7, linecount = 11}, }} @@ -2116,7 +2177,7 @@ describe('ext_multigrid', function() ## grid 3 | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 2, botline = 9, curline = 7, curcol = 0}, + [2] = {win = {id = 1000}, topline = 2, botline = 9, curline = 7, curcol = 0, linecount = 11}, }} command("split") @@ -2140,8 +2201,8 @@ describe('ext_multigrid', function() reprehenderit in voluptate velit esse cillum | ^dolore eu fugiat nulla pariatur. Excepteur sint | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0}, - [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 7, curcol = 0}, + [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0, linecount = 11}, + [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 7, curcol = 0, linecount = 11}, }} feed("b") @@ -2165,8 +2226,8 @@ describe('ext_multigrid', function() reprehenderit in voluptate velit esse ^cillum | dolore eu fugiat nulla pariatur. Excepteur sint | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0}, - [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 6, curcol = 38}, + [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0, linecount = 11}, + [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 6, curcol = 38, linecount = 11}, }} feed("2k") @@ -2190,8 +2251,8 @@ describe('ext_multigrid', function() ea commodo consequat. Duis aute irure dolor in | reprehenderit in voluptate velit esse cillum | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0}, - [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38}, + [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0, linecount = 11}, + [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38, linecount = 11}, }} -- handles non-current window @@ -2216,8 +2277,96 @@ describe('ext_multigrid', function() ea commodo consequat. Duis aute irure dolor in | reprehenderit in voluptate velit esse cillum | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 10}, - [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38}, + [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 10, linecount = 11}, + [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38, linecount = 11}, + }} + end) + + it('does not crash when dragging mouse across grid boundary', function() + screen:try_resize(48, 8) + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + {11:[No Name] }| + [3:------------------------------------------------]| + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1} + }} + insert([[ + Lorem ipsum dolor sit amet, consectetur + adipisicing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. Excepteur sint + occaecat cupidatat non proident, sunt in culpa + qui officia deserunt mollit anim id est + laborum.]]) + + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + {11:[No Name] [+] }| + [3:------------------------------------------------]| + ## grid 2 + ea commodo consequat. Duis aute irure dolor in | + reprehenderit in voluptate velit esse cillum | + dolore eu fugiat nulla pariatur. Excepteur sint | + occaecat cupidatat non proident, sunt in culpa | + qui officia deserunt mollit anim id est | + laborum^. | + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7, linecount = 11}, + }} + + meths.input_mouse('left', 'press', '', 1,5, 1) + poke_eventloop() + meths.input_mouse('left', 'drag', '', 1, 6, 1) + + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + {11:[No Name] [+] }| + [3:------------------------------------------------]| + ## grid 2 + reprehenderit in voluptate velit esse cillum | + dolore eu fugiat nulla pariatur. Excepteur sint | + occaecat cupidatat non proident, sunt in culpa | + qui officia deserunt mollit anim id est | + l^aborum. | + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 6, botline = 12, curline = 10, curcol = 1, linecount = 11}, }} end) end) diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 3826707743..50e5dfac84 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -1,9 +1,9 @@ local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local child_session = require('test.functional.terminal.helpers') +local assert_alive = helpers.assert_alive local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir local eq = helpers.eq -local eval = helpers.eval local feed = helpers.feed local feed_command = helpers.feed_command local iswin = helpers.iswin @@ -86,12 +86,12 @@ describe("shell command :!", function() it("cat a binary file #4142", function() feed(":exe 'silent !cat '.shellescape(v:progpath)<CR>") - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it([[display \x08 char #4142]], function() feed(":silent !echo \08<CR>") - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it('handles control codes', function() diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 0944bfc21a..aeba049557 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local clear, feed = helpers.clear, helpers.feed local source = helpers.source local insert = helpers.insert @@ -9,7 +10,6 @@ local funcs = helpers.funcs local get_pathsep = helpers.get_pathsep local eq = helpers.eq local pcall_err = helpers.pcall_err -local eval = helpers.eval describe('ui/ext_popupmenu', function() local screen @@ -2211,6 +2211,6 @@ describe('builtin popupmenu', function() feed('$i') funcs.complete(col - max_len, items) feed('<c-y>') - eq(2, eval('1+1')) + assert_alive() end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index f73d051857..61f19c3794 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -773,13 +773,14 @@ function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) self.float_pos[grid] = nil end -function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol) +function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol, linecount) self.win_viewport[grid] = { win = win, topline = topline, botline = botline, curline = curline, - curcol = curcol + curcol = curcol, + linecount = linecount } end @@ -1306,7 +1307,7 @@ local function fmt_ext_state(name, state) for k,v in pairs(state) do str = (str.." ["..k.."] = {win = {id = "..v.win.id.."}, topline = " ..v.topline..", botline = "..v.botline..", curline = "..v.curline - ..", curcol = "..v.curcol.."};\n") + ..", curcol = "..v.curcol..", linecount = "..v.linecount.."};\n") end return str .. "}" elseif name == "float_pos" then diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 06c92a4b10..741b93043d 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -176,8 +176,8 @@ describe('Signs', function() command('sign place 5 line=3 name=pietWarn buffer=1') command('sign place 3 line=3 name=pietError buffer=1') screen:expect([[ - {1:>>}XX{6: 1 }a | - XX{1:>>}{6: 2 }b | + {1:>>}{8:XX}{6: 1 }a | + {8:XX}{1:>>}{6: 2 }b | {1:>>}WW{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| @@ -194,7 +194,7 @@ describe('Signs', function() -- With the default setting, we get the sign with the top id. command('set signcolumn=yes:1') screen:expect([[ - XX{6: 1 }a | + {8:XX}{6: 1 }a | {1:>>}{6: 2 }b | WW{6: 3 }c | {2: }{6: 4 }^ | @@ -212,9 +212,9 @@ describe('Signs', function() -- "auto:3" accommodates all the signs we defined so far. command('set signcolumn=auto:3') screen:expect([[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}{2: }{6: 2 }b | - XX{1:>>}WW{6: 3 }c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}{2: }{6: 2 }b | + {8:XX}{1:>>}WW{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| {0:~ }| @@ -230,9 +230,9 @@ describe('Signs', function() -- Check "yes:9". command('set signcolumn=yes:9') screen:expect([[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}{2: }{6: 2 }b | - XX{1:>>}WW{2: }{6: 3 }c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}{2: }{6: 2 }b | + {8:XX}{1:>>}WW{2: }{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| {0:~ }| @@ -249,9 +249,9 @@ describe('Signs', function() -- a single line (same result as "auto:3"). command('set signcolumn=auto:4') screen:expect{grid=[[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}{2: }{6: 2 }b | - XX{1:>>}WW{6: 3 }c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}{2: }{6: 2 }b | + {8:XX}{1:>>}WW{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| {0:~ }| @@ -267,8 +267,8 @@ describe('Signs', function() -- line deletion deletes signs. command('2d') screen:expect([[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}WW{6: 2 }^c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}WW{6: 2 }^c | {2: }{6: 3 } | {0:~ }| {0:~ }| diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index d1af0e955c..4e1852162f 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -913,4 +913,46 @@ describe('Screen', function() ]]} eq(grid_lines, {{2, 0, {{'c', 0, 3}}}}) end) + + -- Copy of Test_cursor_column_in_concealed_line_after_window_scroll in + -- test/functional/ui/syntax_conceal_spec.lua. + describe('concealed line after window scroll', function() + after_each(function() + command(':qall!') + os.remove('Xcolesearch') + end) + + it('has the correct cursor column', function() + insert([[ + 3split + let m = matchadd('Conceal', '=') + setl conceallevel=2 concealcursor=nc + normal gg + "==expr== + ]]) + + command('write Xcolesearch') + feed(":so %<CR>") + + -- Jump to something that is beyond the bottom of the window, + -- so there's a scroll down. + feed("/expr<CR>") + + -- Are the concealed parts of the current line really hidden? + -- Is the window's cursor column properly updated for hidden + -- parts of the current line? + screen:expect{grid=[[ + setl conceallevel2 concealcursornc | + normal gg | + "{5:^expr} | + {2:Xcolesearch }| + normal gg | + "=={5:expr}== | + | + {0:~ }| + {3:Xcolesearch }| + /expr | + ]]} + end) + end) end) diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua index ab8d63cda1..809486d4db 100644 --- a/test/functional/ui/tabline_spec.lua +++ b/test/functional/ui/tabline_spec.lua @@ -69,6 +69,7 @@ describe('ui/ext_tabline', function() command("bnext") local expected_buffers = { + {buffer = { id = 1 }, name = '[No Name]'}, {buffer = { id = 2 }, name = 'another-buffer'}, } screen:expect{grid=[[ diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua deleted file mode 100644 index b8137038b1..0000000000 --- a/test/functional/viml/function_spec.lua +++ /dev/null @@ -1,216 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local clear = helpers.clear -local dedent = helpers.dedent -local redir_exec = helpers.redir_exec - -before_each(clear) - -local function check_func(fname, body, indent) - if type(body) == 'number' then - body = ('return %i'):format(body) - end - eq(dedent(([[ - - function %s()%s - endfunction]] - ), 3):format( - fname, - body and ('\n1' .. (' '):rep(2 + (indent or 8)) .. body) or ''), - redir_exec('function ' .. fname)) -end - -describe(':endfunction', function() - it('accepts bang', function() - eq('', redir_exec([[ - function F() - endfunction! - ]])) - check_func('F') - eq('', redir_exec([[ - function! F() - return 1 - endfunction! - ]])) - check_func('F', 1) - end) - it('accepts comments', function() - eq('', redir_exec([[ - function F1() - endfunction " Comment - ]])) - check_func('F1') - eq('', redir_exec([[ - function F2() - endfunction " }}} - ]])) - check_func('F2') - eq('', redir_exec([[ - function F3() - endfunction " F3 - ]])) - check_func('F3') - eq('', redir_exec([[ - function F4() - endfunction! " F4 - ]])) - check_func('F4') - eq('', redir_exec([[ - function! F4() - return 2 - endfunction! " F4 - ]])) - check_func('F4', 2) - end) - it('accepts function name', function() - eq('', redir_exec([[ - function F0() - endfunction F0 - ]])) - check_func('F0') - eq('', redir_exec([[ - function F1() - endfunction! F1 - ]])) - check_func('F1') - eq('', redir_exec([[ - function! F2() - endfunction! F2 - ]])) - check_func('F2') - eq('', redir_exec([[ - function! F2() - return 3 - endfunction! F2 - ]])) - check_func('F2', 3) - end) - it('accepts weird characters', function() - eq('', redir_exec([[ - function F1() - endfunction: }}} - ]])) - check_func('F1') - -- From accurev - eq('', redir_exec([[ - function F2() - endfunction :}}} - ]])) - check_func('F2') - -- From cream-vimabbrev - eq('', redir_exec([[ - function F3() - endfunction 1}}} - ]])) - check_func('F3') - -- From pyunit - eq('', redir_exec([[ - function F4() - endfunction # }}} - ]])) - check_func('F4') - -- From vim-lldb - eq('', redir_exec([[ - function F5() - endfunction() - ]])) - check_func('F5') - -- From vim-mail - eq('', redir_exec([[ - function F6() - endfunction; - ]])) - check_func('F6') - end) - it('accepts commented bar', function() - eq('', redir_exec([[ - function F1() - endfunction " F1 | echo 42 - ]])) - check_func('F1') - eq('', redir_exec([[ - function! F1() - return 42 - endfunction! " F1 | echo 42 - ]])) - check_func('F1', 42) - end) - it('accepts uncommented bar', function() - eq('\n42', redir_exec([[ - function F1() - endfunction | echo 42 - ]])) - check_func('F1') - end) - it('allows running multiple commands', function() - eq('\n2', redir_exec([[ - function F1() - echo 2 - endfunction - call F1() - ]])) - check_func('F1', 'echo 2') - eq('\n2\n3\n4', redir_exec([[ - function F2() - echo 2 - endfunction F2 - function F3() - echo 3 - endfunction " F3 - function! F4() - echo 4 - endfunction! - call F2() - call F3() - call F4() - ]])) - check_func('F2', 'echo 2') - check_func('F3', 'echo 3') - check_func('F4', 'echo 4') - end) - it('allows running multiple commands with only one character in between', - function() - eq('\n3', redir_exec(dedent([[ - function! F1() - echo 3 - endfunction! - call F1()]]))) - check_func('F1', 'echo 3', 2) - eq('\n4', redir_exec(dedent([[ - function F5() - echo 4 - endfunction - call F5()]]))) - check_func('F5', 'echo 4', 2) - eq('\n5', redir_exec(dedent([[ - function F6() - echo 5 - endfunction " TEST - call F6()]]))) - check_func('F6', 'echo 5', 2) - eq('\n6', redir_exec(dedent([[ - function F7() - echo 6 - endfunction F7 - call F7()]]))) - check_func('F7', 'echo 6', 2) - eq('\n2\n3\n4', redir_exec(dedent([[ - function F2() - echo 2 - endfunction F2 - function F3() - echo 3 - endfunction " F3 - function! F4() - echo 4 - endfunction! - call F2() - call F3() - call F4()]]))) - check_func('F2', 'echo 2', 2) - check_func('F3', 'echo 3', 2) - check_func('F4', 'echo 4', 2) - end) -end) --- vim: foldmarker=▶,▲ diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua index 7d09a652ba..d07e74d40e 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/vimscript/api_functions_spec.lua @@ -155,4 +155,13 @@ describe('eval-API', function() pcall_err(command, "sandbox call nvim_input('ievil')")) eq({''}, meths.buf_get_lines(0, 0, -1, true)) end) + + it('converts blobs to API strings', function() + command('let g:v1 = nvim__id(0z68656c6c6f)') + command('let g:v2 = nvim__id(v:_null_blob)') + eq(1, eval('type(g:v1)')) + eq(1, eval('type(g:v2)')) + eq('hello', eval('g:v1')) + eq('', eval('g:v2')) + end) end) diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/vimscript/buf_functions_spec.lua index 06841a4521..e957e5f5af 100644 --- a/test/functional/eval/buf_functions_spec.lua +++ b/test/functional/vimscript/buf_functions_spec.lua @@ -221,9 +221,9 @@ describe('getbufvar() function', function() eq(0, funcs.getbufvar(1, '&l:autoindent')) eq(0, funcs.getbufvar(1, '&g:autoindent')) -- Also works with global-only options - eq(0, funcs.getbufvar(1, '&hidden')) - eq(0, funcs.getbufvar(1, '&l:hidden')) - eq(0, funcs.getbufvar(1, '&g:hidden')) + eq(1, funcs.getbufvar(1, '&hidden')) + eq(1, funcs.getbufvar(1, '&l:hidden')) + eq(1, funcs.getbufvar(1, '&g:hidden')) -- Also works with window-local options eq(0, funcs.getbufvar(1, '&number')) eq(0, funcs.getbufvar(1, '&l:number')) @@ -279,9 +279,9 @@ describe('setbufvar() function', function() eq(false, winmeths.get_option(windows[3], 'number')) eq(false, winmeths.get_option(meths.get_current_win(), 'number')) - eq(false, meths.get_option('hidden')) - funcs.setbufvar(1, '&hidden', true) eq(true, meths.get_option('hidden')) + funcs.setbufvar(1, '&hidden', 0) + eq(false, meths.get_option('hidden')) eq(false, bufmeths.get_option(buf1, 'autoindent')) funcs.setbufvar(1, '&autoindent', true) diff --git a/test/functional/eval/changedtick_spec.lua b/test/functional/vimscript/changedtick_spec.lua index 99406d9d7a..8533fac9ec 100644 --- a/test/functional/eval/changedtick_spec.lua +++ b/test/functional/vimscript/changedtick_spec.lua @@ -8,8 +8,8 @@ local funcs = helpers.funcs local meths = helpers.meths local command = helpers.command local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec local pcall_err = helpers.pcall_err +local exec_capture = helpers.exec_capture local curbufmeths = helpers.curbufmeths before_each(clear) @@ -56,35 +56,35 @@ describe('b:changedtick', function() local ct = changedtick() local ctn = ct + 100500 eq(0, exc_exec('let d = b:')) - eq('\nE46: Cannot change read-only variable "b:changedtick"', - redir_exec('let b:changedtick = ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] = ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:.changedtick"', - redir_exec('let b:.changedtick = ' .. ctn)) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('let d.changedtick = ' .. ctn)) + eq('Vim(let):E46: Cannot change read-only variable "b:changedtick"', + pcall_err(command, 'let b:changedtick = ' .. ctn)) + eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"', + pcall_err(command, 'let b:["changedtick"] = ' .. ctn)) + eq('Vim(let):E46: Cannot change read-only variable "b:.changedtick"', + pcall_err(command, 'let b:.changedtick = ' .. ctn)) + eq('Vim(let):E46: Cannot change read-only variable "d.changedtick"', + pcall_err(command, 'let d.changedtick = ' .. ctn)) eq('Key is read-only: changedtick', pcall_err(curbufmeths.set_var, 'changedtick', ctn)) - eq('\nE795: Cannot delete variable b:changedtick', - redir_exec('unlet b:changedtick')) - eq('\nE46: Cannot change read-only variable "b:.changedtick"', - redir_exec('unlet b:.changedtick')) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('unlet b:["changedtick"]')) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('unlet d.changedtick')) + eq('Vim(unlet):E795: Cannot delete variable b:changedtick', + pcall_err(command, 'unlet b:changedtick')) + eq('Vim(unlet):E46: Cannot change read-only variable "b:.changedtick"', + pcall_err(command, 'unlet b:.changedtick')) + eq('Vim(unlet):E46: Cannot change read-only variable "b:["changedtick"]"', + pcall_err(command, 'unlet b:["changedtick"]')) + eq('Vim(unlet):E46: Cannot change read-only variable "d.changedtick"', + pcall_err(command, 'unlet d.changedtick')) eq('Key is read-only: changedtick', pcall_err(curbufmeths.del_var, 'changedtick')) eq(ct, changedtick()) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] += ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] -= ' .. ctn)) - eq('\nE46: Cannot change read-only variable "b:["changedtick"]"', - redir_exec('let b:["changedtick"] .= ' .. ctn)) + eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"', + pcall_err(command, 'let b:["changedtick"] += ' .. ctn)) + eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"', + pcall_err(command, 'let b:["changedtick"] -= ' .. ctn)) + eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"', + pcall_err(command, 'let b:["changedtick"] .= ' .. ctn)) eq(ct, changedtick()) @@ -93,23 +93,22 @@ describe('b:changedtick', function() eq(ct + 1, changedtick()) end) it('is listed in :let output', function() - eq('\nb:changedtick #2', - redir_exec(':let b:')) + eq('b:changedtick #2', exec_capture(':let b:')) end) it('fails to unlock b:changedtick', function() eq(0, exc_exec('let d = b:')) eq(0, funcs.islocked('b:changedtick')) eq(0, funcs.islocked('d.changedtick')) - eq('\nE940: Cannot lock or unlock variable b:changedtick', - redir_exec('unlockvar b:changedtick')) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('unlockvar d.changedtick')) + eq('Vim(unlockvar):E940: Cannot lock or unlock variable b:changedtick', + pcall_err(command, 'unlockvar b:changedtick')) + eq('Vim(unlockvar):E46: Cannot change read-only variable "d.changedtick"', + pcall_err(command, 'unlockvar d.changedtick')) eq(0, funcs.islocked('b:changedtick')) eq(0, funcs.islocked('d.changedtick')) - eq('\nE940: Cannot lock or unlock variable b:changedtick', - redir_exec('lockvar b:changedtick')) - eq('\nE46: Cannot change read-only variable "d.changedtick"', - redir_exec('lockvar d.changedtick')) + eq('Vim(lockvar):E940: Cannot lock or unlock variable b:changedtick', + pcall_err(command, 'lockvar b:changedtick')) + eq('Vim(lockvar):E46: Cannot change read-only variable "d.changedtick"', + pcall_err(command, 'lockvar d.changedtick')) eq(0, funcs.islocked('b:changedtick')) eq(0, funcs.islocked('d.changedtick')) end) @@ -119,24 +118,24 @@ describe('b:changedtick', function() end) it('cannot be changed by filter() or map()', function() eq(2, changedtick()) - eq('\nE795: Cannot delete variable filter() argument', - redir_exec('call filter(b:, 0)')) - eq('\nE742: Cannot change value of map() argument', - redir_exec('call map(b:, 0)')) - eq('\nE742: Cannot change value of map() argument', - redir_exec('call map(b:, "v:val")')) + eq('Vim(call):E795: Cannot delete variable filter() argument', + pcall_err(command, 'call filter(b:, 0)')) + eq('Vim(call):E742: Cannot change value of map() argument', + pcall_err(command, 'call map(b:, 0)')) + eq('Vim(call):E742: Cannot change value of map() argument', + pcall_err(command, 'call map(b:, "v:val")')) eq(2, changedtick()) end) it('cannot be remove()d', function() eq(2, changedtick()) - eq('\nE795: Cannot delete variable remove() argument', - redir_exec('call remove(b:, "changedtick")')) + eq('Vim(call):E795: Cannot delete variable remove() argument', + pcall_err(command, 'call remove(b:, "changedtick")')) eq(2, changedtick()) end) it('does not inherit VAR_FIXED when copying dictionary over', function() eq(2, changedtick()) - eq('', redir_exec('let d1 = copy(b:)|let d1.changedtick = 42')) - eq('', redir_exec('let d2 = copy(b:)|unlet d2.changedtick')) + eq('', exec_capture('let d1 = copy(b:)|let d1.changedtick = 42')) + eq('', exec_capture('let d2 = copy(b:)|unlet d2.changedtick')) eq(2, changedtick()) end) end) diff --git a/test/functional/eval/container_functions_spec.lua b/test/functional/vimscript/container_functions_spec.lua index 04a3248c49..04a3248c49 100644 --- a/test/functional/eval/container_functions_spec.lua +++ b/test/functional/vimscript/container_functions_spec.lua diff --git a/test/functional/eval/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua index f23adbc556..d92a81c55b 100644 --- a/test/functional/eval/ctx_functions_spec.lua +++ b/test/functional/vimscript/ctx_functions_spec.lua @@ -9,7 +9,7 @@ local feed = helpers.feed local map = helpers.tbl_map local nvim = helpers.nvim local parse_context = helpers.parse_context -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture local source = helpers.source local trim = helpers.trim local write_file = helpers.write_file @@ -163,33 +163,29 @@ describe('context functions', function() endfunction ]]) - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. + eq('Hello, World!', exec_capture([[call Greet('World')]])) + eq('Hello, World!'.. '\nHello, One!'.. '\nHello, Two!'.. '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) + exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])) call('SaveSFuncs') call('DeleteSFuncs') - eq('\nError detected while processing function Greet:'.. - '\nline 1:'.. - '\nE117: Unknown function: s:greet', - redir_exec([[call Greet('World')]])) - eq('\nError detected while processing function GreetAll:'.. - '\nline 1:'.. - '\nE117: Unknown function: s:greet_all', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) + eq('Vim(call):E117: Unknown function: s:greet', + pcall_err(command, [[call Greet('World')]])) + eq('Vim(call):E117: Unknown function: s:greet_all', + pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]])) call('RestoreFuncs') - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. + eq('Hello, World!', exec_capture([[call Greet('World')]])) + eq('Hello, World!'.. '\nHello, One!'.. '\nHello, Two!'.. '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) + exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])) end) it('saves and restores functions properly', function() @@ -206,12 +202,12 @@ describe('context functions', function() endfunction ]]) - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. + eq('Hello, World!', exec_capture([[call Greet('World')]])) + eq('Hello, World!'.. '\nHello, One!'.. '\nHello, Two!'.. '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) + exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])) call('ctxpush', {'funcs'}) command('delfunction Greet') @@ -223,12 +219,12 @@ describe('context functions', function() call('ctxpop') - eq('\nHello, World!', redir_exec([[call Greet('World')]])) - eq('\nHello, World!'.. + eq('Hello, World!', exec_capture([[call Greet('World')]])) + eq('Hello, World!'.. '\nHello, One!'.. '\nHello, Two!'.. '\nHello, Three!', - redir_exec([[call GreetAll('World', 'One', 'Two', 'Three')]])) + exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])) end) it('errors out when context stack is empty', function() diff --git a/test/functional/eval/environ_spec.lua b/test/functional/vimscript/environ_spec.lua index 9e19568249..9e19568249 100644 --- a/test/functional/eval/environ_spec.lua +++ b/test/functional/vimscript/environ_spec.lua diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/vimscript/errorlist_spec.lua index 077d816903..077d816903 100644 --- a/test/functional/viml/errorlist_spec.lua +++ b/test/functional/vimscript/errorlist_spec.lua diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua new file mode 100644 index 0000000000..e1459ab5b8 --- /dev/null +++ b/test/functional/vimscript/eval_spec.lua @@ -0,0 +1,146 @@ +-- Tests for core Vimscript "eval" behavior. +-- +-- See also: +-- let_spec.lua +-- null_spec.lua +-- operators_spec.lua +-- +-- Tests for the Vimscript |functions| library should live in: +-- test/functional/vimscript/<funcname>_spec.lua +-- test/functional/vimscript/functions_spec.lua + +local helpers = require('test.functional.helpers')(after_each) + +local lfs = require('lfs') +local clear = helpers.clear +local eq = helpers.eq +local exc_exec = helpers.exc_exec +local eval = helpers.eval +local command = helpers.command +local write_file = helpers.write_file +local meths = helpers.meths +local sleep = helpers.sleep +local poke_eventloop = helpers.poke_eventloop +local feed = helpers.feed + +describe('Up to MAX_FUNC_ARGS arguments are handled by', function() + local max_func_args = 20 -- from eval.h + local range = helpers.funcs.range + + before_each(clear) + + it('printf()', function() + local printf = helpers.funcs.printf + local rep = helpers.funcs['repeat'] + local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' + eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) + local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function printf', ret) + end) + + it('rpcnotify()', function() + local rpcnotify = helpers.funcs.rpcnotify + local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) + eq(1, ret) + ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) + end) +end) + +describe("backtick expansion", function() + setup(function() + clear() + lfs.mkdir("test-backticks") + write_file("test-backticks/file1", "test file 1") + write_file("test-backticks/file2", "test file 2") + write_file("test-backticks/file3", "test file 3") + lfs.mkdir("test-backticks/subdir") + write_file("test-backticks/subdir/file4", "test file 4") + -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. + command('silent cd test-backticks') + end) + + teardown(function() + helpers.rmdir('test-backticks') + end) + + it("with default 'shell'", function() + if helpers.iswin() then + command(":silent args `dir /b *2`") + else + command(":silent args `echo ***2`") + end + eq({ "file2", }, eval("argv()")) + if helpers.iswin() then + command(":silent args `dir /s/b *4`") + eq({ "subdir\\file4", }, eval("map(argv(), 'fnamemodify(v:val, \":.\")')")) + else + command(":silent args `echo */*4`") + eq({ "subdir/file4", }, eval("argv()")) + end + end) + + it("with shell=fish", function() + if eval("executable('fish')") == 0 then + pending('missing "fish" command') + return + end + command("set shell=fish") + command(":silent args `echo ***2`") + eq({ "file2", }, eval("argv()")) + command(":silent args `echo */*4`") + eq({ "subdir/file4", }, eval("argv()")) + end) +end) + +describe('List support code', function() + local dur + local min_dur = 8 + local len = 131072 + + if not pending('does not actually allows interrupting with just got_int', function() end) then return end + -- The following tests are confirmed to work with os_breakcheck() just before + -- `if (got_int) {break;}` in tv_list_copy and list_join_inner() and not to + -- work without. + setup(function() + clear() + dur = 0 + while true do + command(([[ + let rt = reltime() + let bl = range(%u) + let dur = reltimestr(reltime(rt)) + ]]):format(len)) + dur = tonumber(meths.get_var('dur')) + if dur >= min_dur then + -- print(('Using len %u, dur %g'):format(len, dur)) + break + else + len = len * 2 + end + end + end) + it('allows interrupting copy', function() + feed(':let t_rt = reltime()<CR>:let t_bl = copy(bl)<CR>') + sleep(min_dur / 16 * 1000) + feed('<C-c>') + 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 + eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) + end + end) + it('allows interrupting join', function() + feed(':let t_rt = reltime()<CR>:let t_j = join(bl)<CR>') + sleep(min_dur / 16 * 1000) + feed('<C-c>') + 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)) + if t_dur >= dur / 8 then + eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) + end + end) +end) diff --git a/test/functional/eval/executable_spec.lua b/test/functional/vimscript/executable_spec.lua index 28aefb72e5..28aefb72e5 100644 --- a/test/functional/eval/executable_spec.lua +++ b/test/functional/vimscript/executable_spec.lua diff --git a/test/functional/eval/execute_spec.lua b/test/functional/vimscript/execute_spec.lua index f52ac4e59b..e21c71dc7f 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/vimscript/execute_spec.lua @@ -3,7 +3,6 @@ local eq = helpers.eq local eval = helpers.eval local clear = helpers.clear local source = helpers.source -local redir_exec = helpers.redir_exec local exc_exec = helpers.exc_exec local funcs = helpers.funcs local Screen = require('test.functional.ui.screen') @@ -15,7 +14,14 @@ describe('execute()', function() before_each(clear) it('captures the same result as :redir', function() - eq(redir_exec('messages'), funcs.execute('messages')) + command([[ + echomsg 'foo 1' + echomsg 'foo 2' + redir => g:__redir_output + silent! messages + redir END + ]]) + eq(eval('g:__redir_output'), funcs.execute('messages')) end) it('captures the concatenated outputs of a List of commands', function() @@ -322,16 +328,16 @@ describe('execute()', function() eq('Vim(call):E731: using Dictionary as a String', ret) ret = exc_exec('call execute("echo add(1, 1)", "")') - eq('Vim(echo):E714: List required', ret) + eq('Vim(echo):E897: List or Blob required', ret) ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")') - eq('Vim(echo):E714: List required', ret) + eq('Vim(echo):E897: List or Blob required', ret) ret = exc_exec('call execute("echo add(1, 1)", "silent")') - eq('Vim(echo):E714: List required', ret) + eq('Vim(echo):E897: List or Blob required', ret) ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")') - eq('Vim(echo):E714: List required', ret) + eq('Vim(echo):E897: List or Blob required', ret) end) end) end) diff --git a/test/functional/eval/exepath_spec.lua b/test/functional/vimscript/exepath_spec.lua index 08d2c59af8..08d2c59af8 100644 --- a/test/functional/eval/exepath_spec.lua +++ b/test/functional/vimscript/exepath_spec.lua diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/vimscript/fnamemodify_spec.lua index d54a6db417..d54a6db417 100644 --- a/test/functional/eval/fnamemodify_spec.lua +++ b/test/functional/vimscript/fnamemodify_spec.lua diff --git a/test/functional/vimscript/functions_spec.lua b/test/functional/vimscript/functions_spec.lua new file mode 100644 index 0000000000..0ad7fd8010 --- /dev/null +++ b/test/functional/vimscript/functions_spec.lua @@ -0,0 +1,20 @@ +-- Tests for misc Vimscript |functions|. +-- +-- If a function is non-trivial, consider moving its spec to: +-- test/functional/vimscript/<funcname>_spec.lua +-- +-- Core "eval" tests live in eval_spec.lua. + +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eval = helpers.eval +local iswin = helpers.iswin +local matches = helpers.matches + +before_each(clear) + +it('windowsversion()', function() + clear() + matches(iswin() and '^%d+%.%d+$' or '^$', eval('windowsversion()')) +end) diff --git a/test/functional/eval/getline_spec.lua b/test/functional/vimscript/getline_spec.lua index 3c56bde094..3c56bde094 100644 --- a/test/functional/eval/getline_spec.lua +++ b/test/functional/vimscript/getline_spec.lua diff --git a/test/functional/eval/glob_spec.lua b/test/functional/vimscript/glob_spec.lua index b8807ecfcc..b8807ecfcc 100644 --- a/test/functional/eval/glob_spec.lua +++ b/test/functional/vimscript/glob_spec.lua diff --git a/test/functional/eval/has_spec.lua b/test/functional/vimscript/has_spec.lua index a3af2d1a20..a3af2d1a20 100644 --- a/test/functional/eval/has_spec.lua +++ b/test/functional/vimscript/has_spec.lua diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/vimscript/hostname_spec.lua index 6112cf64e3..6112cf64e3 100644 --- a/test/functional/eval/hostname_spec.lua +++ b/test/functional/vimscript/hostname_spec.lua diff --git a/test/functional/eval/input_spec.lua b/test/functional/vimscript/input_spec.lua index 14c02f9eb2..14c02f9eb2 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/vimscript/input_spec.lua diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/vimscript/json_functions_spec.lua index 8dcaea806e..c3b607b544 100644 --- a/test/functional/eval/json_functions_spec.lua +++ b/test/functional/vimscript/json_functions_spec.lua @@ -6,7 +6,7 @@ local eq = helpers.eq local eval = helpers.eval local command = helpers.command local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err local NIL = helpers.NIL local source = helpers.source @@ -517,9 +517,8 @@ describe('json_decode() function', function() it('does not overflow when writing error message about decoding ["", ""]', function() - eq('\nE474: Attempt to decode a blank string' - .. '\nE474: Failed to parse \n', - redir_exec('call json_decode(["", ""])')) + eq('Vim(call):E474: Attempt to decode a blank string', + pcall_err(command, 'call json_decode(["", ""])')) end) end) @@ -538,6 +537,11 @@ describe('json_encode() function', function() eq('"þÿþ"', funcs.json_encode('þÿþ')) end) + it('dumps blobs', function() + eq('[]', eval('json_encode(0z)')) + eq('[222, 173, 190, 239]', eval('json_encode(0zDEADBEEF)')) + end) + it('dumps numbers', function() eq('0', funcs.json_encode(0)) eq('10', funcs.json_encode(10)) @@ -769,6 +773,10 @@ describe('json_encode() function', function() eq('""', eval('json_encode($XXX_UNEXISTENT_VAR_XXX)')) end) + it('can dump NULL blob', function() + eq('[]', eval('json_encode(v:_null_blob)')) + end) + it('can dump NULL list', function() eq('[]', eval('json_encode(v:_null_list)')) end) diff --git a/test/functional/viml/lang_spec.lua b/test/functional/vimscript/lang_spec.lua index 6d603b8822..d5254986ab 100644 --- a/test/functional/viml/lang_spec.lua +++ b/test/functional/vimscript/lang_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq local exc_exec, source = helpers.exc_exec, helpers.source -describe('viml', function() +describe('vimscript', function() before_each(clear) it('parses `<SID>` with turkish locale', function() diff --git a/test/functional/eval/let_spec.lua b/test/functional/vimscript/let_spec.lua index 5bc703b567..4ff4090a18 100644 --- a/test/functional/eval/let_spec.lua +++ b/test/functional/vimscript/let_spec.lua @@ -5,7 +5,7 @@ local clear = helpers.clear local command = helpers.command local eval = helpers.eval local meths = helpers.meths -local redir_exec = helpers.redir_exec +local exec_capture = helpers.exec_capture local source = helpers.source local nvim_dir = helpers.nvim_dir @@ -14,14 +14,14 @@ before_each(clear) describe(':let', function() it('correctly lists variables with curly-braces', function() meths.set_var('v', {0}) - eq('\nv [0]', redir_exec('let {"v"}')) + eq('v [0]', exec_capture('let {"v"}')) end) it('correctly lists variables with subscript', function() meths.set_var('v', {0}) - eq('\nv[0] #0', redir_exec('let v[0]')) - eq('\ng:["v"][0] #0', redir_exec('let g:["v"][0]')) - eq('\n{"g:"}["v"][0] #0', redir_exec('let {"g:"}["v"][0]')) + eq('v[0] #0', exec_capture('let v[0]')) + eq('g:["v"][0] #0', exec_capture('let g:["v"][0]')) + eq('{"g:"}["v"][0] #0', exec_capture('let {"g:"}["v"][0]')) end) it(":unlet self-referencing node in a List graph #6070", function() diff --git a/test/functional/eval/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua index 275c72d212..275c72d212 100644 --- a/test/functional/eval/map_functions_spec.lua +++ b/test/functional/vimscript/map_functions_spec.lua diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/vimscript/match_functions_spec.lua index f399ef47d3..9f168c913a 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/vimscript/match_functions_spec.lua @@ -6,7 +6,6 @@ local clear = helpers.clear local funcs = helpers.funcs local command = helpers.command local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err before_each(clear) @@ -40,13 +39,13 @@ describe('setmatches()', function() }}, funcs.getmatches()) end) - it('fails with -1 if highlight group is not defined', function() - eq('Vim:E28: No such highlight group name: 1', - pcall_err(funcs.setmatches, {{group=1, pattern=2, id=3, priority=4}})) - eq({}, funcs.getmatches()) - eq('Vim:E28: No such highlight group name: 1', - pcall_err(funcs.setmatches, {{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})) - eq({}, funcs.getmatches()) + it('does not fail if highlight group is not defined', function() + eq(0, funcs.setmatches{{group=1, pattern=2, id=3, priority=4}}) + eq({{group='1', pattern='2', id=3, priority=4}}, + funcs.getmatches()) + eq(0, funcs.setmatches{{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}}) + eq({{group='1', pos1={2}, pos2={6}, id=3, priority=4, conceal='5'}}, + funcs.getmatches()) end) end) diff --git a/test/functional/eval/minmax_functions_spec.lua b/test/functional/vimscript/minmax_functions_spec.lua index c6eb754f91..91106bef1e 100644 --- a/test/functional/eval/minmax_functions_spec.lua +++ b/test/functional/vimscript/minmax_functions_spec.lua @@ -2,29 +2,28 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq local eval = helpers.eval +local command = helpers.command local clear = helpers.clear local funcs = helpers.funcs -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err before_each(clear) for _, func in ipairs({'min', 'max'}) do describe(func .. '()', function() it('gives a single error message when multiple values failed conversions', function() - eq('\nE745: Using a List as a Number\n0', - redir_exec('echo ' .. func .. '([-5, [], [], [], 5])')) - eq('\nE745: Using a List as a Number\n0', - redir_exec('echo ' .. func .. '({1:-5, 2:[], 3:[], 4:[], 5:5})')) + eq('Vim(echo):E745: Using a List as a Number', + pcall_err(command, 'echo ' .. func .. '([-5, [], [], [], 5])')) + eq('Vim(echo):E745: Using a List as a Number', + pcall_err(command, 'echo ' .. func .. '({1:-5, 2:[], 3:[], 4:[], 5:5})')) for errmsg, errinput in pairs({ - ['E745: Using a List as a Number'] = '[]', - ['E805: Using a Float as a Number'] = '0.0', - ['E703: Using a Funcref as a Number'] = 'function("tr")', - ['E728: Using a Dictionary as a Number'] = '{}', + ['Vim(echo):E745: Using a List as a Number'] = '[]', + ['Vim(echo):E805: Using a Float as a Number'] = '0.0', + ['Vim(echo):E703: Using a Funcref as a Number'] = 'function("tr")', + ['Vim(echo):E728: Using a Dictionary as a Number'] = '{}', }) do - eq('\n' .. errmsg .. '\n0', - redir_exec('echo ' .. func .. '([' .. errinput .. '])')) - eq('\n' .. errmsg .. '\n0', - redir_exec('echo ' .. func .. '({1:' .. errinput .. '})')) + eq(errmsg, pcall_err(command, 'echo ' .. func .. '([' .. errinput .. '])')) + eq(errmsg, pcall_err(command, 'echo ' .. func .. '({1:' .. errinput .. '})')) end end) it('works with arrays/dictionaries with zero items', function() @@ -42,9 +41,8 @@ for _, func in ipairs({'min', 'max'}) do it('errors out for invalid types', function() for _, errinput in ipairs({'1', 'v:true', 'v:false', 'v:null', 'function("tr")', '""'}) do - eq(('\nE712: Argument of %s() must be a List or Dictionary\n0'):format( - func), - redir_exec('echo ' .. func .. '(' .. errinput .. ')')) + eq(('Vim(echo):E712: Argument of %s() must be a List or Dictionary'):format(func), + pcall_err(command, 'echo ' .. func .. '(' .. errinput .. ')')) end end) end) diff --git a/test/functional/eval/modeline_spec.lua b/test/functional/vimscript/modeline_spec.lua index c5bb798f4a..b2346079a1 100644 --- a/test/functional/eval/modeline_spec.lua +++ b/test/functional/vimscript/modeline_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear, command, write_file = helpers.clear, helpers.command, helpers.write_file -local eq, eval = helpers.eq, helpers.eval describe("modeline", function() local tempfile = helpers.tmpname() @@ -14,6 +14,6 @@ describe("modeline", function() write_file(tempfile, 'vim100000000000000000000000') command('e! ' .. tempfile) - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/vimscript/msgpack_functions_spec.lua index a8a413f68b..837b629858 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/vimscript/msgpack_functions_spec.lua @@ -13,6 +13,7 @@ describe('msgpack*() functions', function() it(msg, function() nvim('set_var', 'obj', obj) eq(obj, eval('msgpackparse(msgpackdump(g:obj))')) + eq(obj, eval('msgpackparse(msgpackdump(g:obj, "B"))')) end) end @@ -364,8 +365,7 @@ describe('msgpack*() functions', function() command('let dumped = ["\\xC4\\x01\\n"]') command('let parsed = msgpackparse(dumped)') command('let dumped2 = msgpackdump(parsed)') - eq({{_TYPE={}, _VAL={'\n'}}}, eval('parsed')) - eq(1, eval('parsed[0]._TYPE is v:msgpack_types.binary')) + eq({'\000'}, eval('parsed')) eq(1, eval('dumped ==# dumped2')) end) @@ -392,56 +392,61 @@ describe('msgpack*() functions', function() end) end) +local blobstr = function(list) + local l = {} + for i,v in ipairs(list) do + l[i] = v:gsub('\n', '\000') + end + return table.concat(l, '\n') +end + +-- Test msgpackparse() with a readfile()-style list and a blob argument +local parse_eq = function(expect, list_arg) + local blob_expr = '0z' .. blobstr(list_arg):gsub('(.)', function(c) + return ('%.2x'):format(c:byte()) + end) + eq(expect, funcs.msgpackparse(list_arg)) + command('let g:parsed = msgpackparse(' .. blob_expr .. ')') + eq(expect, eval('g:parsed')) +end + describe('msgpackparse() function', function() before_each(clear) it('restores nil as v:null', function() - command('let dumped = ["\\xC0"]') - command('let parsed = msgpackparse(dumped)') - eq('[v:null]', eval('string(parsed)')) + parse_eq(eval('[v:null]'), {'\192'}) end) it('restores boolean false as v:false', function() - command('let dumped = ["\\xC2"]') - command('let parsed = msgpackparse(dumped)') - eq({false}, eval('parsed')) + parse_eq({false}, {'\194'}) end) it('restores boolean true as v:true', function() - command('let dumped = ["\\xC3"]') - command('let parsed = msgpackparse(dumped)') - eq({true}, eval('parsed')) + parse_eq({true}, {'\195'}) end) it('restores FIXSTR as special dict', function() - command('let dumped = ["\\xa2ab"]') - command('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL={'ab'}}}, eval('parsed')) + parse_eq({{_TYPE={}, _VAL={'ab'}}}, {'\162ab'}) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.string')) end) it('restores BIN 8 as string', function() - command('let dumped = ["\\xC4\\x02ab"]') - eq({'ab'}, eval('msgpackparse(dumped)')) + parse_eq({'ab'}, {'\196\002ab'}) end) it('restores FIXEXT1 as special dictionary', function() - command('let dumped = ["\\xD4\\x10", ""]') - command('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, eval('parsed')) + parse_eq({{_TYPE={}, _VAL={0x10, {"", ""}}}}, {'\212\016', ''}) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.ext')) end) it('restores MAP with BIN key as special dictionary', function() - command('let dumped = ["\\x81\\xC4\\x01a\\xC4\\n"]') - command('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL={{'a', ''}}}}, eval('parsed')) + parse_eq({{_TYPE={}, _VAL={{'a', ''}}}}, {'\129\196\001a\196\n'}) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) end) it('restores MAP with duplicate STR keys as special dictionary', function() command('let dumped = ["\\x82\\xA1a\\xC4\\n\\xA1a\\xC4\\n"]') - -- FIXME Internal error bug + -- FIXME Internal error bug, can't use parse_eq() here command('silent! let parsed = msgpackparse(dumped)') eq({{_TYPE={}, _VAL={ {{_TYPE={}, _VAL={'a'}}, ''}, {{_TYPE={}, _VAL={'a'}}, ''}}} }, eval('parsed')) @@ -451,9 +456,7 @@ describe('msgpackparse() function', function() end) it('restores MAP with MAP key as special dictionary', function() - command('let dumped = ["\\x81\\x80\\xC4\\n"]') - command('let parsed = msgpackparse(dumped)') - eq({{_TYPE={}, _VAL={{{}, ''}}}}, eval('parsed')) + parse_eq({{_TYPE={}, _VAL={{{}, ''}}}}, {'\129\128\196\n'}) eq(1, eval('g:parsed[0]._TYPE is v:msgpack_types.map')) end) @@ -478,43 +481,65 @@ describe('msgpackparse() function', function() end) it('fails to parse a string', function() - eq('Vim(call):E686: Argument of msgpackparse() must be a List', + eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob', exc_exec('call msgpackparse("abcdefghijklmnopqrstuvwxyz")')) end) it('fails to parse a number', function() - eq('Vim(call):E686: Argument of msgpackparse() must be a List', + eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob', exc_exec('call msgpackparse(127)')) end) it('fails to parse a dictionary', function() - eq('Vim(call):E686: Argument of msgpackparse() must be a List', + eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob', exc_exec('call msgpackparse({})')) end) it('fails to parse a funcref', function() - eq('Vim(call):E686: Argument of msgpackparse() must be a List', + eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob', exc_exec('call msgpackparse(function("tr"))')) end) it('fails to parse a partial', function() command('function T() dict\nendfunction') - eq('Vim(call):E686: Argument of msgpackparse() must be a List', + eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob', exc_exec('call msgpackparse(function("T", [1, 2], {}))')) end) it('fails to parse a float', function() - eq('Vim(call):E686: Argument of msgpackparse() must be a List', + eq('Vim(call):E899: Argument of msgpackparse() must be a List or Blob', exc_exec('call msgpackparse(0.0)')) end) + + it('fails on incomplete msgpack string', function() + local expected = 'Vim(call):E475: Invalid argument: Incomplete msgpack string' + eq(expected, exc_exec([[call msgpackparse(["\xc4"])]])) + eq(expected, exc_exec([[call msgpackparse(["\xca", "\x02\x03"])]])) + eq(expected, exc_exec('call msgpackparse(0zc4)')) + eq(expected, exc_exec('call msgpackparse(0zca0a0203)')) + end) + + it('fails when unable to parse msgpack string', function() + local expected = 'Vim(call):E475: Invalid argument: Failed to parse msgpack string' + eq(expected, exc_exec([[call msgpackparse(["\xc1"])]])) + eq(expected, exc_exec('call msgpackparse(0zc1)')) + end) end) describe('msgpackdump() function', function() before_each(clear) + local dump_eq = function(exp_list, arg_expr) + eq(exp_list, eval('msgpackdump(' .. arg_expr .. ')')) + eq(blobstr(exp_list), eval('msgpackdump(' .. arg_expr .. ', "B")')) + end + it('dumps string as BIN 8', function() - nvim('set_var', 'obj', {'Test'}) - eq({"\196\004Test"}, eval('msgpackdump(obj)')) + dump_eq({'\196\004Test'}, '["Test"]') + end) + + it('dumps blob as BIN 8', function() + dump_eq({'\196\005Bl\nb!'}, '[0z426c006221]') end) it('can dump generic mapping with generic mapping keys and values', function() @@ -522,56 +547,56 @@ describe('msgpackdump() function', function() command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('call add(todump._VAL, [todumpv1, todumpv2])') - eq({'\129\128\128'}, eval('msgpackdump([todump])')) + dump_eq({'\129\128\128'}, '[todump]') end) it('can dump v:true', function() - eq({'\195'}, funcs.msgpackdump({true})) + dump_eq({'\195'}, '[v:true]') end) it('can dump v:false', function() - eq({'\194'}, funcs.msgpackdump({false})) + dump_eq({'\194'}, '[v:false]') end) - it('can v:null', function() - command('let todump = v:null') + it('can dump v:null', function() + dump_eq({'\192'}, '[v:null]') end) it('can dump special bool mapping (true)', function() command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 1}') - eq({'\195'}, eval('msgpackdump([todump])')) + dump_eq({'\195'}, '[todump]') end) it('can dump special bool mapping (false)', function() command('let todump = {"_TYPE": v:msgpack_types.boolean, "_VAL": 0}') - eq({'\194'}, eval('msgpackdump([todump])')) + dump_eq({'\194'}, '[todump]') end) it('can dump special nil mapping', function() command('let todump = {"_TYPE": v:msgpack_types.nil, "_VAL": 0}') - eq({'\192'}, eval('msgpackdump([todump])')) + dump_eq({'\192'}, '[todump]') end) it('can dump special ext mapping', function() command('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}') - eq({'\212\005', ''}, eval('msgpackdump([todump])')) + dump_eq({'\212\005', ''}, '[todump]') end) it('can dump special array mapping', function() command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": [5, [""]]}') - eq({'\146\005\145\196\n'}, eval('msgpackdump([todump])')) + dump_eq({'\146\005\145\196\n'}, '[todump]') end) it('can dump special UINT64_MAX mapping', function() command('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump._VAL = [1, 3, 0x7FFFFFFF, 0x7FFFFFFF]') - eq({'\207\255\255\255\255\255\255\255\255'}, eval('msgpackdump([todump])')) + dump_eq({'\207\255\255\255\255\255\255\255\255'}, '[todump]') end) it('can dump special INT64_MIN mapping', function() command('let todump = {"_TYPE": v:msgpack_types.integer}') command('let todump._VAL = [-1, 2, 0, 0]') - eq({'\211\128\n\n\n\n\n\n\n'}, eval('msgpackdump([todump])')) + dump_eq({'\211\128\n\n\n\n\n\n\n'}, '[todump]') end) it('fails to dump a function reference', function() @@ -610,13 +635,13 @@ describe('msgpackdump() function', function() it('can dump dict with two same dicts inside', function() command('let inter = {}') command('let todump = {"a": inter, "b": inter}') - eq({"\130\161a\128\161b\128"}, eval('msgpackdump([todump])')) + dump_eq({"\130\161a\128\161b\128"}, '[todump]') end) it('can dump list with two same lists inside', function() command('let inter = []') command('let todump = [inter, inter]') - eq({"\146\144\144"}, eval('msgpackdump([todump])')) + dump_eq({"\146\144\144"}, '[todump]') end) it('fails to dump a recursive list in a special dict', function() @@ -667,9 +692,9 @@ describe('msgpackdump() function', function() exc_exec('call msgpackdump()')) end) - it('fails when called with two arguments', function() + it('fails when called with three arguments', function() eq('Vim(call):E118: Too many arguments for function: msgpackdump', - exc_exec('call msgpackdump(["", ""], 1)')) + exc_exec('call msgpackdump(["", ""], 1, 2)')) end) it('fails to dump a string', function() @@ -711,9 +736,13 @@ describe('msgpackdump() function', function() end) it('can dump NULL string', function() - eq({'\196\n'}, eval('msgpackdump([$XXX_UNEXISTENT_VAR_XXX])')) - eq({'\196\n'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) - eq({'\160'}, eval('msgpackdump([{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}])')) + dump_eq({'\196\n'}, '[$XXX_UNEXISTENT_VAR_XXX]') + dump_eq({'\196\n'}, '[{"_TYPE": v:msgpack_types.binary, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}]') + dump_eq({'\160'}, '[{"_TYPE": v:msgpack_types.string, "_VAL": [$XXX_UNEXISTENT_VAR_XXX]}]') + end) + + it('can dump NULL blob', function() + eq({'\196\n'}, eval('msgpackdump([v:_null_blob])')) end) it('can dump NULL list', function() diff --git a/test/functional/eval/null_spec.lua b/test/functional/vimscript/null_spec.lua index b1ceff9115..7ecbcd2fd6 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/vimscript/null_spec.lua @@ -1,7 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local curbufmeths = helpers.curbufmeths -local redir_exec = helpers.redir_exec local exc_exec = helpers.exc_exec local command = helpers.command local clear = helpers.clear @@ -9,6 +8,19 @@ local meths = helpers.meths local funcs = helpers.funcs local eq = helpers.eq +local function redir_exec(cmd) + meths.set_var('__redir_exec_cmd', cmd) + command([[ + redir => g:__redir_exec_output + silent! execute g:__redir_exec_cmd + redir END + ]]) + local ret = meths.get_var('__redir_exec_output') + meths.del_var('__redir_exec_output') + meths.del_var('__redir_exec_cmd') + return ret +end + describe('NULL', function() before_each(function() clear() @@ -44,7 +56,7 @@ describe('NULL', function() -- Incorrect behaviour -- FIXME Should error out with different message null_test('makes :unlet act as if it is not a list', ':unlet L[0]', - 'Vim(unlet):E689: Can only index a List or Dictionary') + 'Vim(unlet):E689: Can only index a List, Dictionary or Blob') -- Subjectable behaviour @@ -53,7 +65,7 @@ describe('NULL', function() -- Correct behaviour null_expr_test('can be indexed with error message for empty list', 'L[0]', - 'E684: list index out of range: 0\nE15: Invalid expression: L[0]', nil) + 'E684: list index out of range: 0', nil) null_expr_test('can be splice-indexed', 'L[:]', 0, {}) null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) @@ -68,7 +80,7 @@ describe('NULL', function() null_expr_test('can be copied', 'copy(L)', 0, {}) null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) null_expr_test('does not crash when indexed', 'L[1]', - 'E684: list index out of range: 1\nE15: Invalid expression: L[1]', nil) + 'E684: list index out of range: 1', nil) null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) null_expr_test('does not crash col()', 'col(L)', 0, 0) null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) @@ -135,7 +147,7 @@ describe('NULL', function() end) describe('dict', function() it('does not crash when indexing NULL dict', function() - eq('\nE716: Key not present in Dictionary: "test"\nE15: Invalid expression: v:_null_dict.test', + eq('\nE716: Key not present in Dictionary: "test"', redir_exec('echo v:_null_dict.test')) end) null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) diff --git a/test/functional/eval/operators_spec.lua b/test/functional/vimscript/operators_spec.lua index 4d07bc1b05..4d07bc1b05 100644 --- a/test/functional/eval/operators_spec.lua +++ b/test/functional/vimscript/operators_spec.lua diff --git a/test/functional/eval/printf_spec.lua b/test/functional/vimscript/printf_spec.lua index 27e24c4118..27e24c4118 100644 --- a/test/functional/eval/printf_spec.lua +++ b/test/functional/vimscript/printf_spec.lua diff --git a/test/functional/eval/reltime_spec.lua b/test/functional/vimscript/reltime_spec.lua index d87943e485..d87943e485 100644 --- a/test/functional/eval/reltime_spec.lua +++ b/test/functional/vimscript/reltime_spec.lua diff --git a/test/functional/eval/server_spec.lua b/test/functional/vimscript/server_spec.lua index 238d1aeb0f..238d1aeb0f 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/vimscript/server_spec.lua diff --git a/test/functional/eval/setpos_spec.lua b/test/functional/vimscript/setpos_spec.lua index 935f387bcc..935f387bcc 100644 --- a/test/functional/eval/setpos_spec.lua +++ b/test/functional/vimscript/setpos_spec.lua diff --git a/test/functional/eval/sort_spec.lua b/test/functional/vimscript/sort_spec.lua index e1cc2c2924..e09949a0f2 100644 --- a/test/functional/eval/sort_spec.lua +++ b/test/functional/vimscript/sort_spec.lua @@ -8,7 +8,7 @@ local meths = helpers.meths local funcs = helpers.funcs local command = helpers.command local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err before_each(clear) @@ -50,8 +50,7 @@ describe('sort()', function() return (a:a > a:b) - (a:a < a:b) endfunction ]]) - eq('\nE745: Using a List as a Number\nE702: Sort compare function failed', - redir_exec('let sl = sort([1, 0, [], 3, 2], "Cmp")')) - eq({1, 0, {}, 3, 2}, meths.get_var('sl')) + eq('Vim(let):E745: Using a List as a Number', + pcall_err(command, 'let sl = sort([1, 0, [], 3, 2], "Cmp")')) end) end) diff --git a/test/functional/eval/special_vars_spec.lua b/test/functional/vimscript/special_vars_spec.lua index 97a12d490d..97a12d490d 100644 --- a/test/functional/eval/special_vars_spec.lua +++ b/test/functional/vimscript/special_vars_spec.lua diff --git a/test/functional/eval/string_spec.lua b/test/functional/vimscript/string_spec.lua index adc1af9b8e..cb7e93f264 100644 --- a/test/functional/eval/string_spec.lua +++ b/test/functional/vimscript/string_spec.lua @@ -5,11 +5,10 @@ local command = helpers.command local meths = helpers.meths local eval = helpers.eval local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err local funcs = helpers.funcs local NIL = helpers.NIL local source = helpers.source -local dedent = helpers.dedent describe('string() function', function() before_each(clear) @@ -140,8 +139,8 @@ describe('string() function', function() let TestDictRef = function('TestDict', d) let d.tdr = TestDictRef ]]) - eq("\nE724: unable to correctly dump variable with self-referencing container\nfunction('TestDict', {'tdr': function('TestDict', {E724@1})})", - redir_exec('echo string(d.tdr)')) + eq("Vim(echo):E724: unable to correctly dump variable with self-referencing container", + pcall_err(command, 'echo string(d.tdr)')) end) it('dumps automatically created partials', function() @@ -163,11 +162,8 @@ describe('string() function', function() it('does not crash or halt when dumping partials with reference cycles in self', function() meths.set_var('d', {v=true}) - eq(dedent([[ - - E724: unable to correctly dump variable with self-referencing container - {'p': function('<SNR>1_Test2', {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]), - redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) + eq([[Vim(echo):E724: unable to correctly dump variable with self-referencing container]], + pcall_err(command, 'echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))')) end) it('does not show errors when dumping partials referencing the same dictionary', @@ -190,11 +186,8 @@ describe('string() function', function() -- there was error in dumping partials). Tested explicitly in -- test/unit/api/private_helpers_spec.lua. eval('add(l, function("Test1", l))') - eq(dedent([=[ - - E724: unable to correctly dump variable with self-referencing container - function('Test1', [[{E724@2}, function('Test1', [{E724@2}])], function('Test1', [[{E724@4}, function('Test1', [{E724@4}])]])])]=]), - redir_exec('echo string(function("Test1", l))')) + eq([=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=], + pcall_err(command, 'echo string(function("Test1", l))')) end) it('does not crash or halt when dumping partials with reference cycles in self and arguments', @@ -204,11 +197,8 @@ describe('string() function', function() eval('add(l, l)') eval('add(l, function("Test1", l))') eval('add(l, function("Test1", d))') - eq(dedent([=[ - - E724: unable to correctly dump variable with self-referencing container - {'p': function('<SNR>1_Test2', [[{E724@3}, function('Test1', [{E724@3}]), function('Test1', {E724@0})], function('Test1', [[{E724@5}, function('Test1', [{E724@5}]), function('Test1', {E724@0})]]), function('Test1', {E724@0})], {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]), - redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) + eq([=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=], + pcall_err(command, 'echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))')) end) end) @@ -235,10 +225,10 @@ describe('string() function', function() it('dumps recursive lists despite the error', function() meths.set_var('l', {}) eval('add(l, l)') - eq('\nE724: unable to correctly dump variable with self-referencing container\n[{E724@0}]', - redir_exec('echo string(l)')) - eq('\nE724: unable to correctly dump variable with self-referencing container\n[[{E724@1}]]', - redir_exec('echo string([l])')) + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + pcall_err(command, 'echo string(l)')) + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + pcall_err(command, 'echo string([l])')) end) end) @@ -268,10 +258,10 @@ describe('string() function', function() it('dumps recursive dictionaries despite the error', function() meths.set_var('d', {d=1}) eval('extend(d, {"d": d})') - eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'d\': {E724@0}}', - redir_exec('echo string(d)')) - eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'out\': {\'d\': {E724@1}}}', - redir_exec('echo string({"out": d})')) + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + pcall_err(command, 'echo string(d)')) + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + pcall_err(command, 'echo string({"out": d})')) end) end) end) diff --git a/test/functional/eval/system_spec.lua b/test/functional/vimscript/system_spec.lua index c374baf695..24a1f05390 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local nvim_dir = helpers.nvim_dir local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, @@ -302,7 +303,7 @@ describe('system()', function() if v_errnum then eq("E5677:", v_errnum) end - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) end) @@ -317,11 +318,11 @@ describe('system()', function() if v_errnum then eq("E5677:", v_errnum) end - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('works with an empty string', function() eq("test\n", eval('system("echo test", "")')) - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/eval/timer_spec.lua b/test/functional/vimscript/timer_spec.lua index 9ee0735e40..9ee0735e40 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/vimscript/timer_spec.lua diff --git a/test/functional/eval/uniq_spec.lua b/test/functional/vimscript/uniq_spec.lua index 5cdba0a0f6..43ad4a7640 100644 --- a/test/functional/eval/uniq_spec.lua +++ b/test/functional/vimscript/uniq_spec.lua @@ -2,10 +2,9 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq local clear = helpers.clear -local meths = helpers.meths local command = helpers.command local exc_exec = helpers.exc_exec -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err before_each(clear) @@ -24,8 +23,7 @@ describe('uniq()', function() return (a:a > a:b) - (a:a < a:b) endfunction ]]) - eq('\nE745: Using a List as a Number\nE882: Uniq compare function failed', - redir_exec('let fl = uniq([0, 0, [], 1, 1], "Cmp")')) - eq({0, {}, 1, 1}, meths.get_var('fl')) + eq('Vim(let):E745: Using a List as a Number', + pcall_err(command, 'let fl = uniq([0, 0, [], 1, 1], "Cmp")')) end) end) diff --git a/test/functional/eval/vvar_event_spec.lua b/test/functional/vimscript/vvar_event_spec.lua index eec8aa917a..eec8aa917a 100644 --- a/test/functional/eval/vvar_event_spec.lua +++ b/test/functional/vimscript/vvar_event_spec.lua diff --git a/test/functional/eval/wait_spec.lua b/test/functional/vimscript/wait_spec.lua index ee95e02a7f..ee95e02a7f 100644 --- a/test/functional/eval/wait_spec.lua +++ b/test/functional/vimscript/wait_spec.lua diff --git a/test/functional/eval/writefile_spec.lua b/test/functional/vimscript/writefile_spec.lua index 356680ba7c..5f693249a9 100644 --- a/test/functional/eval/writefile_spec.lua +++ b/test/functional/vimscript/writefile_spec.lua @@ -8,7 +8,8 @@ local meths = helpers.meths local exc_exec = helpers.exc_exec local read_file = helpers.read_file local write_file = helpers.write_file -local redir_exec = helpers.redir_exec +local pcall_err = helpers.pcall_err +local command = helpers.command local fname = 'Xtest-functional-eval-writefile' local dname = fname .. '.d' @@ -106,51 +107,51 @@ describe('writefile()', function() it('shows correct file name when supplied numbers', function() meths.set_current_dir(dname) - eq('\nE482: Can\'t open file 2 for writing: illegal operation on a directory', - redir_exec(('call writefile([42], %s)'):format(ddname_tail))) + eq('Vim(call):E482: Can\'t open file 2 for writing: illegal operation on a directory', + pcall_err(command, ('call writefile([42], %s)'):format(ddname_tail))) end) it('errors out with invalid arguments', function() write_file(fname, 'TEST') - eq('\nE119: Not enough arguments for function: writefile', - redir_exec('call writefile()')) - eq('\nE119: Not enough arguments for function: writefile', - redir_exec('call writefile([])')) - eq('\nE118: Too many arguments for function: writefile', - redir_exec(('call writefile([], "%s", "b", 1)'):format(fname))) + eq('Vim(call):E119: Not enough arguments for function: writefile', + pcall_err(command, 'call writefile()')) + eq('Vim(call):E119: Not enough arguments for function: writefile', + pcall_err(command, 'call writefile([])')) + eq('Vim(call):E118: Too many arguments for function: writefile', + pcall_err(command, ('call writefile([], "%s", "b", 1)'):format(fname))) for _, arg in ipairs({'0', '0.0', 'function("tr")', '{}', '"test"'}) do - eq('\nE686: Argument of writefile() must be a List', - redir_exec(('call writefile(%s, "%s", "b")'):format(arg, fname))) + eq('Vim(call):E475: Invalid argument: writefile() first argument must be a List or a Blob', + pcall_err(command, ('call writefile(%s, "%s", "b")'):format(arg, fname))) end for _, args in ipairs({'[], %s, "b"', '[], "' .. fname .. '", %s'}) do - eq('\nE806: using Float as a String', - redir_exec(('call writefile(%s)'):format(args:format('0.0')))) - eq('\nE730: using List as a String', - redir_exec(('call writefile(%s)'):format(args:format('[]')))) - eq('\nE731: using Dictionary as a String', - redir_exec(('call writefile(%s)'):format(args:format('{}')))) - eq('\nE729: using Funcref as a String', - redir_exec(('call writefile(%s)'):format(args:format('function("tr")')))) + eq('Vim(call):E806: using Float as a String', + pcall_err(command, ('call writefile(%s)'):format(args:format('0.0')))) + eq('Vim(call):E730: using List as a String', + pcall_err(command, ('call writefile(%s)'):format(args:format('[]')))) + eq('Vim(call):E731: using Dictionary as a String', + pcall_err(command, ('call writefile(%s)'):format(args:format('{}')))) + eq('Vim(call):E729: using Funcref as a String', + pcall_err(command, ('call writefile(%s)'):format(args:format('function("tr")')))) end - eq('\nE5060: Unknown flag: «»', - redir_exec(('call writefile([], "%s", "bs«»")'):format(fname))) + eq('Vim(call):E5060: Unknown flag: «»', + pcall_err(command, ('call writefile([], "%s", "bs«»")'):format(fname))) eq('TEST', read_file(fname)) end) it('does not write to file if error in list', function() local args = '["tset"] + repeat([%s], 3), "' .. fname .. '"' - eq('\nE805: Expected a Number or a String, Float found', - redir_exec(('call writefile(%s)'):format(args:format('0.0')))) + eq('Vim(call):E805: Expected a Number or a String, Float found', + pcall_err(command, ('call writefile(%s)'):format(args:format('0.0')))) eq(nil, read_file(fname)) write_file(fname, 'TEST') - eq('\nE745: Expected a Number or a String, List found', - redir_exec(('call writefile(%s)'):format(args:format('[]')))) + eq('Vim(call):E745: Expected a Number or a String, List found', + pcall_err(command, ('call writefile(%s)'):format(args:format('[]')))) eq('TEST', read_file(fname)) - eq('\nE728: Expected a Number or a String, Dictionary found', - redir_exec(('call writefile(%s)'):format(args:format('{}')))) + eq('Vim(call):E728: Expected a Number or a String, Dictionary found', + pcall_err(command, ('call writefile(%s)'):format(args:format('{}')))) eq('TEST', read_file(fname)) - eq('\nE703: Expected a Number or a String, Funcref found', - redir_exec(('call writefile(%s)'):format(args:format('function("tr")')))) + eq('Vim(call):E703: Expected a Number or a String, Funcref found', + pcall_err(command, ('call writefile(%s)'):format(args:format('function("tr")')))) eq('TEST', read_file(fname)) end) end) diff --git a/test/functional/visual/meta_key_spec.lua b/test/functional/visual/meta_key_spec.lua deleted file mode 100644 index 11f7203da0..0000000000 --- a/test/functional/visual/meta_key_spec.lua +++ /dev/null @@ -1,22 +0,0 @@ -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 12d9f19187..9ac3904776 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -58,9 +58,9 @@ local check_logs_useless_lines = { --- Invokes `fn` and includes the tail of `logfile` in the error message if it --- fails. --- ---@param logfile Log file, defaults to $NVIM_LOG_FILE or '.nvimlog' ---@param fn Function to invoke ---@param ... Function arguments +---@param logfile Log file, defaults to $NVIM_LOG_FILE or '.nvimlog' +---@param fn Function to invoke +---@param ... Function arguments local function dumplog(logfile, fn, ...) -- module.validate({ -- logfile={logfile,'s',true}, @@ -70,7 +70,7 @@ local function dumplog(logfile, fn, ...) if status == false then logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' local logtail = module.read_nvim_log(logfile) - error(string.format('%s\n%s', rv, logtail)) + error(string.format('%s\n%s', tostring(rv), logtail)) end end function module.eq(expected, actual, context, logfile) @@ -102,8 +102,8 @@ end --- Asserts that `pat` matches one or more lines in the tail of $NVIM_LOG_FILE. --- ---@param pat (string) Lua pattern to search for in the log file. ---@param logfile (string, default=$NVIM_LOG_FILE) full path to log file. +---@param pat (string) Lua pattern to search for in the log file. +---@param logfile (string, default=$NVIM_LOG_FILE) full path to log file. function module.assert_log(pat, logfile) logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' local nrlines = 10 @@ -810,6 +810,6 @@ function module.read_nvim_log(logfile, ci_rename) return log end -module = shared.tbl_extend('error', module, Paths, shared) +module = shared.tbl_extend('error', module, Paths, shared, require('test.deprecated')) return module diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index 891e6def09..5fc3b83a13 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -43,7 +43,8 @@ local function argreset(arg, args) end end -local function test_vim_str2nr(s, what, exp, maxlen) +local function test_vim_str2nr(s, what, exp, maxlen, strict) + if strict == nil then strict = true end local bits = {} for k, _ in pairs(exp) do bits[#bits + 1] = k @@ -62,11 +63,11 @@ local function test_vim_str2nr(s, what, exp, maxlen) cv[k] = args[k] end end - lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen) + lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict) for cck, ccv in pairs(cv) do if exp[cck] ~= tonumber(ccv[0]) then - error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d): %d'):format( - cck, exp[cck], s, tonumber(what), maxlen, tonumber(ccv[0]) + error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format( + cck, exp[cck], s, tonumber(what), maxlen, tostring(strict), tonumber(ccv[0]) )) end end @@ -85,10 +86,13 @@ describe('vim_str2nr()', function() test_vim_str2nr('', lib.STR2NR_ALL, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_DEC, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) end) itp('works with decimal numbers', function() @@ -97,31 +101,39 @@ describe('vim_str2nr()', function() lib.STR2NR_BIN, lib.STR2NR_OCT, lib.STR2NR_HEX, + lib.STR2NR_OOCT, lib.STR2NR_BIN + lib.STR2NR_OCT, lib.STR2NR_BIN + lib.STR2NR_HEX, lib.STR2NR_OCT + lib.STR2NR_HEX, + lib.STR2NR_OOCT + lib.STR2NR_HEX, lib.STR2NR_ALL, lib.STR2NR_FORCE + lib.STR2NR_DEC, }) do -- Check that all digits are recognized test_vim_str2nr( '12345', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0) test_vim_str2nr( '67890', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0) - test_vim_str2nr( '12345A', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0) - test_vim_str2nr( '67890A', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0) + test_vim_str2nr( '12345A', flags, {len = 0}, 0) + test_vim_str2nr( '67890A', flags, {len = 0}, 0) + test_vim_str2nr( '12345A', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0, false) + test_vim_str2nr( '67890A', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0, false) test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0) test_vim_str2nr( '42', flags, {len = 1, num = 4, unum = 4, pre = 0}, 1) test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 2) test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) -- includes NUL byte in maxlen - test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0) - test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) + test_vim_str2nr( '42x', flags, {len = 0}, 0) + test_vim_str2nr( '42x', flags, {len = 0}, 3) + test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0, false) + test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3, false) test_vim_str2nr('-42', flags, {len = 3, num = -42, unum = 42, pre = 0}, 3) test_vim_str2nr('-42', flags, {len = 1, num = 0, unum = 0, pre = 0}, 1) - test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 0) - test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 4) + test_vim_str2nr('-42x', flags, {len = 0}, 0) + test_vim_str2nr('-42x', flags, {len = 0}, 4) + test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 0, false) + test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 4, false) end end) itp('works with binary numbers', function() @@ -144,62 +156,77 @@ describe('vim_str2nr()', function() test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0) test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0b101', flags, {len = 0}, 2) + test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0b101', flags, {len = 3, num = 1, unum = 1, pre = bin}, 3) test_vim_str2nr( '0b101', flags, {len = 4, num = 2, unum = 2, pre = bin}, 4) test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 5) test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6) - test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0) - test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6) + test_vim_str2nr( '0b1012', flags, {len = 0}, 0) + test_vim_str2nr( '0b1012', flags, {len = 0}, 6) + test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0, false) + test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6, false) test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0) test_vim_str2nr('-0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0b101', flags, {len = 0}, 3) + test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0b101', flags, {len = 4, num = -1, unum = 1, pre = bin}, 4) test_vim_str2nr('-0b101', flags, {len = 5, num = -2, unum = 2, pre = bin}, 5) test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 6) test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7) - test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0) - test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7) + test_vim_str2nr('-0b1012', flags, {len = 0}, 0) + test_vim_str2nr('-0b1012', flags, {len = 0}, 7) + test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0, false) + test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7, false) test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0) test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0B101', flags, {len = 0}, 2) + test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0B101', flags, {len = 3, num = 1, unum = 1, pre = BIN}, 3) test_vim_str2nr( '0B101', flags, {len = 4, num = 2, unum = 2, pre = BIN}, 4) test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 5) test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6) - test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0) - test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6) + test_vim_str2nr( '0B1012', flags, {len = 0}, 0) + test_vim_str2nr( '0B1012', flags, {len = 0}, 6) + test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0, false) + test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6, false) test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0) test_vim_str2nr('-0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0B101', flags, {len = 0}, 3) + test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0B101', flags, {len = 4, num = -1, unum = 1, pre = BIN}, 4) test_vim_str2nr('-0B101', flags, {len = 5, num = -2, unum = 2, pre = BIN}, 5) test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 6) test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7) - test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0) - test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7) + test_vim_str2nr('-0B1012', flags, {len = 0}, 0) + test_vim_str2nr('-0B1012', flags, {len = 0}, 7) + test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0, false) + test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7, false) if flags > lib.STR2NR_FORCE then test_vim_str2nr('-101', flags, {len = 4, num = -5, unum = 5, pre = 0}, 0) end end end) - itp('works with octal numbers', function() + itp('works with octal numbers (0 prefix)', function() for _, flags in ipairs({ lib.STR2NR_OCT, lib.STR2NR_OCT + lib.STR2NR_BIN, lib.STR2NR_OCT + lib.STR2NR_HEX, + lib.STR2NR_OCT + lib.STR2NR_OOCT, lib.STR2NR_ALL, lib.STR2NR_FORCE + lib.STR2NR_OCT, + lib.STR2NR_FORCE + lib.STR2NR_OOCT, + lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT, }) do local oct if flags > lib.STR2NR_FORCE then @@ -218,8 +245,10 @@ describe('vim_str2nr()', function() test_vim_str2nr( '0548', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3) test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4) - test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4) - test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0) + test_vim_str2nr( '054x', flags, {len = 0}, 4) + test_vim_str2nr( '054x', flags, {len = 0}, 0) + test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4, false) + test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0, false) test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0) test_vim_str2nr('-054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) @@ -229,13 +258,110 @@ describe('vim_str2nr()', function() test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4) test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5) - test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5) - test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0) + test_vim_str2nr('-054x', flags, {len = 0}, 5) + test_vim_str2nr('-054x', flags, {len = 0}, 0) + test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5, false) + test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0, false) if flags > lib.STR2NR_FORCE then test_vim_str2nr('-54', flags, {len = 3, num = -44, unum = 44, pre = 0}, 0) - test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5) - test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0) + test_vim_str2nr('-0548', flags, {len = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 0}, 0) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5, false) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0, false) + else + test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0) + end + end + end) + itp('works with octal numbers (0o or 0O prefix)', function() + for _, flags in ipairs({ + lib.STR2NR_OOCT, + lib.STR2NR_OOCT + lib.STR2NR_BIN, + lib.STR2NR_OOCT + lib.STR2NR_HEX, + lib.STR2NR_OCT + lib.STR2NR_OOCT, + lib.STR2NR_OCT + lib.STR2NR_OOCT + lib.STR2NR_BIN, + lib.STR2NR_OCT + lib.STR2NR_OOCT + lib.STR2NR_HEX, + lib.STR2NR_ALL, + lib.STR2NR_FORCE + lib.STR2NR_OCT, + lib.STR2NR_FORCE + lib.STR2NR_OOCT, + lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT, + }) do + local oct + local OCT + if flags > lib.STR2NR_FORCE then + oct = 0 + OCT = 0 + else + oct = ('o'):byte() + OCT = ('O'):byte() + end + + test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 0) + test_vim_str2nr( '0o054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0o054', flags, {len = 0}, 2) + test_vim_str2nr( '0o054', flags, {len = 3, num = 0, unum = 0, pre = oct}, 3) + test_vim_str2nr( '0o054', flags, {len = 4, num = 5, unum = 5, pre = oct}, 4) + test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 5) + test_vim_str2nr( '0o0548', flags, {len = 5, num = 44, unum = 44, pre = oct}, 5) + test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 6) + + test_vim_str2nr( '0o054x', flags, {len = 0}, 6) + test_vim_str2nr( '0o054x', flags, {len = 0}, 0) + test_vim_str2nr( '0o054x', flags, {len = 5, num = 44, unum = 44, pre = oct}, 6, false) + test_vim_str2nr( '0o054x', flags, {len = 5, num = 44, unum = 44, pre = oct}, 0, false) + + test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 0) + test_vim_str2nr('-0o054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0o054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0o054', flags, {len = 0}, 3) + test_vim_str2nr('-0o054', flags, {len = 4, num = 0, unum = 0, pre = oct}, 4) + test_vim_str2nr('-0o054', flags, {len = 5, num = -5, unum = 5, pre = oct}, 5) + test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 6) + test_vim_str2nr('-0o0548', flags, {len = 6, num = -44, unum = 44, pre = oct}, 6) + test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 7) + + test_vim_str2nr('-0o054x', flags, {len = 0}, 7) + test_vim_str2nr('-0o054x', flags, {len = 0}, 0) + test_vim_str2nr('-0o054x', flags, {len = 6, num = -44, unum = 44, pre = oct}, 7, false) + test_vim_str2nr('-0o054x', flags, {len = 6, num = -44, unum = 44, pre = oct}, 0, false) + + test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 0) + test_vim_str2nr( '0O054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0O054', flags, {len = 0}, 2) + test_vim_str2nr( '0O054', flags, {len = 3, num = 0, unum = 0, pre = OCT}, 3) + test_vim_str2nr( '0O054', flags, {len = 4, num = 5, unum = 5, pre = OCT}, 4) + test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 5) + test_vim_str2nr( '0O0548', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 5) + test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 6) + + test_vim_str2nr( '0O054x', flags, {len = 0}, 6) + test_vim_str2nr( '0O054x', flags, {len = 0}, 0) + test_vim_str2nr( '0O054x', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 6, false) + test_vim_str2nr( '0O054x', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 0, false) + + test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 0) + test_vim_str2nr('-0O054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0O054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0O054', flags, {len = 0}, 3) + test_vim_str2nr('-0O054', flags, {len = 4, num = 0, unum = 0, pre = OCT}, 4) + test_vim_str2nr('-0O054', flags, {len = 5, num = -5, unum = 5, pre = OCT}, 5) + test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 6) + test_vim_str2nr('-0O0548', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 6) + test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 7) + + test_vim_str2nr('-0O054x', flags, {len = 0}, 7) + test_vim_str2nr('-0O054x', flags, {len = 0}, 0) + test_vim_str2nr('-0O054x', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 7, false) + test_vim_str2nr('-0O054x', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 0, false) + + if flags > lib.STR2NR_FORCE then + test_vim_str2nr('-0548', flags, {len = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 0}, 0) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5, false) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0, false) + test_vim_str2nr('-055', flags, {len = 4, num = -45, unum = 45, pre = 0}, 0) else test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5) test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0) @@ -268,53 +394,85 @@ describe('vim_str2nr()', function() test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 0) test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0x101', flags, {len = 0}, 2) + test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0x101', flags, {len = 3, num = 1, unum = 1, pre = hex}, 3) test_vim_str2nr( '0x101', flags, {len = 4, num = 16, unum = 16, pre = hex}, 4) test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 5) test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 6) - test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 0) - test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 6) + test_vim_str2nr( '0x101G', flags, {len = 0}, 0) + test_vim_str2nr( '0x101G', flags, {len = 0}, 6) + test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 0, false) + test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 6, false) test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 0) test_vim_str2nr('-0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0x101', flags, {len = 0}, 3) + test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0x101', flags, {len = 4, num = -1, unum = 1, pre = hex}, 4) test_vim_str2nr('-0x101', flags, {len = 5, num = -16, unum = 16, pre = hex}, 5) test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 6) test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 7) - test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 0) - test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 7) + test_vim_str2nr('-0x101G', flags, {len = 0}, 0) + test_vim_str2nr('-0x101G', flags, {len = 0}, 7) + test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 0, false) + test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 7, false) test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0) test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0X101', flags, {len = 0}, 2) + test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0X101', flags, {len = 3, num = 1, unum = 1, pre = HEX}, 3) test_vim_str2nr( '0X101', flags, {len = 4, num = 16, unum = 16, pre = HEX}, 4) test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 5) test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6) - test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0) - test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6) + test_vim_str2nr( '0X101G', flags, {len = 0}, 0) + test_vim_str2nr( '0X101G', flags, {len = 0}, 6) + test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0, false) + test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6, false) test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0) test_vim_str2nr('-0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0X101', flags, {len = 0}, 3) + test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0X101', flags, {len = 4, num = -1, unum = 1, pre = HEX}, 4) test_vim_str2nr('-0X101', flags, {len = 5, num = -16, unum = 16, pre = HEX}, 5) test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 6) test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7) - test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0) - test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7) + test_vim_str2nr('-0X101G', flags, {len = 0}, 0) + test_vim_str2nr('-0X101G', flags, {len = 0}, 7) + test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0, false) + test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7, false) if flags > lib.STR2NR_FORCE then test_vim_str2nr('-101', flags, {len = 4, num = -257, unum = 257, pre = 0}, 0) end end end) + -- Test_str2nr() in test_functions.vim already tests normal usage + itp('works with weirdly quoted numbers', function() + local flags = lib.STR2NR_DEC + lib.STR2NR_QUOTE + test_vim_str2nr("'027", flags, {len = 0}, 0) + test_vim_str2nr("'027", flags, {len = 0}, 0, false) + test_vim_str2nr("1'2'3'4", flags, {len = 7, num = 1234, unum = 1234, pre = 0}, 0) + + -- counter-intuitive, but like Vim, strict=true should partially accept + -- these: (' and - are not alpha-numeric) + test_vim_str2nr("7''331", flags, {len = 1, num = 7, unum = 7, pre = 0}, 0) + test_vim_str2nr("123'x4", flags, {len = 3, num = 123, unum = 123, pre = 0}, 0) + test_vim_str2nr("1337'", flags, {len = 4, num = 1337, unum = 1337, pre = 0}, 0) + test_vim_str2nr("-'", flags, {len = 1, num = 0, unum = 0, pre = 0}, 0) + + flags = lib.STR2NR_HEX + lib.STR2NR_QUOTE + local hex = ('x'):byte() + test_vim_str2nr("0x'abcd", flags, {len = 0}, 0) + test_vim_str2nr("0x'abcd", flags, {len = 1, num = 0, unum = 0, pre = 0}, 0, false) + test_vim_str2nr("0xab''cd", flags, {len = 4, num = 171, unum = 171, pre = hex}, 0) + end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index d81e272877..e61b568f3a 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2531,7 +2531,7 @@ describe('typval.c', function() value='tr', dict={}, }) - lib.tv_item_lock(p_tv, -1, true) + lib.tv_item_lock(p_tv, -1, true, false) eq(lib.VAR_UNLOCKED, p_tv.vval.v_partial.pt_dict.dv_lock) end) itp('does not change VAR_FIXED values', function() @@ -2542,14 +2542,14 @@ describe('typval.c', function() d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED l_tv.v_lock = lib.VAR_FIXED l_tv.vval.v_list.lv_lock = lib.VAR_FIXED - lib.tv_item_lock(d_tv, 1, true) - lib.tv_item_lock(l_tv, 1, true) + lib.tv_item_lock(d_tv, 1, true, false) + lib.tv_item_lock(l_tv, 1, true, false) eq(lib.VAR_FIXED, d_tv.v_lock) eq(lib.VAR_FIXED, l_tv.v_lock) eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock) - lib.tv_item_lock(d_tv, 1, false) - lib.tv_item_lock(l_tv, 1, false) + lib.tv_item_lock(d_tv, 1, false, false) + lib.tv_item_lock(l_tv, 1, false, false) eq(lib.VAR_FIXED, d_tv.v_lock) eq(lib.VAR_FIXED, l_tv.v_lock) eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) @@ -2561,9 +2561,9 @@ describe('typval.c', function() local d_tv = lua2typvalt(null_dict) local s_tv = lua2typvalt(null_string) alloc_log:clear() - lib.tv_item_lock(l_tv, 1, true) - lib.tv_item_lock(d_tv, 1, true) - lib.tv_item_lock(s_tv, 1, true) + lib.tv_item_lock(l_tv, 1, true, false) + lib.tv_item_lock(d_tv, 1, true, false) + lib.tv_item_lock(s_tv, 1, true, false) eq(null_list, typvalt2lua(l_tv)) eq(null_dict, typvalt2lua(d_tv)) eq(null_string, typvalt2lua(s_tv)) diff --git a/test/unit/undo_spec.lua b/test/unit/undo_spec.lua index 616c6fbe3d..f7f8d26d58 100644 --- a/test/unit/undo_spec.lua +++ b/test/unit/undo_spec.lua @@ -38,7 +38,7 @@ child_call_once(function() -- -- compute a hash for this undofile buffer_hash = ffi.new('char_u[32]') - undo.u_compute_hash(buffer_hash) + undo.u_compute_hash(file_buffer, buffer_hash) end) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 032baf6578..8342044b5e 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -52,6 +52,32 @@ local predefined_hl_defs = { QuickFixLine=true, Substitute=true, Whitespace=true, + Error=true, + Todo=true, + String=true, + Character=true, + Number=true, + Boolean=true, + Float=true, + Function=true, + Conditional=true, + Repeat=true, + Label=true, + Operator=true, + Keyword=true, + Exception=true, + Include=true, + Define=true, + Macro=true, + PreCondit=true, + StorageClass=true, + Structure=true, + Typedef=true, + Tag=true, + SpecialChar=true, + Delimiter=true, + SpecialComment=true, + Debug=true, -- From highlight_init_(dark|light) ColorColumn=true, @@ -83,8 +109,6 @@ local predefined_hl_defs = { Visual=true, WarningMsg=true, Normal=true, - - -- From syncolor.vim, if &background Comment=true, Constant=true, Special=true, @@ -94,36 +118,6 @@ local predefined_hl_defs = { Type=true, Underlined=true, Ignore=true, - - -- From syncolor.vim, below if &background - Error=true, - Todo=true, - - -- From syncolor.vim, links at the bottom - String=true, - Character=true, - Number=true, - Boolean=true, - Float=true, - Function=true, - Conditional=true, - Repeat=true, - Label=true, - Operator=true, - Keyword=true, - Exception=true, - Include=true, - Define=true, - Macro=true, - PreCondit=true, - StorageClass=true, - Structure=true, - Typedef=true, - Tag=true, - SpecialChar=true, - Delimiter=true, - SpecialComment=true, - Debug=true, } local nvim_hl_defs = {} |