diff options
Diffstat (limited to 'test/functional')
104 files changed, 5070 insertions, 1125 deletions
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index 890710b6e6..f19d7a362b 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -114,8 +114,8 @@ describe('nvim_create_user_command', function() ]] eq({ - args = [[this is a\ test]], - fargs = {"this", "is", "a test"}, + args = [[this\ is a\ test]], + fargs = {"this ", "is", "a test"}, bang = false, line1 = 1, line2 = 1, @@ -125,6 +125,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -135,7 +136,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -144,7 +145,7 @@ describe('nvim_create_user_command', function() count = 2, reg = "", }, exec_lua [=[ - vim.api.nvim_command([[CommandWithLuaCallback this is a\ test]]) + vim.api.nvim_command([[CommandWithLuaCallback this\ is a\ test]]) return result ]=]) @@ -160,6 +161,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -170,7 +172,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -195,6 +197,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -205,7 +208,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -224,12 +227,13 @@ describe('nvim_create_user_command', function() bang = true, line1 = 10, line2 = 10, - mods = "confirm unsilent botright", + mods = "confirm unsilent botright horizontal", smods = { browse = false, confirm = true, emsg_silent = false, hide = false, + horizontal = true, keepalt = false, keepjumps = false, keepmarks = false, @@ -240,7 +244,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "botright", - tab = 0, + tab = -1, unsilent = true, verbose = -1, vertical = false, @@ -249,7 +253,7 @@ describe('nvim_create_user_command', function() count = 10, reg = "", }, exec_lua [=[ - vim.api.nvim_command('unsilent botright confirm 10CommandWithLuaCallback! h\tey ') + vim.api.nvim_command('unsilent horizontal botright confirm 10CommandWithLuaCallback! h\tey ') return result ]=]) @@ -265,6 +269,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -275,7 +280,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -300,6 +305,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -310,7 +316,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -347,6 +353,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -357,7 +364,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -383,6 +390,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -393,7 +401,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -415,6 +423,7 @@ describe('nvim_create_user_command', function() nargs = 0, bang = true, count = 2, + register = true, }) ]] eq({ @@ -429,6 +438,7 @@ describe('nvim_create_user_command', function() confirm = false, emsg_silent = false, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -439,7 +449,7 @@ describe('nvim_create_user_command', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -451,6 +461,42 @@ describe('nvim_create_user_command', function() vim.cmd('CommandWithNoArgs') return result ]]) + -- register can be specified + eq({ + args = "", + fargs = {}, + bang = false, + line1 = 1, + line2 = 1, + mods = "", + smods = { + browse = false, + confirm = false, + emsg_silent = false, + hide = false, + horizontal = false, + keepalt = false, + keepjumps = false, + keepmarks = false, + keeppatterns = false, + lockmarks = false, + noautocmd = false, + noswapfile = false, + sandbox = false, + silent = false, + split = "", + tab = -1, + unsilent = false, + verbose = -1, + vertical = false, + }, + range = 0, + count = 2, + reg = "+", + }, exec_lua [[ + vim.cmd('CommandWithNoArgs +') + return result + ]]) end) @@ -514,8 +560,29 @@ describe('nvim_create_user_command', function() vim.api.nvim_cmd({ cmd = 'echo', args = { '&verbose' }, mods = opts.smods }, {}) end, {}) ]] - eq("3", meths.cmd({ cmd = 'MyEcho', mods = { verbose = 3 } }, { output = true })) + + eq(1, #meths.list_tabpages()) + exec_lua[[ + vim.api.nvim_create_user_command('MySplit', function(opts) + vim.api.nvim_cmd({ cmd = 'split', mods = opts.smods }, {}) + end, {}) + ]] + meths.cmd({ cmd = 'MySplit' }, {}) + eq(1, #meths.list_tabpages()) + eq(2, #meths.list_wins()) + meths.cmd({ cmd = 'MySplit', mods = { tab = 1 } }, {}) + eq(2, #meths.list_tabpages()) + eq(2, funcs.tabpagenr()) + meths.cmd({ cmd = 'MySplit', mods = { tab = 1 } }, {}) + eq(3, #meths.list_tabpages()) + eq(2, funcs.tabpagenr()) + meths.cmd({ cmd = 'MySplit', mods = { tab = 3 } }, {}) + eq(4, #meths.list_tabpages()) + eq(4, funcs.tabpagenr()) + meths.cmd({ cmd = 'MySplit', mods = { tab = 0 } }, {}) + eq(5, #meths.list_tabpages()) + eq(1, funcs.tabpagenr()) end) end) diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index bc8d811c6d..00f5b25b8a 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -1046,7 +1046,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[3], 0, 4, 0, 8) end) - it('substitions over multiple lines with newline in pattern', function() + it('substitutes over multiple lines with newline in pattern', function() feed('A<cr>67890<cr>xx<esc>') set_extmark(ns, marks[1], 0, 3) set_extmark(ns, marks[2], 0, 4) @@ -1078,7 +1078,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[6], 1, 2, 0, 5) end) - it('substitions with multiple newlines in pattern', function() + it('substitutes with multiple newlines in pattern', function() feed('A<cr>67890<cr>xx<esc>') set_extmark(ns, marks[1], 0, 4) set_extmark(ns, marks[2], 0, 5) @@ -1093,7 +1093,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[5], 2, 0, 0, 6) end) - it('substitions over multiple lines with replace in substition', function() + it('substitutes over multiple lines with replace in substitution', function() feed('A<cr>67890<cr>xx<esc>') set_extmark(ns, marks[1], 0, 1) set_extmark(ns, marks[2], 0, 2) @@ -1111,7 +1111,7 @@ describe('API/extmarks', function() eq({1, 3}, get_extmark_by_id(ns, marks[3])) end) - it('substitions over multiple lines with replace in substition', function() + it('substitutes over multiple lines with replace in substitution', function() feed('A<cr>x3<cr>xx<esc>') set_extmark(ns, marks[1], 1, 0) set_extmark(ns, marks[2], 1, 1) @@ -1122,7 +1122,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[3], 1, 2, 2, 0) end) - it('substitions over multiple lines with replace in substition', function() + it('substitutes over multiple lines with replace in substitution', function() feed('A<cr>x3<cr>xx<esc>') set_extmark(ns, marks[1], 0, 1) set_extmark(ns, marks[2], 0, 2) @@ -1140,7 +1140,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[3], 0, 4, 1, 3) end) - it('substitions with newline in match and sub, delta is 0', function() + it('substitutes with newline in match and sub, delta is 0', function() feed('A<cr>67890<cr>xx<esc>') set_extmark(ns, marks[1], 0, 3) set_extmark(ns, marks[2], 0, 4) @@ -1157,7 +1157,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[6], 2, 0, 2, 0) end) - it('substitions with newline in match and sub, delta > 0', function() + it('substitutes with newline in match and sub, delta > 0', function() feed('A<cr>67890<cr>xx<esc>') set_extmark(ns, marks[1], 0, 3) set_extmark(ns, marks[2], 0, 4) @@ -1174,7 +1174,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[6], 2, 0, 3, 0) end) - it('substitions with newline in match and sub, delta < 0', function() + it('substitutes with newline in match and sub, delta < 0', function() feed('A<cr>67890<cr>xx<cr>xx<esc>') set_extmark(ns, marks[1], 0, 3) set_extmark(ns, marks[2], 0, 4) @@ -1193,7 +1193,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[7], 3, 0, 2, 0) end) - it('substitions with backrefs, newline inserted into sub', function() + it('substitutes with backrefs, newline inserted into sub', function() feed('A<cr>67890<cr>xx<cr>xx<esc>') set_extmark(ns, marks[1], 0, 3) set_extmark(ns, marks[2], 0, 4) @@ -1210,7 +1210,7 @@ describe('API/extmarks', function() check_undo_redo(ns, marks[6], 2, 0, 3, 0) end) - it('substitions a ^', function() + it('substitutes a ^', function() set_extmark(ns, marks[1], 0, 0) set_extmark(ns, marks[2], 0, 1) feed([[:s:^:x<cr>]]) @@ -1397,7 +1397,7 @@ describe('API/extmarks', function() eq({{id, 1, 0}}, bufmeths.get_extmarks(buf, ns, 0, -1, {})) end) - it('does not crash with append/delete/undo seqence', function() + it('does not crash with append/delete/undo sequence', function() meths.exec([[ let ns = nvim_create_namespace('myplugin') call nvim_buf_set_extmark(0, ns, 0, 0, {}) diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 2730f7e23d..3b36563d21 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -354,4 +354,9 @@ describe("API: set highlight", function() meths.set_hl(0, 'Normal', {fg='#000083', bg='#0000F3'}) eq({foreground = 131, background = 243}, nvim("get_hl_by_name", 'Normal', true)) end) + + it('does not segfault on invalid group name #20009', function() + eq('Invalid highlight name: foo bar', pcall_err(meths.set_hl, 0, 'foo bar', {bold = true})) + assert_alive() + end) end) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index a93a4544ff..30c351b26a 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -6,6 +6,7 @@ local command = helpers.command local curbufmeths = helpers.curbufmeths local eq, neq = helpers.eq, helpers.neq local exec_lua = helpers.exec_lua +local exec = helpers.exec local feed = helpers.feed local funcs = helpers.funcs local meths = helpers.meths @@ -336,21 +337,26 @@ describe('nvim_get_keymap', function() end) it('can handle lua mappings', function() - eq(0, exec_lua [[ + eq(0, exec_lua([[ GlobalCount = 0 - vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) return GlobalCount - ]]) + ]])) feed('asdf\n') - eq(1, exec_lua[[return GlobalCount]]) + eq(1, exec_lua([[return GlobalCount]])) - eq(2, exec_lua[[ + eq(2, exec_lua([[ vim.api.nvim_get_keymap('n')[1].callback() return GlobalCount + ]])) + + exec([[ + call nvim_get_keymap('n')[0].callback() ]]) + eq(3, exec_lua([[return GlobalCount]])) + local mapargs = meths.get_keymap('n') - assert(type(mapargs[1].callback) == 'number', 'callback is not luaref number') mapargs[1].callback = nil eq({ lhs='asdf', @@ -834,17 +840,29 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end) it ('maparg() returns lua mapping correctly', function() - exec_lua [[ - vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end }) - ]] - assert.truthy(string.match(funcs.maparg('asdf', 'n'), - "^<Lua %d+>")) + eq(0, exec_lua([[ + GlobalCount = 0 + vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]])) + + assert.truthy(string.match(funcs.maparg('asdf', 'n'), "^<Lua %d+>")) + local mapargs = funcs.maparg('asdf', 'n', false, true) - assert(type(mapargs.callback) == 'number', 'callback is not luaref number') mapargs.callback = nil mapargs.lhsraw = nil mapargs.lhsrawalt = nil eq(generate_mapargs('n', 'asdf', nil, {sid=sid_lua}), mapargs) + + eq(1, exec_lua([[ + vim.fn.maparg('asdf', 'n', false, true).callback() + return GlobalCount + ]])) + + exec([[ + call maparg('asdf', 'n', v:false, v:true).callback() + ]]) + eq(2, exec_lua([[return GlobalCount]])) end) it('can make lua expr mappings replacing keycodes', function() @@ -867,7 +885,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() eq({'<space>'}, meths.buf_get_lines(0, 0, -1, false)) end) - it('lua expr mapping returning nil is equivalent to returnig an empty string', function() + it('lua expr mapping returning nil is equivalent to returning an empty string', function() exec_lua [[ vim.api.nvim_set_keymap ('i', 'aa', '', {callback = function() return nil end, expr = true }) ]] diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 1c00f001ff..1c554b05a3 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -7,6 +7,7 @@ local exec_lua = helpers.exec_lua local retry = helpers.retry local isCI = helpers.isCI local assert_alive = helpers.assert_alive +local uname = helpers.uname describe('notify', function() local channel @@ -78,6 +79,9 @@ describe('notify', function() end) it('cancels stale events on channel close', function() + if uname() == 'freebsd' then + pending('Failing FreeBSD test') + end if isCI() then pending('hangs on CI #14083 #15251') return diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 72a03c409a..af6cee7e90 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local lfs = require('lfs') +local luv = require('luv') local fmt = string.format local assert_alive = helpers.assert_alive @@ -14,6 +15,7 @@ local funcs = helpers.funcs local iswin = helpers.iswin local meths = helpers.meths local matches = helpers.matches +local pesc = helpers.pesc local mkdir_p = helpers.mkdir_p local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed local is_os = helpers.is_os @@ -1903,11 +1905,32 @@ describe('API', function() end) end) + describe('nvim_out_write', function() + it('prints long messages correctly #20534', function() + exec([[ + set more + redir => g:out + silent! call nvim_out_write('a') + silent! call nvim_out_write('a') + silent! call nvim_out_write('a') + silent! call nvim_out_write("\n") + silent! call nvim_out_write('a') + silent! call nvim_out_write('a') + silent! call nvim_out_write(repeat('a', 5000) .. "\n") + silent! call nvim_out_write('a') + silent! call nvim_out_write('a') + silent! call nvim_out_write('a') + silent! call nvim_out_write("\n") + redir END + ]]) + eq('\naaa\n' .. ('a'):rep(5002) .. '\naaa', meths.get_var('out')) + end) + end) + describe('nvim_err_write', function() local screen before_each(function() - clear() screen = Screen.new(40, 8) screen:attach() screen:set_default_attr_ids({ @@ -2254,7 +2277,7 @@ 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 + -- 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,,') @@ -2732,8 +2755,8 @@ describe('API', function() it('should have information about window options', function() eq({ - allows_duplicates = true, - commalist = false; + allows_duplicates = false, + commalist = true; default = ""; flaglist = false; global_local = false; @@ -3176,9 +3199,6 @@ describe('API', function() cmd = 'echo', args = { 'foo' }, bang = false, - range = {}, - count = -1, - reg = '', addr = 'none', magic = { file = false, @@ -3195,6 +3215,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3205,7 +3226,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3218,8 +3239,6 @@ describe('API', function() args = { '/math.random/math.max/' }, bang = false, range = { 4, 6 }, - count = -1, - reg = '', addr = 'line', magic = { file = false, @@ -3236,6 +3255,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3246,7 +3266,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3260,7 +3280,6 @@ describe('API', function() bang = false, range = { 1 }, count = 1, - reg = '', addr = 'buf', magic = { file = false, @@ -3277,6 +3296,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3287,7 +3307,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3300,7 +3320,6 @@ describe('API', function() args = {}, bang = false, range = {}, - count = -1, reg = '+', addr = 'line', magic = { @@ -3318,6 +3337,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3328,12 +3348,51 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, } }, meths.parse_cmd('put +', {})) + eq({ + cmd = 'put', + args = {}, + bang = false, + range = {}, + reg = '', + addr = 'line', + magic = { + file = false, + bar = true + }, + nargs = '0', + nextcmd = '', + mods = { + browse = false, + confirm = false, + emsg_silent = false, + filter = { + pattern = "", + force = false + }, + hide = false, + horizontal = false, + keepalt = false, + keepjumps = false, + keepmarks = false, + keeppatterns = false, + lockmarks = false, + noautocmd = false, + noswapfile = false, + sandbox = false, + silent = false, + split = "", + tab = -1, + unsilent = false, + verbose = -1, + vertical = false, + } + }, meths.parse_cmd('put', {})) end) it('works with range, count and register', function() eq({ @@ -3359,6 +3418,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3369,7 +3429,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3382,8 +3442,6 @@ describe('API', function() args = {}, bang = true, range = {}, - count = -1, - reg = '', addr = 'line', magic = { file = true, @@ -3400,6 +3458,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3410,7 +3469,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3423,8 +3482,6 @@ describe('API', function() args = { 'foo.txt' }, bang = false, range = {}, - count = -1, - reg = '', addr = '?', magic = { file = true, @@ -3441,6 +3498,7 @@ describe('API', function() force = false }, hide = false, + horizontal = true, keepalt = false, keepjumps = false, keepmarks = false, @@ -3451,19 +3509,17 @@ describe('API', function() sandbox = false, silent = true, split = "topleft", - tab = 2, + tab = 1, unsilent = false, verbose = 15, vertical = false, }, - }, meths.parse_cmd('15verbose silent! aboveleft topleft tab filter /foo/ split foo.txt', {})) + }, meths.parse_cmd('15verbose silent! horizontal topleft tab filter /foo/ split foo.txt', {})) eq({ cmd = 'split', args = { 'foo.txt' }, bang = false, range = {}, - count = -1, - reg = '', addr = '?', magic = { file = true, @@ -3480,6 +3536,7 @@ describe('API', function() force = true }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3495,7 +3552,7 @@ describe('API', function() verbose = 0, vertical = false, }, - }, meths.parse_cmd('0verbose unsilent botright confirm filter! /foo/ split foo.txt', {})) + }, meths.parse_cmd('0verbose unsilent botright 0tab confirm filter! /foo/ split foo.txt', {})) end) it('works with user commands', function() command('command -bang -nargs=+ -range -addr=lines MyCommand echo foo') @@ -3504,8 +3561,6 @@ describe('API', function() args = { 'test', 'it' }, bang = true, range = { 4, 6 }, - count = -1, - reg = '', addr = 'line', magic = { file = false, @@ -3522,6 +3577,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3532,7 +3588,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3545,8 +3601,6 @@ describe('API', function() args = { 'a.txt' }, bang = false, range = {}, - count = -1, - reg = '', addr = 'arg', magic = { file = true, @@ -3563,6 +3617,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3573,7 +3628,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3586,9 +3641,6 @@ describe('API', function() cmd = 'MyCommand', args = { 'test it' }, bang = false, - range = {}, - count = -1, - reg = '', addr = 'none', magic = { file = false, @@ -3605,6 +3657,7 @@ describe('API', function() force = false }, hide = false, + horizontal = false, keepalt = false, keepjumps = false, keepmarks = false, @@ -3615,7 +3668,7 @@ describe('API', function() sandbox = false, silent = false, split = "", - tab = 0, + tab = -1, unsilent = false, verbose = -1, vertical = false, @@ -3668,6 +3721,59 @@ describe('API', function() :^ | ]]) end) + it('does not move cursor or change search history/pattern #19878 #19890', function() + meths.buf_set_lines(0, 0, -1, true, {'foo', 'bar', 'foo', 'bar'}) + eq({1, 0}, meths.win_get_cursor(0)) + eq('', funcs.getreg('/')) + eq('', funcs.histget('search')) + feed(':') -- call the API in cmdline mode to test whether it changes search history + eq({ + cmd = 'normal', + args = {'x'}, + bang = true, + range = {3, 4}, + addr = 'line', + magic = { + file = false, + bar = false, + }, + nargs = '+', + nextcmd = '', + mods = { + browse = false, + confirm = false, + emsg_silent = false, + filter = { + pattern = "", + force = false, + }, + hide = false, + horizontal = false, + keepalt = false, + keepjumps = false, + keepmarks = false, + keeppatterns = false, + lockmarks = false, + noautocmd = false, + noswapfile = false, + sandbox = false, + silent = false, + split = "", + tab = -1, + unsilent = false, + verbose = -1, + vertical = false, + } + }, meths.parse_cmd('+2;/bar/normal! x', {})) + eq({1, 0}, meths.win_get_cursor(0)) + eq('', funcs.getreg('/')) + eq('', funcs.histget('search')) + end) + it('result can be used directly by nvim_cmd #20051', function() + eq("foo", meths.cmd(meths.parse_cmd('echo "foo"', {}), { output = true })) + meths.cmd(meths.parse_cmd("set cursorline", {}), {}) + eq(true, meths.get_option_value("cursorline", {})) + end) end) describe('nvim_cmd', function() it('works', function () @@ -3749,10 +3855,20 @@ describe('API', function() eq('1', meths.cmd({ cmd = 'echomsg', args = { '1' }, mods = { silent = true } }, { output = true })) - -- with :silent message isn't added to message history + -- but message isn't added to message history eq('', meths.cmd({ cmd = 'messages' }, { output = true })) meths.create_user_command("Foo", 'set verbose', {}) eq(" verbose=1", meths.cmd({ cmd = "Foo", mods = { verbose = 1 } }, { output = true })) + meths.create_user_command("Mods", "echo '<mods>'", {}) + eq('keepmarks keeppatterns silent 3verbose aboveleft horizontal', + meths.cmd({ cmd = "Mods", mods = { + horizontal = true, + keepmarks = true, + keeppatterns = true, + silent = true, + split = 'aboveleft', + verbose = 3, + } }, { output = true })) eq(0, meths.get_option_value("verbose", {})) command('edit foo.txt | edit bar.txt') eq(' 1 #h "foo.txt" line 1', @@ -3841,11 +3957,23 @@ describe('API', function() eq({'aa'}, meths.buf_get_lines(0, 0, 1, false)) assert_alive() end) + it('supports filename expansion', function() + meths.cmd({ cmd = 'argadd', args = { '%:p:h:t', '%:p:h:t' } }, {}) + local arg = funcs.expand('%:p:h:t') + eq({ arg, arg }, funcs.argv()) + end) it("'make' command works when argument count isn't 1 #19696", function() command('set makeprg=echo') - meths.cmd({ cmd = 'make' }, {}) + command('set shellquote=') + matches('^:!echo ', + meths.cmd({ cmd = 'make' }, { output = true })) + assert_alive() + matches('^:!echo foo bar', + meths.cmd({ cmd = 'make', args = { 'foo', 'bar' } }, { output = true })) assert_alive() - meths.cmd({ cmd = 'make', args = { 'foo', 'bar' } }, {}) + local arg_pesc = pesc(funcs.expand('%:p:h:t')) + matches(('^:!echo %s %s'):format(arg_pesc, arg_pesc), + meths.cmd({ cmd = 'make', args = { '%:p:h:t', '%:p:h:t' } }, { output = true })) assert_alive() end) it('doesn\'t display messages when output=true', function() @@ -3878,5 +4006,23 @@ describe('API', function() 15 | ]]} end) + it('works with non-String args', function() + eq('2', meths.cmd({cmd = 'echo', args = {2}}, {output = true})) + eq('1', meths.cmd({cmd = 'echo', args = {true}}, {output = true})) + end) + describe('first argument as count', function() + before_each(clear) + + it('works', function() + command('vsplit | enew') + meths.cmd({cmd = 'bdelete', args = {meths.get_current_buf()}}, {}) + eq(1, meths.get_current_buf().id) + end) + it('works with :sleep using milliseconds', function() + local start = luv.now() + meths.cmd({cmd = 'sleep', args = {'100m'}}, {}) + ok(luv.now() - start <= 300) + end) + end) end) end) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 901d24327c..7c65cf9c37 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -15,25 +15,6 @@ 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) - local slen = string.len(str) - local nlines = eval("&lines") - for i = 1,nlines do - local iseq = true - for j = 1,slen do - if string.byte(str,j) ~= eval("screenchar("..i..","..j..")") then - iseq = false - break - end - end - if iseq then - return true - end - end - return false -end - describe('API/win', function() before_each(clear) @@ -79,27 +60,61 @@ describe('API/win', function() end) it('updates the screen, and also when the window is unfocused', function() + local screen = Screen.new(30, 9) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue}, + [2] = {bold = true, reverse = true}; + [3] = {reverse = true}; + }) + screen:attach() + insert("prologue") feed('100o<esc>') insert("epilogue") local win = curwin() feed('gg') - poke_eventloop() -- let nvim process the 'gg' command + screen:expect{grid=[[ + ^prologue | + | + | + | + | + | + | + | + | + ]]} -- cursor position is at beginning eq({1, 0}, window('get_cursor', win)) - eq(true, is_visible("prologue")) - eq(false, is_visible("epilogue")) -- move cursor to end window('set_cursor', win, {101, 0}) - eq(false, is_visible("prologue")) - eq(true, is_visible("epilogue")) + screen:expect{grid=[[ + | + | + | + | + | + | + | + ^epilogue | + | + ]]} -- move cursor to the beginning again window('set_cursor', win, {1, 0}) - eq(true, is_visible("prologue")) - eq(false, is_visible("epilogue")) + screen:expect{grid=[[ + ^prologue | + | + | + | + | + | + | + | + | + ]]} -- move focus to new window nvim('command',"new") @@ -107,18 +122,45 @@ describe('API/win', function() -- sanity check, cursor position is kept eq({1, 0}, window('get_cursor', win)) - eq(true, is_visible("prologue")) - eq(false, is_visible("epilogue")) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {2:[No Name] }| + prologue | + | + | + {3:[No Name] [+] }| + | + ]]} -- move cursor to end window('set_cursor', win, {101, 0}) - eq(false, is_visible("prologue")) - eq(true, is_visible("epilogue")) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {2:[No Name] }| + | + | + epilogue | + {3:[No Name] [+] }| + | + ]]} -- move cursor to the beginning again window('set_cursor', win, {1, 0}) - eq(true, is_visible("prologue")) - eq(false, is_visible("epilogue")) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {2:[No Name] }| + prologue | + | + | + {3:[No Name] [+] }| + | + ]]} -- curwin didn't change back neq(win, curwin()) diff --git a/test/functional/autocmd/cursorhold_spec.lua b/test/functional/autocmd/cursorhold_spec.lua index 506b688853..b04bd5233a 100644 --- a/test/functional/autocmd/cursorhold_spec.lua +++ b/test/functional/autocmd/cursorhold_spec.lua @@ -2,30 +2,82 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local eq = helpers.eq -local eval = helpers.eval local feed = helpers.feed local retry = helpers.retry -local source = helpers.source +local exec = helpers.source local sleep = helpers.sleep +local meths = helpers.meths -describe('CursorHoldI', function() - before_each(clear) +before_each(clear) + +describe('CursorHold', function() + before_each(function() + exec([[ + let g:cursorhold = 0 + augroup test + au CursorHold * let g:cursorhold += 1 + augroup END + ]]) + end) + + it('is triggered correctly #12587', function() + local function test_cursorhold(fn, early) + local ut = 2 + -- if testing with small 'updatetime' fails, double its value and test again + retry(10, nil, function() + ut = ut * 2 + meths.set_option('updatetime', ut) + feed('0') -- reset did_cursorhold + meths.set_var('cursorhold', 0) + sleep(ut / 4) + fn() + eq(0, meths.get_var('cursorhold')) + sleep(ut / 2) + fn() + eq(0, meths.get_var('cursorhold')) + sleep(ut / 2) + eq(early, meths.get_var('cursorhold')) + sleep(ut / 4 * 3) + eq(1, meths.get_var('cursorhold')) + end) + end + local ignore_key = meths.replace_termcodes('<Ignore>', true, true, true) + test_cursorhold(function() end, 1) + test_cursorhold(function() feed('') end, 1) + test_cursorhold(function() meths.feedkeys('', 'n', true) end, 1) + test_cursorhold(function() feed('<Ignore>') end, 0) + test_cursorhold(function() meths.feedkeys(ignore_key, 'n', true) end, 0) + end) + + it("reducing 'updatetime' while waiting for CursorHold #20241", function() + meths.set_option('updatetime', 10000) + feed('0') -- reset did_cursorhold + meths.set_var('cursorhold', 0) + sleep(50) + eq(0, meths.get_var('cursorhold')) + meths.set_option('updatetime', 20) + sleep(10) + eq(1, meths.get_var('cursorhold')) + end) +end) + +describe('CursorHoldI', function() -- NOTE: since this test uses RPC it is not necessary to trigger the initial -- issue (#3757) via timer's or RPC callbacks in the first place. it('is triggered after input', function() - source([[ - set updatetime=1 + exec([[ + set updatetime=1 - let g:cursorhold = 0 - augroup test - au CursorHoldI * let g:cursorhold += 1 - augroup END + let g:cursorhold = 0 + augroup test + au CursorHoldI * let g:cursorhold += 1 + augroup END ]]) feed('ifoo') retry(5, nil, function() sleep(1) - eq(1, eval('g:cursorhold')) + eq(1, meths.get_var('cursorhold')) end) end) end) diff --git a/test/functional/autocmd/winscrolled_spec.lua b/test/functional/autocmd/winscrolled_spec.lua index 5c1b758961..12b8e7c42d 100644 --- a/test/functional/autocmd/winscrolled_spec.lua +++ b/test/functional/autocmd/winscrolled_spec.lua @@ -61,6 +61,20 @@ describe('WinScrolled', function() eq(3, eval('g:scrolled')) end) + it('is triggered by scrolling on a long wrapped line #19968', function() + local height = meths.win_get_height(0) + local width = meths.win_get_width(0) + meths.buf_set_lines(0, 0, -1, true, {('foo'):rep(height * width)}) + meths.win_set_cursor(0, {1, height * width - 1}) + eq(0, eval('g:scrolled')) + feed('gj') + eq(1, eval('g:scrolled')) + feed('0') + eq(2, eval('g:scrolled')) + feed('$') + eq(3, eval('g:scrolled')) + end) + it('is triggered when the window scrolls in Insert mode', function() local height = meths.win_get_height(0) local lines = {} diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua index a4d22685e8..d1ff5b8036 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -23,6 +23,9 @@ local iswin = helpers.iswin local assert_alive = helpers.assert_alive local expect_exit = helpers.expect_exit local write_file = helpers.write_file +local Screen = require('test.functional.ui.screen') +local feed_command = helpers.feed_command +local uname = helpers.uname describe('fileio', function() before_each(function() @@ -35,6 +38,7 @@ describe('fileio', function() os.remove('Xtest_startup_file2') os.remove('Xtest_тест.md') os.remove('Xtest-u8-int-max') + os.remove('Xtest-overwrite-forced') rmdir('Xtest_startup_swapdir') rmdir('Xtest_backupdir') end) @@ -83,6 +87,9 @@ describe('fileio', function() end) it('backup #9709', function() + if uname() == 'freebsd' then + pending('Failing FreeBSD test') + end clear({ args={ '-i', 'Xtest_startup_shada', '--cmd', 'set directory=Xtest_startup_swapdir' } }) @@ -102,6 +109,9 @@ describe('fileio', function() end) it('backup with full path #11214', function() + if uname() == 'freebsd' then + pending('Failing FreeBSD test') + end clear() mkdir('Xtest_backupdir') command('set backup') @@ -144,6 +154,61 @@ describe('fileio', function() command('edit ++enc=utf32 Xtest-u8-int-max') assert_alive() end) + + it(':w! does not show "file has been changed" warning', function() + clear() + write_file("Xtest-overwrite-forced", 'foobar') + command('set nofixendofline') + local screen = Screen.new(40,4) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4} + }) + screen:attach() + command("set shortmess-=F") + + command("e Xtest-overwrite-forced") + screen:expect([[ + ^foobar | + {1:~ }| + {1:~ }| + "Xtest-overwrite-forced" [noeol] 1L, 6B | + ]]) + + -- Get current unix time. + local cur_unix_time = os.time(os.date("!*t")) + local future_time = cur_unix_time + 999999 + -- Set the file's access/update time to be + -- greater than the time at which it was created. + local uv = require("luv") + uv.fs_utime('Xtest-overwrite-forced', future_time, future_time) + -- use async feed_command because nvim basically hangs on the prompt + feed_command("w") + screen:expect([[ + {2:WARNING: The file has been changed since}| + {2: reading it!!!} | + {3:Do you really want to write to it (y/n)^?}| + | + ]]) + + feed("n") + feed("<cr>") + screen:expect([[ + ^foobar | + {1:~ }| + {1:~ }| + | + ]]) + -- Use a screen test because the warning does not set v:errmsg. + command("w!") + screen:expect([[ + ^foobar | + {1:~ }| + {1:~ }| + <erwrite-forced" [noeol] 1L, 6B written | + ]]) + end) end) describe('tmpdir', function() diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index 669bc99136..61fae7622c 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -4,6 +4,8 @@ local eq = helpers.eq local eval = helpers.eval local command = helpers.command local iswin = helpers.iswin +local insert = helpers.insert +local feed = helpers.feed describe('path collapse', function() local targetdir @@ -54,3 +56,15 @@ describe('path collapse', function() eq(expected_path, eval('expand("%:p")')) end) end) + +describe('file search', function() + before_each(clear) + + it('find multibyte file name in line #20517', function() + command('cd test/functional/fixtures') + insert('filename_with_unicode_ααα') + eq('', eval('expand("%")')) + feed('gf') + eq('filename_with_unicode_ααα', eval('expand("%:t")')) + end) +end) diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua index d7bd075eb2..846d79abf3 100644 --- a/test/functional/core/remote_spec.lua +++ b/test/functional/core/remote_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local command = helpers.command local eq = helpers.eq +local exec_lua = helpers.exec_lua local expect = helpers.expect local funcs = helpers.funcs local insert = helpers.insert @@ -48,8 +49,8 @@ describe('Remote', function() -- our incoming --remote calls. local client_starter = spawn(new_argv(), false, nil, true) set_session(client_starter) - local client_job_id = funcs.jobstart(client_argv) - eq({ 0 }, funcs.jobwait({client_job_id})) + -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness. + eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], client_argv)) client_starter:close() set_session(server) end @@ -121,8 +122,8 @@ describe('Remote', function() -- the event loop. If the server event loop is blocked, it can't process -- our incoming --remote calls. clear() - local bogus_job_id = funcs.jobstart(bogus_argv) - eq({2}, funcs.jobwait({bogus_job_id})) + -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness. + eq({ 2 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], bogus_argv)) end it('bogus subcommand', function() run_and_check_exit_code('--remote-bogus') diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 4f9df4010e..a32c801c97 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -354,7 +354,9 @@ describe('startup', function() local function pack_clear(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/'}} + 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/'}, + args_rm={'runtimepath'}, + } end diff --git a/test/functional/editor/K_spec.lua b/test/functional/editor/K_spec.lua index 8ad81ac3d6..3b5580540f 100644 --- a/test/functional/editor/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, retry = - helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.retry +local eq, clear, eval, feed, meths, retry = + helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.meths, helpers.retry describe('K', function() local test_file = 'K_spec_out' @@ -58,4 +58,11 @@ describe('K', function() helpers.neq(bufnr, eval('bufnr()')) end) + it('empty string falls back to :help #19298', function() + meths.set_option('keywordprg', '') + meths.buf_set_lines(0, 0, -1, true, {'doesnotexist'}) + feed('K') + eq('E149: Sorry, no help for doesnotexist', meths.get_vvar('errmsg')) + end) + end) diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua index e3d3cdbd85..cd51a65be3 100644 --- a/test/functional/editor/mode_insert_spec.lua +++ b/test/functional/editor/mode_insert_spec.lua @@ -6,12 +6,20 @@ local expect = helpers.expect local command = helpers.command local eq = helpers.eq local eval = helpers.eval +local curbuf_contents = helpers.curbuf_contents describe('insert-mode', function() before_each(function() clear() end) + it('indents only once after "!" keys #12894', function() + command('let counter = []') + command('set indentexpr=len(add(counter,0))') + feed('i<C-F>x') + eq(' x', curbuf_contents()) + end) + it('CTRL-@', function() -- Inserts last-inserted text, leaves insert-mode. insert('hello') diff --git a/test/functional/editor/tabpage_spec.lua b/test/functional/editor/tabpage_spec.lua index 849a02c28b..f8ca6986bd 100644 --- a/test/functional/editor/tabpage_spec.lua +++ b/test/functional/editor/tabpage_spec.lua @@ -9,6 +9,9 @@ local feed = helpers.feed local eval = helpers.eval local exec = helpers.exec local funcs = helpers.funcs +local meths = helpers.meths +local curwin = helpers.curwin +local assert_alive = helpers.assert_alive describe('tabpage', function() before_each(clear) @@ -54,6 +57,45 @@ describe('tabpage', function() neq(999, eval('g:win_closed')) end) + it('no segfault with strange WinClosed autocommand #20290', function() + pcall(exec, [[ + set nohidden + edit Xa + split Xb + tab split + new + autocmd WinClosed * tabprev | bwipe! + close + ]]) + assert_alive() + end) + + it('nvim_win_close and nvim_win_hide update tabline #20285', function() + eq(1, #meths.list_tabpages()) + eq({1, 1}, funcs.win_screenpos(0)) + local win1 = curwin().id + + command('tabnew') + eq(2, #meths.list_tabpages()) + eq({2, 1}, funcs.win_screenpos(0)) + local win2 = curwin().id + + meths.win_close(win1, true) + eq(win2, curwin().id) + eq(1, #meths.list_tabpages()) + eq({1, 1}, funcs.win_screenpos(0)) + + command('tabnew') + eq(2, #meths.list_tabpages()) + eq({2, 1}, funcs.win_screenpos(0)) + local win3 = curwin().id + + meths.win_hide(win2) + eq(win3, curwin().id) + eq(1, #meths.list_tabpages()) + eq({1, 1}, funcs.win_screenpos(0)) + end) + it('switching tabpage after setting laststatus=3 #19591', function() local screen = Screen.new(40, 8) screen:set_default_attr_ids({ diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 21adcf37da..afa6b519d5 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -4,6 +4,7 @@ local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source local insert = helpers.insert local eq, next_msg = helpers.eq, helpers.next_msg local exc_exec = helpers.exc_exec +local exec_lua = helpers.exec_lua local command = helpers.command local eval = helpers.eval @@ -21,6 +22,8 @@ describe('VimL dictionary notifications', function() -- t:) and a dictionary variable, so we generate them in the following -- function. local function gentests(dict_expr, dict_init) + local is_g = dict_expr == 'g:' + local function update(opval, key) if not key then key = 'watched' @@ -32,6 +35,28 @@ describe('VimL dictionary notifications', function() end end + local function update_with_api(opval, key) + if not key then + key = 'watched' + end + if opval == '' then + exec_lua(('vim.api.nvim_del_var(\'%s\')'):format(key)) + else + exec_lua(('vim.api.nvim_set_var(\'%s\', %s)'):format(key, opval)) + end + end + + local function update_with_vim_g(opval, key) + if not key then + key = 'watched' + end + if opval == '' then + exec_lua(('vim.g.%s = nil'):format(key)) + else + exec_lua(('vim.g.%s %s'):format(key, opval)) + end + end + local function verify_echo() -- helper to verify that no notifications are sent after certain change -- to a dict @@ -76,6 +101,18 @@ describe('VimL dictionary notifications', function() update('', 'watched2') update('') verify_echo() + if is_g then + update_with_api('"test"') + update_with_api('"test2"', 'watched2') + update_with_api('', 'watched2') + update_with_api('') + verify_echo() + update_with_vim_g('= "test"') + update_with_vim_g('= "test2"', 'watched2') + update_with_vim_g('', 'watched2') + update_with_vim_g('') + verify_echo() + end end) it('is not triggered when unwatched keys are updated', function() @@ -83,6 +120,16 @@ describe('VimL dictionary notifications', function() update('.= "noop2"', 'unwatched') update('', 'unwatched') verify_echo() + if is_g then + update_with_api('"noop"', 'unwatched') + update_with_api('vim.g.unwatched .. "noop2"', 'unwatched') + update_with_api('', 'unwatched') + verify_echo() + update_with_vim_g('= "noop"', 'unwatched') + update_with_vim_g('= vim.g.unwatched .. "noop2"', 'unwatched') + update_with_vim_g('', 'unwatched') + verify_echo() + end end) it('is triggered by remove()', function() @@ -92,6 +139,22 @@ describe('VimL dictionary notifications', function() verify_value({old = 'test'}) end) + if is_g then + it('is triggered by remove() when updated with nvim_*_var', function() + update_with_api('"test"') + verify_value({new = 'test'}) + nvim('command', 'call remove('..dict_expr..', "watched")') + verify_value({old = 'test'}) + end) + + it('is triggered by remove() when updated with vim.g', function() + update_with_vim_g('= "test"') + verify_value({new = 'test'}) + nvim('command', 'call remove('..dict_expr..', "watched")') + verify_value({old = 'test'}) + end) + end + it('is triggered by extend()', function() update('= "xtend"') verify_value({new = 'xtend'}) diff --git a/test/functional/ex_cmds/normal_spec.lua b/test/functional/ex_cmds/normal_spec.lua index f6e7dd2b3a..009f1d6516 100644 --- a/test/functional/ex_cmds/normal_spec.lua +++ b/test/functional/ex_cmds/normal_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local command = helpers.command +local funcs = helpers.funcs local feed = helpers.feed local expect = helpers.expect local eq = helpers.eq @@ -8,20 +9,30 @@ local eval = helpers.eval before_each(clear) -describe(':normal', function() +describe(':normal!', function() it('can get out of Insert mode if called from Ex mode #17924', function() feed('gQnormal! Ifoo<CR>') expect('foo') end) - it('normal! does not execute command in Ex mode when running out of characters', function() + it('does not execute command in Ex mode when running out of characters', function() command('let g:var = 0') command('normal! gQlet g:var = 1') eq(0, eval('g:var')) end) - it('normal! gQinsert does not hang #17980', function() + it('gQinsert does not hang #17980', function() command('normal! gQinsert') expect('') end) + + it('can stop Visual mode without closing cmdwin vim-patch:9.0.0234', function() + feed('q:') + feed('v') + eq('v', funcs.mode(1)) + eq(':', funcs.getcmdwintype()) + command('normal! \027') + eq('n', funcs.mode(1)) + eq(':', funcs.getcmdwintype()) + end) end) diff --git a/test/functional/ex_cmds/quit_spec.lua b/test/functional/ex_cmds/quit_spec.lua index fe138a24c8..3680801dae 100644 --- a/test/functional/ex_cmds/quit_spec.lua +++ b/test/functional/ex_cmds/quit_spec.lua @@ -7,7 +7,7 @@ describe(':qa', function() end) it('verify #3334', function() - -- just testing if 'qa' passed as a program argument wont result in memory + -- just testing if 'qa' passed as a program argument won't result in memory -- errors end) end) diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index 163ded43f9..10ebefd8cd 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -104,7 +104,7 @@ describe(':source', function() eq("0zBEEFCAFE", meths.exec('echo d', true)) exec('set cpoptions+=C') - eq('Vim(let):E15: Invalid expression: #{', exc_exec('source')) + eq('Vim(let):E723: Missing end of Dictionary \'}\': ', exc_exec('source')) end) it('selection in current buffer', function() @@ -138,7 +138,7 @@ describe(':source', function() eq('Vim(echo):E117: Unknown function: s:C', exc_exec('echo D()')) exec('set cpoptions+=C') - eq('Vim(let):E15: Invalid expression: #{', exc_exec("'<,'>source")) + eq('Vim(let):E723: Missing end of Dictionary \'}\': ', exc_exec("'<,'>source")) end) it('does not break if current buffer is modified while sourced', function() @@ -166,6 +166,7 @@ describe(':source', function() vim.g.sourced_lua = 1 vim.g.sfile_value = vim.fn.expand('<sfile>') vim.g.stack_value = vim.fn.expand('<stack>') + vim.g.script_value = vim.fn.expand('<script>') ]]) command('set shellslash') @@ -173,6 +174,7 @@ describe(':source', function() eq(1, eval('g:sourced_lua')) matches([[/test%.lua$]], meths.get_var('sfile_value')) matches([[/test%.lua$]], meths.get_var('stack_value')) + matches([[/test%.lua$]], meths.get_var('script_value')) os.remove(test_file) end) @@ -214,6 +216,7 @@ describe(':source', function() "\ 2]=] vim.g.sfile_value = vim.fn.expand('<sfile>') vim.g.stack_value = vim.fn.expand('<stack>') + vim.g.script_value = vim.fn.expand('<script>') ]]) command('edit '..test_file) @@ -223,6 +226,7 @@ describe(':source', function() eq(' \\ 1\n "\\ 2', exec_lua('return _G.a')) eq(':source (no file)', meths.get_var('sfile_value')) eq(':source (no file)', meths.get_var('stack_value')) + eq(':source (no file)', meths.get_var('script_value')) 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 4d984af41e..69404039ff 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -1,8 +1,8 @@ local Screen = require('test.functional.ui.screen') 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 eq, eval, expect, exec = + helpers.eq, helpers.eval, helpers.expect, helpers.exec local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command @@ -10,6 +10,8 @@ local feed = helpers.feed local nvim_prog = helpers.nvim_prog local ok = helpers.ok local rmdir = helpers.rmdir +local new_argv = helpers.new_argv +local pesc = helpers.pesc local os_kill = helpers.os_kill local set_session = helpers.set_session local spawn = helpers.spawn @@ -55,11 +57,11 @@ describe(':preserve', function() set swapfile fileformat=unix undolevels=-1 ]] - source(init) + exec(init) command('edit! '..testfile) feed('isometext<esc>') command('preserve') - source('redir => g:swapname | silent swapname | redir END') + exec('redir => g:swapname | silent swapname | redir END') local swappath1 = eval('g:swapname') @@ -69,12 +71,12 @@ describe(':preserve', function() true) set_session(nvim2) - source(init) + exec(init) -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. command('autocmd SwapExists * let v:swapchoice = "r"') command('silent edit! '..testfile) - source('redir => g:swapname | silent swapname | redir END') + exec('redir => g:swapname | silent swapname | redir END') local swappath2 = eval('g:swapname') @@ -92,25 +94,28 @@ end) describe('swapfile detection', function() local swapdir = lfs.currentdir()..'/Xtest_swapdialog_dir' + local nvim0 + -- Put swapdir at the start of the 'directory' list. #1836 + -- Note: `set swapfile` *must* go after `set directory`: otherwise it may + -- attempt to create a swapfile in different directory. + local init = [[ + set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[// + set swapfile fileformat=unix undolevels=-1 hidden + ]] before_each(function() - clear() + nvim0 = spawn(new_argv()) + set_session(nvim0) rmdir(swapdir) lfs.mkdir(swapdir) end) after_each(function() + set_session(nvim0) command('%bwipeout!') rmdir(swapdir) end) it('always show swapfile dialog #8840 #9027', function() local testfile = 'Xtest_swapdialog_file1' - -- Put swapdir at the start of the 'directory' list. #1836 - -- Note: `set swapfile` *must* go after `set directory`: otherwise it may - -- attempt to create a swapfile in different directory. - local init = [[ - set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[// - set swapfile fileformat=unix undolevels=-1 hidden - ]] local expected_no_dialog = '^'..(' '):rep(256)..'|\n' for _=1,37 do @@ -119,19 +124,17 @@ describe('swapfile detection', function() expected_no_dialog = expected_no_dialog..testfile..(' '):rep(216)..'0,0-1 All|\n' expected_no_dialog = expected_no_dialog..(' '):rep(256)..'|\n' - source(init) + exec(init) command('edit! '..testfile) feed('isometext<esc>') command('preserve') - os_kill(eval('getpid()')) -- Start another Nvim instance. - local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, - true) + local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, true, nil, true) set_session(nvim2) local screen2 = Screen.new(256, 40) screen2:attach() - source(init) + exec(init) -- With shortmess+=F command('set shortmess+=F') @@ -176,5 +179,88 @@ describe('swapfile detection', function() } }) feed('<cr>') + + nvim2:close() + end) + + -- oldtest: Test_swap_prompt_splitwin() + it('selecting "q" in the attention prompt', function() + exec(init) + command('edit Xfile1') + command('preserve') -- should help to make sure the swap file exists + + local screen = Screen.new(75, 18) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + }) + + local nvim1 = spawn(new_argv(), true, nil, true) + set_session(nvim1) + screen:attach() + exec(init) + feed(':split Xfile1\n') + screen:expect({ + any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^') + }) + feed('q') + feed(':<CR>') + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + : | + ]]) + nvim1:close() + + local nvim2 = spawn(new_argv(), true, nil, true) + set_session(nvim2) + screen:attach() + exec(init) + command('set more') + command('au bufadd * let foo_w = wincol()') + feed(':e Xfile1<CR>') + screen:expect({any = pesc('{1:-- More --}^')}) + feed('<Space>') + screen:expect({ + any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^') + }) + feed('q') + command([[echo 'hello']]) + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + hello | + ]]) + nvim2:close() end) end) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index 32fe397c03..14035a4341 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -9,6 +9,7 @@ local feed_command = helpers.feed_command local funcs = helpers.funcs local meths = helpers.meths local iswin = helpers.iswin +local uname = helpers.uname local fname = 'Xtest-functional-ex_cmds-write' local fname_bak = fname .. '~' @@ -52,6 +53,9 @@ describe(':write', function() end) it('&backupcopy=no replaces symlink with new file', function() + if uname() == 'freebsd' then + pending('Failing FreeBSD test') + end command('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') if iswin() then @@ -91,6 +95,9 @@ describe(':write', function() end) it('errors out correctly', function() + if uname() == 'freebsd' then + pending('Failing FreeBSD test') + end command('let $HOME=""') eq(funcs.fnamemodify('.', ':p:h'), funcs.fnamemodify('.', ':p:h:~')) -- Message from check_overwrite diff --git a/test/functional/fixtures/api_level_10.mpack b/test/functional/fixtures/api_level_10.mpack Binary files differnew file mode 100644 index 0000000000..2a1f51045d --- /dev/null +++ b/test/functional/fixtures/api_level_10.mpack diff --git a/test/functional/fixtures/filename_with_unicode_ααα b/test/functional/fixtures/filename_with_unicode_ααα new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/functional/fixtures/filename_with_unicode_ααα diff --git a/test/functional/fixtures/printenv-test.c b/test/functional/fixtures/printenv-test.c index 0e68129543..295b4f04c3 100644 --- a/test/functional/fixtures/printenv-test.c +++ b/test/functional/fixtures/printenv-test.c @@ -3,13 +3,13 @@ #include <stdio.h> -#ifdef WIN32 +#ifdef MSWIN # include <windows.h> #else # include <stdlib.h> #endif -#ifdef WIN32 +#ifdef MSWIN int wmain(int argc, wchar_t **argv) #else int main(int argc, char **argv) @@ -19,7 +19,7 @@ int main(int argc, char **argv) return 1; } -#ifdef WIN32 +#ifdef MSWIN wchar_t *value = _wgetenv(argv[1]); if (value == NULL) { return 1; diff --git a/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua b/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua index a312572c5b..45226ce24b 100644 --- a/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua +++ b/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua @@ -1,3 +1,5 @@ return function (val, res) - vim.loop.new_async(function() _G[res] = require'leftpad'(val) end):send() + local handle + handle = vim.loop.new_async(function() _G[res] = require'leftpad'(val) handle:close() end) + handle:send() end diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c index 4438b73a22..6ee7715021 100644 --- a/test/functional/fixtures/tty-test.c +++ b/test/functional/fixtures/tty-test.c @@ -5,7 +5,7 @@ #include <stdio.h> #include <stdlib.h> #include <uv.h> -#ifdef _WIN32 +#ifdef MSWIN # include <windows.h> #else # include <unistd.h> @@ -23,7 +23,7 @@ uv_tty_t tty_out; bool owns_tty(void); // silence -Wmissing-prototypes bool owns_tty(void) { -#ifdef _WIN32 +#ifdef MSWIN // XXX: We need to make proper detect owns tty // HWND consoleWnd = GetConsoleWindow(); // DWORD dwProcessId; @@ -38,14 +38,14 @@ bool owns_tty(void) static void walk_cb(uv_handle_t *handle, void *arg) { if (!uv_is_closing(handle)) { -#ifdef WIN32 +#ifdef MSWIN uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL); #endif uv_close(handle, NULL); } } -#ifndef WIN32 +#ifndef MSWIN static void sig_handler(int signum) { switch (signum) { @@ -64,7 +64,7 @@ static void sig_handler(int signum) } #endif -#ifdef WIN32 +#ifdef MSWIN static void sigwinch_cb(uv_signal_t *handle, int signum) { int width, height; @@ -102,7 +102,7 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) uv_write_t req; uv_buf_t b = { .base = buf->base, -#ifdef WIN32 +#ifdef MSWIN .len = (ULONG)cnt #else .len = (size_t)cnt @@ -171,7 +171,7 @@ int main(int argc, char **argv) uv_prepare_t prepare; uv_prepare_init(uv_default_loop(), &prepare); uv_prepare_start(&prepare, prepare_cb); -#ifndef WIN32 +#ifndef MSWIN uv_tty_init(uv_default_loop(), &tty, fileno(stderr), 1); #else uv_tty_init(uv_default_loop(), &tty, fileno(stdin), 1); @@ -182,7 +182,7 @@ int main(int argc, char **argv) uv_tty_set_mode(&tty, UV_TTY_MODE_RAW); tty.data = &interrupted; uv_read_start(STRUCT_CAST(uv_stream_t, &tty), alloc_cb, read_cb); -#ifndef WIN32 +#ifndef MSWIN struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; @@ -196,7 +196,7 @@ int main(int argc, char **argv) #endif uv_run(uv_default_loop(), UV_RUN_DEFAULT); -#ifndef WIN32 +#ifndef MSWIN // XXX: Without this the SIGHUP handler is skipped on some systems. sleep(100); #endif diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 981cfc306e..723d2ccfa4 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -29,6 +29,7 @@ local module = { } local start_dir = lfs.currentdir() +local runtime_set = 'set runtimepath^=./build/lib/nvim/' module.nvim_prog = ( os.getenv('NVIM_PRG') or global_helpers.test_build_dir .. '/bin/nvim' @@ -40,6 +41,8 @@ module.nvim_set = ( ..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid') module.nvim_argv = { module.nvim_prog, '-u', 'NONE', '-i', 'NONE', + -- XXX: find treesitter parsers. + '--cmd', runtime_set, '--cmd', module.nvim_set, '--cmd', 'mapclear', '--cmd', 'mapclear!', @@ -272,7 +275,6 @@ function module.command(cmd) module.request('nvim_command', cmd) end - -- Use for commands which expect nvim to quit. -- The first argument can also be a timeout. function module.expect_exit(fn_or_timeout, ...) @@ -345,14 +347,17 @@ end -- Removes Nvim startup args from `args` matching items in `args_rm`. -- --- "-u", "-i", "--cmd" are treated specially: their "values" are also removed. +-- - Special case: "-u", "-i", "--cmd" are treated specially: their "values" are also removed. +-- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', } +-- -- Example: -- args={'--headless', '-u', 'NONE'} -- args_rm={'--cmd', '-u'} -- Result: -- {'--headless'} -- --- All cases are removed. +-- All matching cases are removed. +-- -- Example: -- args={'--cmd', 'foo', '-N', '--cmd', 'bar'} -- args_rm={'--cmd', '-u'} @@ -373,6 +378,9 @@ local function remove_args(args, args_rm) last = '' elseif tbl_contains(args_rm, arg) then last = arg + elseif arg == runtime_set and tbl_contains(args_rm, 'runtimepath') then + table.remove(new_args) -- Remove the preceding "--cmd". + last = '' else table.insert(new_args, arg) end @@ -380,10 +388,23 @@ local function remove_args(args, args_rm) return new_args end +function module.check_close(old_session) + local start_time = luv.now() + old_session:close() + luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). + local end_time = luv.now() + local delta = end_time - start_time + if delta > 500 then + print("nvim took " .. delta .. " milliseconds to exit after last test\n".. + "This indicates a likely problem with the test even if it passed!\n") + io.stdout:flush() + end +end + --- @param io_extra used for stdin_fd, see :help ui-option function module.spawn(argv, merge, env, keep, io_extra) if session and not keep then - session:close() + module.check_close(session) end local child_stream = ChildProcessStream.spawn( @@ -540,16 +561,16 @@ function module.set_shell_powershell(fake) assert(found) end local shell = found and (iswin() and 'powershell' or 'pwsh') or module.testprg('pwsh-test') - local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;' + local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.UTF8Encoding]::new();' local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin() - and {'alias:cat', 'alias:echo', 'alias:sleep'} + and {'alias:cat', 'alias:echo', 'alias:sleep', 'alias:sort'} or {'alias:echo'}, ',')..';' module.exec([[ let &shell = ']]..shell..[[' set shellquote= shellxquote= let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ]]..cmd..[[' let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' - let &shellredir = '-RedirectStandardOutput %s -NoNewWindow -Wait' + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' ]]) return found end @@ -755,15 +776,6 @@ function module.pending_win32(pending_fn) end end -function module.pending_c_parser(pending_fn) - local status, _ = unpack(module.exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]])) - if not status then - pending_fn 'no C parser, skipping' - return true - end - return false -end - -- Calls pending() and returns `true` if the system is too slow to -- run fragile or expensive tests. Else returns `false`. function module.skip_fragile(pending_fn, cond) diff --git a/test/functional/legacy/006_argument_list_spec.lua b/test/functional/legacy/006_argument_list_spec.lua deleted file mode 100644 index d269bf8ec9..0000000000 --- a/test/functional/legacy/006_argument_list_spec.lua +++ /dev/null @@ -1,85 +0,0 @@ --- Test for autocommand that redefines the argument list, when doing ":all". - -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, dedent, eq = helpers.command, helpers.dedent, helpers.eq -local curbuf_contents = helpers.curbuf_contents -local poke_eventloop = helpers.poke_eventloop - -describe('argument list', function() - setup(clear) - - it('is working', function() - insert([[ - start of test file Xxx - this is a test - this is a test - this is a test - this is a test - end of test file Xxx]]) - poke_eventloop() - - command('au BufReadPost Xxx2 next Xxx2 Xxx1') - command('/^start of') - - -- Write test file Xxx1 - feed('A1<Esc>:.,/end of/w! Xxx1<cr>') - - -- Write test file Xxx2 - feed('$r2:.,/end of/w! Xxx2<cr>') - - -- Write test file Xxx3 - feed('$r3:.,/end of/w! Xxx3<cr>') - poke_eventloop() - - -- Redefine arglist; go to Xxx1 - command('next! Xxx1 Xxx2 Xxx3') - - -- Open window for all args - command('all') - - -- Write contents of Xxx1 - command('%yank A') - - -- Append contents of last window (Xxx1) - feed('') - poke_eventloop() - command('%yank A') - - -- should now be in Xxx2 - command('rew') - - -- Append contents of Xxx2 - command('%yank A') - - command('%d') - command('0put=@a') - command('$d') - - eq(dedent([[ - start of test file Xxx1 - this is a test - this is a test - this is a test - this is a test - end of test file Xxx - start of test file Xxx1 - this is a test - this is a test - this is a test - this is a test - end of test file Xxx - start of test file Xxx2 - this is a test - this is a test - this is a test - this is a test - end of test file Xxx]]), curbuf_contents()) - end) - - teardown(function() - os.remove('Xxx1') - os.remove('Xxx2') - os.remove('Xxx3') - end) -end) diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua index 15dbd05cf5..e88afd9c47 100644 --- a/test/functional/legacy/030_fileformats_spec.lua +++ b/test/functional/legacy/030_fileformats_spec.lua @@ -255,7 +255,7 @@ describe('fileformats option', function() -- Assert buffer contents. This has to be done manually as -- helpers.expect() calls helpers.dedent() which messes up the white space - -- and carrige returns. + -- and carriage returns. eq( 'unix\n'.. 'unix\n'.. diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua index 4d71a526c1..c0ff3ed17a 100644 --- a/test/functional/legacy/055_list_and_dict_types_spec.lua +++ b/test/functional/legacy/055_list_and_dict_types_spec.lua @@ -907,7 +907,7 @@ describe('list and dictionary types', function() feed('o<C-R>=a<CR><esc>') feed_command('lang C') feed_command('redir => a') - -- The test failes if this is not in one line. + -- The test fails if this is not in one line. feed_command("try|foobar|catch|let a = matchstr(v:exception,'^[^ ]*')|endtry") feed_command('redir END') feed('o<C-R>=a<CR><esc>') diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index d164d9c02f..235a826640 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -3,10 +3,8 @@ local helpers = require('test.functional.helpers')(after_each) 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 clear, command = helpers.clear, helpers.command local insert = helpers.insert -local pcall_err = helpers.pcall_err describe('063: Test for ":match", "matchadd()" and related functions', function() setup(clear) @@ -19,105 +17,9 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( [1] = {background = Screen.colors.Red}, }) - -- Check that "matcharg()" returns the correct group and pattern if a match - -- is defined. command("highlight MyGroup1 term=bold ctermbg=red guibg=red") command("highlight MyGroup2 term=italic ctermbg=green guibg=green") command("highlight MyGroup3 term=underline ctermbg=blue guibg=blue") - command("match MyGroup1 /TODO/") - command("2match MyGroup2 /FIXME/") - command("3match MyGroup3 /XXX/") - eq({'MyGroup1', 'TODO'}, eval('matcharg(1)')) - eq({'MyGroup2', 'FIXME'}, eval('matcharg(2)')) - eq({'MyGroup3', 'XXX'}, eval('matcharg(3)')) - - -- Check that "matcharg()" returns an empty list if the argument is not 1, - -- 2 or 3 (only 0 and 4 are tested). - eq({}, eval('matcharg(0)')) - eq({}, eval('matcharg(4)')) - - -- Check that "matcharg()" returns ['', ''] if a match is not defined. - command("match") - command("2match") - command("3match") - eq({'', ''}, eval('matcharg(1)')) - eq({'', ''}, eval('matcharg(2)')) - eq({'', ''}, eval('matcharg(3)')) - - -- Check that "matchadd()" and "getmatches()" agree on added matches and - -- that default values apply. - command("let m1 = matchadd('MyGroup1', 'TODO')") - command("let m2 = matchadd('MyGroup2', 'FIXME', 42)") - command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") - eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 4}, - {group = 'MyGroup2', pattern = 'FIXME', priority = 42, id = 5}, - {group = 'MyGroup3', pattern = 'XXX', priority = 60, id = 17}}, - eval('getmatches()')) - - -- Check that "matchdelete()" deletes the matches defined in the previous - -- test correctly. - command("call matchdelete(m1)") - command("call matchdelete(m2)") - command("call matchdelete(m3)") - eq({}, eval('getmatches()')) - - --- Check that "matchdelete()" returns 0 if successful and otherwise -1. - command("let m = matchadd('MyGroup1', 'TODO')") - eq(0, eval('matchdelete(m)')) - - -- matchdelete throws error and returns -1 on failure - neq(true, pcall(function() eval('matchdelete(42)') end)) - 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()". - command("let m1 = matchadd('MyGroup1', 'TODO')") - command("let m2 = matchadd('MyGroup2', 'FIXME', 42)") - command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") - command("match MyGroup1 /COFFEE/") - command("2match MyGroup2 /HUMPPA/") - command("3match MyGroup3 /VIM/") - command("call clearmatches()") - eq({}, eval('getmatches()')) - - -- Check that "setmatches()" restores a list of matches saved by - -- "getmatches()" without changes. (Matches with equal priority must also - -- remain in the same order.) - command("let m1 = matchadd('MyGroup1', 'TODO')") - command("let m2 = matchadd('MyGroup2', 'FIXME', 42)") - command("let m3 = matchadd('MyGroup3', 'XXX', 60, 17)") - command("match MyGroup1 /COFFEE/") - command("2match MyGroup2 /HUMPPA/") - command("3match MyGroup3 /VIM/") - command("let ml = getmatches()") - local ml = eval("ml") - command("call clearmatches()") - command("call setmatches(ml)") - eq(ml, eval('getmatches()')) - - -- Check that "setmatches()" can correctly restore the matches from matchaddpos() - command("call clearmatches()") - command("call setmatches(ml)") - eq(ml, eval('getmatches()')) - - -- 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('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. - -- (A range of valid and invalid input values are tried out to generate the - -- return values.) - eq(0,eval("setmatches([])")) - eq(0,eval("setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}])")) - command("call clearmatches()") - 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/072_undo_file_spec.lua b/test/functional/legacy/072_undo_file_spec.lua index b4927e779e..80665027c3 100644 --- a/test/functional/legacy/072_undo_file_spec.lua +++ b/test/functional/legacy/072_undo_file_spec.lua @@ -69,7 +69,7 @@ describe('72', function() feed_command('set undofile ul=100') feed('uuuuuu:w >>test.out<cr>') - ---- Open the output to see if it meets the expections + ---- Open the output to see if it meets the expectations feed_command('e! test.out') -- Assert buffer contents. diff --git a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua index e94b46ca66..54620c7104 100644 --- a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua +++ b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua @@ -26,7 +26,7 @@ describe('tag search with !_TAG_FILE_ENCODING', function() '!_TAG_FILE_ENCODING cp932 //\n' .. '\130`\130a\130b Xtags2.txt /\130`\130a\130b\n' ) - -- The last file is very long but repetetive and can be generated on the + -- The last file is very long but repetitive and can be generated on the -- fly. local text = helpers.dedent([[ !_TAG_FILE_SORTED 1 // diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 4d9e88c446..a15809907b 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -3,7 +3,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, command, eq = helpers.clear, helpers.command, helpers.eq -local eval, exc_exec, neq = helpers.eval, helpers.exc_exec, helpers.neq local expect_exit = helpers.expect_exit local feed = helpers.feed local pcall_err = helpers.pcall_err @@ -11,233 +10,6 @@ local pcall_err = helpers.pcall_err describe('argument list commands', function() before_each(clear) - local function init_abc() - command('args a b c') - command('next') - end - - local function reset_arglist() - command('arga a | %argd') - end - - local function assert_fails(cmd, err) - neq(nil, exc_exec(cmd):find(err)) - end - - it('test that argidx() works', function() - command('args a b c') - command('last') - eq(2, eval('argidx()')) - command('%argdelete') - eq(0, eval('argidx()')) - - command('args a b c') - eq(0, eval('argidx()')) - command('next') - eq(1, eval('argidx()')) - command('next') - eq(2, eval('argidx()')) - command('1argdelete') - eq(1, eval('argidx()')) - command('1argdelete') - eq(0, eval('argidx()')) - command('1argdelete') - eq(0, eval('argidx()')) - end) - - it('test that argadd() works', function() - command('%argdelete') - command('argadd a b c') - eq(0, eval('argidx()')) - - command('%argdelete') - command('argadd a') - eq(0, eval('argidx()')) - command('argadd b c d') - eq(0, eval('argidx()')) - - init_abc() - command('argadd x') - eq({'a', 'b', 'x', 'c'}, eval('argv()')) - eq(1, eval('argidx()')) - - init_abc() - command('0argadd x') - eq({'x', 'a', 'b', 'c'}, eval('argv()')) - eq(2, eval('argidx()')) - - init_abc() - command('1argadd x') - eq({'a', 'x', 'b', 'c'}, eval('argv()')) - eq(2, eval('argidx()')) - - init_abc() - command('$argadd x') - eq({'a', 'b', 'c', 'x'}, eval('argv()')) - eq(1, eval('argidx()')) - - init_abc() - command('$argadd x') - command('+2argadd y') - eq({'a', 'b', 'c', 'x', 'y'}, eval('argv()')) - eq(1, eval('argidx()')) - - command('%argd') - command('edit d') - command('arga') - eq(1, eval('len(argv())')) - eq('d', eval('get(argv(), 0, "")')) - - command('%argd') - command('new') - command('arga') - eq(0, eval('len(argv())')) - end) - - it('test for 0argadd and 0argedit', function() - reset_arglist() - - command('arga a b c d') - command('2argu') - command('0arga added') - eq({'added', 'a', 'b', 'c', 'd'}, eval('argv()')) - - command('%argd') - command('arga a b c d') - command('2argu') - command('0arge edited') - eq({'edited', 'a', 'b', 'c', 'd'}, eval('argv()')) - - command('2argu') - command('arga third') - eq({'edited', 'a', 'third', 'b', 'c', 'd'}, eval('argv()')) - end) - - it('test for argc()', function() - reset_arglist() - eq(0, eval('argc()')) - command('argadd a b') - eq(2, eval('argc()')) - end) - - it('test for arglistid()', function() - reset_arglist() - command('arga a b') - eq(0, eval('arglistid()')) - command('split') - command('arglocal') - eq(1, eval('arglistid()')) - command('tabnew | tabfirst') - eq(0, eval('arglistid(2)')) - eq(1, eval('arglistid(1, 1)')) - eq(0, eval('arglistid(2, 1)')) - eq(1, eval('arglistid(1, 2)')) - command('tabonly | only | enew!') - command('argglobal') - eq(0, eval('arglistid()')) - end) - - it('test for argv()', function() - reset_arglist() - eq({}, eval('argv()')) - eq('', eval('argv(2)')) - command('argadd a b c d') - eq('c', eval('argv(2)')) - end) - - it('test for :argedit command', function() - reset_arglist() - command('argedit a') - eq({'a'}, eval('argv()')) - eq('a', eval('expand("%:t")')) - command('argedit b') - eq({'a', 'b'}, eval('argv()')) - eq('b', eval('expand("%:t")')) - command('argedit a') - eq({'a', 'b', 'a'}, eval('argv()')) - eq('a', eval('expand("%:t")')) - command('argedit c') - 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()')) - end) - - it('test for :argdelete command', function() - reset_arglist() - command('args aa a aaa b bb') - command('argdelete a*') - eq({'b', 'bb'}, eval('argv()')) - eq('aa', eval('expand("%:t")')) - command('last') - command('argdelete %') - eq({'b'}, eval('argv()')) - assert_fails('argdelete', 'E610:') - assert_fails('1,100argdelete', 'E16:') - reset_arglist() - command('args a b c d') - command('next') - command('argdel') - eq({'a', 'c', 'd'}, eval('argv()')) - command('%argdel') - end) - - it('test for the :next, :prev, :first, :last, :rewind commands', function() - reset_arglist() - command('args a b c d') - command('last') - eq(3, eval('argidx()')) - assert_fails('next', 'E165:') - command('prev') - eq(2, eval('argidx()')) - command('Next') - eq(1, eval('argidx()')) - command('first') - eq(0, eval('argidx()')) - assert_fails('prev', 'E164:') - command('3next') - eq(3, eval('argidx()')) - command('rewind') - eq(0, eval('argidx()')) - command('%argd') - end) - - it('test for autocommand that redefines the argument list, when doing ":all"', function() - command('autocmd BufReadPost Xxx2 next Xxx2 Xxx1') - command("call writefile(['test file Xxx1'], 'Xxx1')") - command("call writefile(['test file Xxx2'], 'Xxx2')") - command("call writefile(['test file Xxx3'], 'Xxx3')") - - command('new') - -- redefine arglist; go to Xxx1 - command('next! Xxx1 Xxx2 Xxx3') - -- open window for all args - command('all') - eq('test file Xxx1', eval('getline(1)')) - command('wincmd w') - command('wincmd w') - eq('test file Xxx1', eval('getline(1)')) - -- should now be in Xxx2 - command('rewind') - eq('test file Xxx2', eval('getline(1)')) - - command('autocmd! BufReadPost Xxx2') - command('enew! | only') - command("call delete('Xxx1')") - command("call delete('Xxx2')") - command("call delete('Xxx3')") - command('argdelete Xxx*') - command('bwipe! Xxx1 Xxx2 Xxx3') - end) - it('quitting Vim with unedited files in the argument list throws E173', function() command('set nomore') command('args a b c') diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 5e586d3a6a..e00b468c16 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -211,7 +211,7 @@ describe('au OptionSet', function() expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) - it('should trigger if the current buffer is different from the targetted buffer', function() + it('should trigger if the current buffer is different from the targeted buffer', function() local new_buffer = make_buffer() local new_bufnr = buf.get_number(new_buffer) @@ -590,7 +590,7 @@ describe('au OptionSet', function() expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) - it('should trigger if the current buffer is different from the targetted buffer', function() + it('should trigger if the current buffer is different from the targeted buffer', function() set_hook('buftype') local new_buffer = make_buffer() @@ -616,7 +616,7 @@ describe('au OptionSet', function() expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) - it('should not trigger if the current window is different from the targetted window', function() + it('should not trigger if the current window is different from the targeted window', function() set_hook('cursorcolumn') local new_winnr = get_new_window_number() diff --git a/test/functional/legacy/buffer_spec.lua b/test/functional/legacy/buffer_spec.lua new file mode 100644 index 0000000000..acaa9a51f1 --- /dev/null +++ b/test/functional/legacy/buffer_spec.lua @@ -0,0 +1,59 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, source = helpers.clear, helpers.source +local call, eq, meths = helpers.call, helpers.eq, helpers.meths + +local function expected_empty() + eq({}, meths.get_vvar('errors')) +end + +describe('buffer', function() + before_each(function() + clear() + meths.ui_attach(80, 24, {}) + meths.set_option('hidden', false) + end) + + it('deleting a modified buffer with :confirm', function() + source([[ + func Test_bdel_with_confirm() + new + call setline(1, 'test') + call assert_fails('bdel', 'E89:') + call nvim_input('c') + confirm bdel + call assert_equal(2, winnr('$')) + call assert_equal(1, &modified) + call nvim_input('n') + confirm bdel + call assert_equal(1, winnr('$')) + endfunc + ]]) + call('Test_bdel_with_confirm') + expected_empty() + end) + + it('editing another buffer from a modified buffer with :confirm', function() + source([[ + func Test_goto_buf_with_confirm() + new Xfile + enew + call setline(1, 'test') + call assert_fails('b Xfile', 'E37:') + call nvim_input('c') + call assert_fails('confirm b Xfile', 'E37:') + call assert_equal(1, &modified) + call assert_equal('', @%) + call nvim_input('y') + call assert_fails('confirm b Xfile', 'E37:') + call assert_equal(1, &modified) + call assert_equal('', @%) + call nvim_input('n') + confirm b Xfile + call assert_equal('Xfile', @%) + close! + endfunc + ]]) + call('Test_goto_buf_with_confirm') + expected_empty() + end) +end) diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua index cf02636890..2fceb6a132 100644 --- a/test/functional/legacy/cmdline_spec.lua +++ b/test/functional/legacy/cmdline_spec.lua @@ -1,9 +1,12 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear = helpers.clear +local command = helpers.command local feed = helpers.feed local feed_command = helpers.feed_command local exec = helpers.exec +local meths = helpers.meths +local pesc = helpers.pesc describe('cmdline', function() before_each(clear) @@ -18,8 +21,6 @@ describe('cmdline', function() [3] = {reverse = true}; [4] = {bold = true, foreground = Screen.colors.Blue1}; } - -- TODO(bfredl): redraw with tabs is severly broken. fix it - feed_command [[ set display-=msgsep ]] feed_command([[call setline(1, range(30))]]) screen:expect([[ @@ -60,7 +61,7 @@ describe('cmdline', function() {4:~ }| | | - :tabnew | + | ]]} feed [[gt]] @@ -140,4 +141,180 @@ describe('cmdline', function() :^ | ]]) end) + + -- oldtest: Test_redraw_in_autocmd() + it('cmdline cursor position is correct after :redraw with cmdheight=2', function() + local screen = Screen.new(30, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + }) + screen:attach() + exec([[ + set cmdheight=2 + autocmd CmdlineChanged * redraw + ]]) + feed(':for i in range(3)<CR>') + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + :for i in range(3) | + : ^ | + ]]) + feed(':let i =') + -- Note: this may still be considered broken, ref #18140 + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + : :let i =^ | + | + ]]) + end) + + it("setting 'cmdheight' works after outputting two messages vim-patch:9.0.0665", function() + local screen = Screen.new(60, 8) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, reverse = true}, -- StatusLine + }) + screen:attach() + exec([[ + set cmdheight=1 laststatus=2 + func EchoTwo() + set laststatus=2 + set cmdheight=5 + echo 'foo' + echo 'bar' + set cmdheight=1 + endfunc + ]]) + feed(':call EchoTwo()') + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[No Name] }| + :call EchoTwo()^ | + ]]) + feed('<CR>') + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[No Name] }| + | + ]]) + end) + + -- oldtest: Test_cmdheight_tabline() + it("changing 'cmdheight' when there is a tabline", function() + local screen = Screen.new(60, 8) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, reverse = true}, -- StatusLine + [2] = {bold = true}, -- TabLineSel + [3] = {reverse = true}, -- TabLineFill + }) + screen:attach() + meths.set_option('laststatus', 2) + meths.set_option('showtabline', 2) + meths.set_option('cmdheight', 1) + screen:expect([[ + {2: [No Name] }{3: }| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[No Name] }| + | + ]]) + end) +end) + +describe('cmdwin', function() + before_each(clear) + + -- oldtest: Test_cmdwin_interrupted() + it('still uses a new buffer when interrupting more prompt on open', function() + local screen = Screen.new(30, 16) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, reverse = true}, -- StatusLine + [2] = {reverse = true}, -- StatusLineNC + [3] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + [4] = {bold = true}, -- ModeMsg + }) + screen:attach() + command('set more') + command('autocmd WinNew * highlight') + feed('q:') + screen:expect({any = pesc('{3:-- More --}^')}) + feed('q') + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:[No Name] }| + {0::}^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[Command Line] }| + | + ]]) + feed([[aecho 'done']]) + screen:expect([[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:[No Name] }| + {0::}echo 'done'^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:[Command Line] }| + {4:-- INSERT --} | + ]]) + feed('<CR>') + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + done | + ]]) + end) end) diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua index f0ffaf2c48..9160129a02 100644 --- a/test/functional/legacy/display_spec.lua +++ b/test/functional/legacy/display_spec.lua @@ -9,6 +9,7 @@ local command = helpers.command describe('display', function() before_each(clear) + -- oldtest: Test_display_scroll_at_topline() it('scroll when modified at topline vim-patch:8.2.1488', function() local screen = Screen.new(20, 4) screen:attach() @@ -26,6 +27,7 @@ describe('display', function() ]]) end) + -- oldtest: Test_display_scroll_update_visual() it('scrolling when modified at topline in Visual mode vim-patch:8.2.4626', function() local screen = Screen.new(60, 8) screen:attach() @@ -56,8 +58,8 @@ describe('display', function() ]]) end) - it('@@@ in the last line shows correctly in a narrow window vim-patch:8.2.4718', function() - local screen = Screen.new(60, 10) + local function run_test_display_lastline(euro) + local screen = Screen.new(75, 10) screen:set_default_attr_ids({ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText [2] = {bold = true, reverse = true}, -- StatusLine @@ -65,39 +67,86 @@ describe('display', function() }) screen:attach() exec([[ - call setline(1, ['aaa', 'b'->repeat(100)]) + call setline(1, ['aaa', 'b'->repeat(200)]) set display=truncate + vsplit 100wincmd < ]]) - screen:expect([[ - ^a│aaa | - a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| - a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | - b│{1:~ }| - b│{1:~ }| - b│{1:~ }| - b│{1:~ }| - {1:@}│{1:~ }| - {2:< }{3:[No Name] [+] }| - | - ]]) + local fillchar = '@' + if euro then + command('set fillchars=lastline:€') + fillchar = '€' + end + screen:expect((([[ + ^a│aaa | + a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + a│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + b│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + b│{1:~ }| + b│{1:~ }| + b│{1:~ }| + {1:@}│{1:~ }| + {2:< }{3:[No Name] [+] }| + | + ]]):gsub('@', fillchar))) + command('set display=lastline') screen:expect_unchanged() + command('100wincmd >') - screen:expect([[ - ^aaa │a| - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│a| - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb │a| - {1:~ }│b| - {1:~ }│b| - {1:~ }│b| - {1:~ }│b| - {1:~ }│{1:@}| - {2:[No Name] [+] }{3:<}| - | - ]]) + screen:expect((([[ + ^aaa │a| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│a| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb│a| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb │b| + {1:~ }│b| + {1:~ }│b| + {1:~ }│b| + {1:~ }│{1:@}| + {2:[No Name] [+] }{3:<}| + | + ]]):gsub('@', fillchar))) + command('set display=truncate') screen:expect_unchanged() + + command('close') + command('3split') + screen:expect((([[ + ^aaa | + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + {1:@@@ }| + {2:[No Name] [+] }| + aaa | + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + {3:[No Name] [+] }| + | + ]]):gsub('@', fillchar))) + + command('close') + command('2vsplit') + screen:expect((([[ + ^aa│aaa | + a │bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + bb│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + bb│bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + bb│{1:~ }| + bb│{1:~ }| + bb│{1:~ }| + {1:@@}│{1:~ }| + {2:< }{3:[No Name] [+] }| + | + ]]):gsub('@', fillchar))) + end + + -- oldtest: Test_display_lastline() + it('display "lastline" works correctly', function() + run_test_display_lastline() + end) + it('display "lastline" works correctly with multibyte fillchar', function() + run_test_display_lastline(true) end) end) diff --git a/test/functional/legacy/listlbr_spec.lua b/test/functional/legacy/listlbr_spec.lua index f70d55f4a3..d4f11a61c2 100644 --- a/test/functional/legacy/listlbr_spec.lua +++ b/test/functional/legacy/listlbr_spec.lua @@ -1,11 +1,12 @@ -- Test for linebreak and list option (non-utf8) local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local feed, insert, source = helpers.feed, helpers.insert, helpers.source local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect describe('listlbr', function() - setup(clear) + before_each(clear) -- luacheck: ignore 621 (Indentation) -- luacheck: ignore 611 (Line contains only whitespaces) @@ -195,4 +196,97 @@ describe('listlbr', function() aa>-----a-$ ~ ]]) end) + + -- oldtest: Test_linebreak_reset_restore() + it('cursor position is drawn correctly after operator', function() + local screen = Screen.new(60, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {background = Screen.colors.LightGrey}, -- Visual + [2] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg + }) + screen:attach() + + -- f_wincol() calls validate_cursor() + source([[ + set linebreak showcmd noshowmode formatexpr=wincol()-wincol() + call setline(1, repeat('a', &columns - 10) .. ' bbbbbbbbbb c') + ]]) + + feed('$v$') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb {1:c}^ | + {0:~ }| + {0:~ }| + {0:~ }| + 2 | + ]]) + feed('zo') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb ^c | + {0:~ }| + {0:~ }| + {0:~ }| + {2:E490: No fold found} | + ]]) + + feed('$v$') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb {1:c}^ | + {0:~ }| + {0:~ }| + {0:~ }| + {2:E490: No fold found} 2 | + ]]) + feed('gq') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb ^c | + {0:~ }| + {0:~ }| + {0:~ }| + {2:E490: No fold found} | + ]]) + + feed('$<C-V>$') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb {1:c}^ | + {0:~ }| + {0:~ }| + {0:~ }| + {2:E490: No fold found} 1x2 | + ]]) + feed('I') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb ^c | + {0:~ }| + {0:~ }| + {0:~ }| + {2:E490: No fold found} | + ]]) + + feed('<Esc>$v$') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb {1:c}^ | + {0:~ }| + {0:~ }| + {0:~ }| + {2:E490: No fold found} 2 | + ]]) + feed('s') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + bbbbbbbbbb ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {2:E490: No fold found} | + ]]) + end) end) diff --git a/test/functional/legacy/match_spec.lua b/test/functional/legacy/match_spec.lua new file mode 100644 index 0000000000..271f844f9d --- /dev/null +++ b/test/functional/legacy/match_spec.lua @@ -0,0 +1,38 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local exec = helpers.exec + +before_each(clear) + +describe('matchaddpos()', function() + -- oldtest: Test_matchaddpos_dump() + it('can add more than 8 match positions vim-patch:9.0.0620', function() + local screen = Screen.new(60, 14) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {background = Screen.colors.Yellow}, -- Search + }) + screen:attach() + exec([[ + call setline(1, ['1234567890123']->repeat(14)) + call matchaddpos('Search', range(1, 12)->map({i, v -> [v, v]})) + ]]) + screen:expect([[ + {1:^1}234567890123 | + 1{1:2}34567890123 | + 12{1:3}4567890123 | + 123{1:4}567890123 | + 1234{1:5}67890123 | + 12345{1:6}7890123 | + 123456{1:7}890123 | + 1234567{1:8}90123 | + 12345678{1:9}0123 | + 123456789{1:0}123 | + 1234567890{1:1}23 | + 12345678901{1:2}3 | + 1234567890123 | + | + ]]) + end) +end) diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua index 159cf7a551..71a53c8381 100644 --- a/test/functional/legacy/messages_spec.lua +++ b/test/functional/legacy/messages_spec.lua @@ -4,27 +4,65 @@ local clear = helpers.clear local command = helpers.command local exec = helpers.exec local feed = helpers.feed +local meths = helpers.meths +local nvim_dir = helpers.nvim_dir before_each(clear) describe('messages', function() local screen + -- oldtest: Test_warning_scroll() + it('a warning causes scrolling if and only if it has a stacktrace', function() + screen = Screen.new(75, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + [2] = {bold = true, reverse = true}, -- MsgSeparator + [3] = {foreground = Screen.colors.Red}, -- WarningMsg + }) + screen:attach() + + -- When the warning comes from a script, messages are scrolled so that the + -- stacktrace is visible. + -- It is a bit hard to assert the screen when sourcing a script, so skip this part. + + -- When the warning does not come from a script, messages are not scrolled. + command('enew') + command('set readonly') + feed('u') + screen:expect({grid = [[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:W10: Warning: Changing a readonly file}^ | + ]], timeout = 500}) + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + Already at oldest change | + ]]) + end) + describe('more prompt', function() before_each(function() + command('set more') + end) + + -- oldtest: Test_message_more() + it('works', function() screen = Screen.new(75, 6) screen:set_default_attr_ids({ - [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg [2] = {foreground = Screen.colors.Brown}, -- LineNr - [3] = {foreground = Screen.colors.Blue}, -- SpecialKey }) screen:attach() - command('set more') - end) - -- oldtest: Test_message_more() - it('works', function() command('call setline(1, range(1, 100))') feed(':%pfoo<C-H><C-H><C-H>#') @@ -313,16 +351,139 @@ describe('messages', function() ]]) end) + -- oldtest: Test_echo_verbose_system() + it('verbose message before echo command', function() + screen = Screen.new(60, 10) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + }) + screen:attach() + + command('cd '..nvim_dir) + meths.set_option('shell', './shell-test') + meths.set_option('shellcmdflag', 'REP 20') + meths.set_option('shellxquote', '') -- win: avoid extra quotes + + -- display a page and go back, results in exactly the same view + feed([[:4 verbose echo system('foo')<CR>]]) + screen:expect([[ + Executing command: "'./shell-test' 'REP' '20' 'foo'" | + | + 0: foo | + 1: foo | + 2: foo | + 3: foo | + 4: foo | + 5: foo | + 6: foo | + {1:-- More --}^ | + ]]) + feed('<Space>') + screen:expect([[ + 7: foo | + 8: foo | + 9: foo | + 10: foo | + 11: foo | + 12: foo | + 13: foo | + 14: foo | + 15: foo | + {1:-- More --}^ | + ]]) + feed('b') + screen:expect([[ + Executing command: "'./shell-test' 'REP' '20' 'foo'" | + | + 0: foo | + 1: foo | + 2: foo | + 3: foo | + 4: foo | + 5: foo | + 6: foo | + {1:-- More --}^ | + ]]) + + -- do the same with 'cmdheight' set to 2 + feed('q') + command('set ch=2') + command('mode') -- FIXME: bottom is invalid after scrolling + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + | + ]]) + feed([[:4 verbose echo system('foo')<CR>]]) + screen:expect([[ + Executing command: "'./shell-test' 'REP' '20' 'foo'" | + | + 0: foo | + 1: foo | + 2: foo | + 3: foo | + 4: foo | + 5: foo | + 6: foo | + {1:-- More --}^ | + ]]) + feed('<Space>') + screen:expect([[ + 7: foo | + 8: foo | + 9: foo | + 10: foo | + 11: foo | + 12: foo | + 13: foo | + 14: foo | + 15: foo | + {1:-- More --}^ | + ]]) + feed('b') + screen:expect([[ + Executing command: "'./shell-test' 'REP' '20' 'foo'" | + | + 0: foo | + 1: foo | + 2: foo | + 3: foo | + 4: foo | + 5: foo | + 6: foo | + {1:-- More --}^ | + ]]) + end) + -- oldtest: Test_quit_long_message() it('with control characters can be quit vim-patch:8.2.1844', function() - screen:try_resize(40, 6) + screen = Screen.new(40, 10) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + [2] = {foreground = Screen.colors.Blue}, -- SpecialKey + }) + screen:attach() + feed([[:echom range(9999)->join("\x01")<CR>]]) screen:expect([[ - 0{3:^A}1{3:^A}2{3:^A}3{3:^A}4{3:^A}5{3:^A}6{3:^A}7{3:^A}8{3:^A}9{3:^A}10{3:^A}11{3:^A}12| - {3:^A}13{3:^A}14{3:^A}15{3:^A}16{3:^A}17{3:^A}18{3:^A}19{3:^A}20{3:^A}21{3:^A}22| - {3:^A}23{3:^A}24{3:^A}25{3:^A}26{3:^A}27{3:^A}28{3:^A}29{3:^A}30{3:^A}31{3:^A}32| - {3:^A}33{3:^A}34{3:^A}35{3:^A}36{3:^A}37{3:^A}38{3:^A}39{3:^A}40{3:^A}41{3:^A}42| - {3:^A}43{3:^A}44{3:^A}45{3:^A}46{3:^A}47{3:^A}48{3:^A}49{3:^A}50{3:^A}51{3:^A}52| + 0{2:^A}1{2:^A}2{2:^A}3{2:^A}4{2:^A}5{2:^A}6{2:^A}7{2:^A}8{2:^A}9{2:^A}10{2:^A}11{2:^A}12| + {2:^A}13{2:^A}14{2:^A}15{2:^A}16{2:^A}17{2:^A}18{2:^A}19{2:^A}20{2:^A}21{2:^A}22| + {2:^A}23{2:^A}24{2:^A}25{2:^A}26{2:^A}27{2:^A}28{2:^A}29{2:^A}30{2:^A}31{2:^A}32| + {2:^A}33{2:^A}34{2:^A}35{2:^A}36{2:^A}37{2:^A}38{2:^A}39{2:^A}40{2:^A}41{2:^A}42| + {2:^A}43{2:^A}44{2:^A}45{2:^A}46{2:^A}47{2:^A}48{2:^A}49{2:^A}50{2:^A}51{2:^A}52| + {2:^A}53{2:^A}54{2:^A}55{2:^A}56{2:^A}57{2:^A}58{2:^A}59{2:^A}60{2:^A}61{2:^A}62| + {2:^A}63{2:^A}64{2:^A}65{2:^A}66{2:^A}67{2:^A}68{2:^A}69{2:^A}70{2:^A}71{2:^A}72| + {2:^A}73{2:^A}74{2:^A}75{2:^A}76{2:^A}77{2:^A}78{2:^A}79{2:^A}80{2:^A}81{2:^A}82| + {2:^A}83{2:^A}84{2:^A}85{2:^A}86{2:^A}87{2:^A}88{2:^A}89{2:^A}90{2:^A}91{2:^A}92| {1:-- More --}^ | ]]) feed('q') @@ -332,6 +493,10 @@ describe('messages', function() {0:~ }| {0:~ }| {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| | ]]) end) diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua index 67991f5d48..5a94fca794 100644 --- a/test/functional/legacy/search_spec.lua +++ b/test/functional/legacy/search_spec.lua @@ -644,7 +644,35 @@ end) describe('Search highlight', function() before_each(clear) - it('Search highlight is combined with Visual highlight vim-patch:8.2.2797', function() + + -- oldtest: Test_hlsearch_dump() + it('beyond line end vim-patch:8.2.2542', function() + local screen = Screen.new(50, 6) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [2] = {background = Screen.colors.Yellow}, -- Search + [3] = {background = Screen.colors.Grey90}, -- CursorLine + }) + screen:attach() + exec([[ + set hlsearch noincsearch cursorline + call setline(1, ["xxx", "xxx", "xxx"]) + /.* + 2 + ]]) + feed([[/\_.*<CR>]]) + screen:expect([[ + {2:xxx } | + {2:xxx } | + {2:^xxx }{3: }| + {1:~ }| + {1:~ }| + /\_.* | + ]]) + end) + + -- oldtest: Test_hlsearch_and_visual() + it('is combined with Visual highlight vim-patch:8.2.2797', function() local screen = Screen.new(40, 6) screen:set_default_attr_ids({ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText diff --git a/test/functional/legacy/080_substitute_spec.lua b/test/functional/legacy/substitute_spec.lua index faeb61e3af..f3ce343680 100644 --- a/test/functional/legacy/080_substitute_spec.lua +++ b/test/functional/legacy/substitute_spec.lua @@ -3,11 +3,13 @@ -- Test for *:s%* on :substitute. local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local feed, insert = helpers.feed, helpers.insert +local exec = helpers.exec local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect local eq, eval = helpers.eq, helpers.eval -describe('substitue()', function() +describe('substitute()', function() before_each(clear) -- The original test contained several TEST_X lines to delimit different @@ -132,7 +134,7 @@ describe('substitue()', function() end) end) -describe(':substitue', function() +describe(':substitute', function() before_each(clear) it('with \\ze and \\zs and confirmation dialog (TEST_8)', function() @@ -159,4 +161,29 @@ describe(':substitue', function() feed('yyq') -- For the dialog of the previous :s command. expect('XXx') end) + + it('first char is highlighted with confirmation dialog and empty match', function() + local screen = Screen.new(60, 8) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {reverse = true}, -- IncSearch + [2] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg + }) + screen:attach() + exec([[ + set nohlsearch noincsearch + call setline(1, ['one', 'two', 'three']) + ]]) + feed(':%s/^/ /c<CR>') + screen:expect([[ + {1:o}ne | + two | + three | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:replace with (y/n/a/q/l/^E/^Y)?}^ | + ]]) + end) end) diff --git a/test/functional/legacy/window_cmd_spec.lua b/test/functional/legacy/window_cmd_spec.lua new file mode 100644 index 0000000000..8b89c55f5b --- /dev/null +++ b/test/functional/legacy/window_cmd_spec.lua @@ -0,0 +1,196 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local exec = helpers.exec +local exec_lua = helpers.exec_lua +local feed = helpers.feed + +describe('splitkeep', function() + local screen = Screen.new() + before_each(function() + clear('--cmd', 'set splitkeep=screen') + screen:attach() + end) + + -- oldtest: Test_splitkeep_callback() + it('does not scroll when split in callback', function() + exec([[ + call setline(1, range(&lines)) + function C1(a, b, c) + split | wincmd p + endfunction + function C2(a, b, c) + close | split + endfunction + ]]) + exec_lua([[ + vim.api.nvim_set_keymap("n", "j", "", { callback = function() + vim.cmd("call jobstart([&sh, &shcf, 'true'], { 'on_exit': 'C1' })") + end + })]]) + exec_lua([[ + vim.api.nvim_set_keymap("n", "t", "", { callback = function() + vim.api.nvim_set_current_win( + vim.api.nvim_open_win(vim.api.nvim_create_buf(false, {}), false, { + width = 10, + relative = "cursor", + height = 4, + row = 0, + col = 0, + })) + vim.cmd("call termopen([&sh, &shcf, 'true'], { 'on_exit': 'C2' })") + end + })]]) + feed('j') + screen:expect([[ + 0 | + 1 | + 2 | + 3 | + 4 | + 5 | + [No Name] [+] | + ^7 | + 8 | + 9 | + 10 | + 11 | + [No Name] [+] | + | + ]]) + feed(':quit<CR>Ht') + screen:expect([[ + ^0 | + 1 | + 2 | + 3 | + 4 | + 5 | + [No Name] [+] | + 7 | + 8 | + 9 | + 10 | + 11 | + [No Name] [+] | + :quit | + ]]) + feed(':set sb<CR>:quit<CR>Gj') + screen:expect([[ + 1 | + 2 | + 3 | + 4 | + ^5 | + [No Name] [+] | + 7 | + 8 | + 9 | + 10 | + 11 | + 12 | + [No Name] [+] | + :quit | + ]]) + feed(':quit<CR>Gt') + screen:expect([[ + 1 | + 2 | + 3 | + 4 | + 5 | + [No Name] [+] | + 7 | + 8 | + 9 | + 10 | + 11 | + ^12 | + [No Name] [+] | + :quit | + ]]) + end) + + -- oldtest: Test_splitkeep_fold() + it('does not scroll when window has closed folds', function() + exec([[ + set splitkeep=screen + set foldmethod=marker + set number + let line = 1 + for n in range(1, &lines) + call setline(line, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', + \ 'after fold']) + let line += 8 + endfor + ]]) + feed('L:wincmd s<CR>') + screen:expect([[ + 1 +-- 7 lines: int FuncName() {···················| + 8 after fold | + 9 +-- 7 lines: int FuncName() {···················| + 16 after fold | + 17 +-- 7 lines: int FuncName() {···················| + 24 ^after fold | + [No Name] [+] | + 32 after fold | + 33 +-- 7 lines: int FuncName() {···················| + 40 after fold | + 41 +-- 7 lines: int FuncName() {···················| + 48 after fold | + [No Name] [+] | + :wincmd s | + ]]) + feed(':quit<CR>') + screen:expect([[ + 1 +-- 7 lines: int FuncName() {···················| + 8 after fold | + 9 +-- 7 lines: int FuncName() {···················| + 16 after fold | + 17 +-- 7 lines: int FuncName() {···················| + 24 after fold | + 25 +-- 7 lines: int FuncName() {···················| + 32 after fold | + 33 +-- 7 lines: int FuncName() {···················| + 40 after fold | + 41 +-- 7 lines: int FuncName() {···················| + 48 after fold | + 49 ^+-- 7 lines: int FuncName() {···················| + :quit | + ]]) + feed('H:below split<CR>') + screen:expect([[ + 1 +-- 7 lines: int FuncName() {···················| + 8 after fold | + 9 +-- 7 lines: int FuncName() {···················| + 16 after fold | + 17 +-- 7 lines: int FuncName() {···················| + [No Name] [+] | + 25 ^+-- 7 lines: int FuncName() {···················| + 32 after fold | + 33 +-- 7 lines: int FuncName() {···················| + 40 after fold | + 41 +-- 7 lines: int FuncName() {···················| + 48 after fold | + [No Name] [+] | + :below split | + ]]) + feed(':wincmd k<CR>:quit<CR>') + screen:expect([[ + 1 +-- 7 lines: int FuncName() {···················| + 8 after fold | + 9 +-- 7 lines: int FuncName() {···················| + 16 after fold | + 17 +-- 7 lines: int FuncName() {···················| + 24 after fold | + 25 ^+-- 7 lines: int FuncName() {···················| + 32 after fold | + 33 +-- 7 lines: int FuncName() {···················| + 40 after fold | + 41 +-- 7 lines: int FuncName() {···················| + 48 after fold | + 49 +-- 7 lines: int FuncName() {···················| + :quit | + ]]) + end) +end) diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua index f173a15d32..03832978a4 100644 --- a/test/functional/lua/api_spec.lua +++ b/test/functional/lua/api_spec.lua @@ -166,7 +166,7 @@ describe('luaeval(vim.api.…)', function() eq({v={}}, funcs.luaeval('vim.api.nvim__id_dictionary({v={[vim.type_idx]=vim.types.array, [vim.val_idx]=10, [5]=1, foo=2}})')) -- If API requests dictionary, then empty table will be the one. This is not - -- the case normally because empty table is an empty arrray. + -- the case normally because empty table is an empty array. eq({}, funcs.luaeval('vim.api.nvim__id_dictionary({})')) eq(4, eval([[type(luaeval('vim.api.nvim__id_dictionary({})'))]])) end) diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 10de45274c..2fd44b8b5f 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -118,6 +118,24 @@ describe('lua buffer event callbacks: on_lines', function() } tick = tick + 1 + tick = tick + 1 + command('redo') + check_events { + { "test1", "lines", 1, tick, 3, 5, 4, 32 }; + { "test2", "lines", 1, tick, 3, 5, 4, 32 }; + { "test2", "changedtick", 1, tick+1 }; + } + tick = tick + 1 + + tick = tick + 1 + command('undo!') + check_events { + { "test1", "lines", 1, tick, 3, 4, 5, 13 }; + { "test2", "lines", 1, tick, 3, 4, 5, 13 }; + { "test2", "changedtick", 1, tick+1 }; + } + tick = tick + 1 + -- simulate next callback returning true exec_lua("test_unreg = 'test1'") @@ -315,7 +333,7 @@ describe('lua: nvim_buf_attach on_bytes', function() start_txt = meths.buf_get_lines(0, 0, -1, true) end local shadowbytes = table.concat(start_txt, '\n') .. '\n' - -- TODO: while we are brewing the real strong coffe, + -- TODO: while we are brewing the real strong coffee, -- verify should check buf_get_offset after every check_events if verify then local len = meths.buf_get_offset(0, meths.buf_line_count(0)) diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index f9647f5b6a..28a8679205 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -128,6 +128,37 @@ describe('vim.diagnostic', function() eq('Diagnostic #1', result[1].message) end) + it('removes diagnostics from the cache when a buffer is removed', function() + eq(2, exec_lua [[ + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + local other_bufnr = vim.fn.bufadd('test | test') + local lines = vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, true) + vim.api.nvim_buf_set_lines(other_bufnr, 0, 1, false, lines) + vim.cmd('bunload! ' .. other_bufnr) + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 2, 1, 2, 1), + }) + vim.diagnostic.set(diagnostic_ns, other_bufnr, { + make_error('Diagnostic #3', 3, 1, 3, 1), + }) + vim.api.nvim_set_current_buf(other_bufnr) + vim.opt_local.buflisted = true + vim.cmd('bwipeout!') + return #vim.diagnostic.get() + ]]) + eq(2, exec_lua [[ + vim.api.nvim_set_current_buf(diagnostic_bufnr) + vim.opt_local.buflisted = false + return #vim.diagnostic.get() + ]]) + eq(0, exec_lua [[ + vim.cmd('bwipeout!') + return #vim.diagnostic.get() + ]]) + end) + it('resolves buffer number 0 to the current buffer', function() eq(2, exec_lua [[ vim.api.nvim_set_current_buf(diagnostic_bufnr) @@ -1952,19 +1983,26 @@ end) end) it('triggers the autocommand when diagnostics are set', function() - eq(true, exec_lua [[ + eq({true, true}, exec_lua [[ -- Set a different buffer as current to test that <abuf> is being set properly in -- DiagnosticChanged callbacks local tmp = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_current_buf(tmp) - vim.g.diagnostic_autocmd_triggered = 0 - vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = +expand("<abuf>")') + local triggered = {} + vim.api.nvim_create_autocmd('DiagnosticChanged', { + callback = function(args) + triggered = {args.buf, #args.data.diagnostics} + end, + }) vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic', 0, 0, 0, 0) }) - return vim.g.diagnostic_autocmd_triggered == diagnostic_bufnr + return { + triggered[1] == diagnostic_bufnr, + triggered[2] == 1, + } ]]) end) diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua index be57b2db31..2a7be53164 100644 --- a/test/functional/lua/filetype_spec.lua +++ b/test/functional/lua/filetype_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local exec_lua = helpers.exec_lua local eq = helpers.eq +local meths = helpers.meths local clear = helpers.clear local pathroot = helpers.pathroot local command = helpers.command @@ -94,3 +95,10 @@ describe('vim.filetype', function() ]]) end) end) + +describe('filetype.lua', function() + it('does not override user autocommands that set filetype #20333', function() + clear({args={'--clean', '--cmd', 'autocmd BufRead *.md set filetype=notmarkdown', 'README.md'}}) + eq('notmarkdown', meths.buf_get_option(0, 'filetype')) + end) +end) diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 2bcc84db0f..3123ec324c 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -78,6 +78,23 @@ describe('vim.fs', function() return vim.fs.find(nvim, { path = dir, type = 'file' }) ]], test_build_dir, nvim_prog_basename)) end) + + it('accepts predicate as names', function() + eq({test_build_dir}, exec_lua([[ + local dir = ... + local opts = { path = dir, upward = true, type = 'directory' } + return vim.fs.find(function(x) return x == 'build' end, opts) + ]], nvim_dir)) + eq({nvim_prog}, exec_lua([[ + local dir, nvim = ... + return vim.fs.find(function(x) return x == nvim end, { path = dir, type = 'file' }) + ]], test_build_dir, nvim_prog_basename)) + eq({}, exec_lua([[ + local dir = ... + local opts = { path = dir, upward = true, type = 'directory' } + return vim.fs.find(function(x) return x == 'no-match' end, opts) + ]], nvim_dir)) + end) end) describe('normalize()', function() diff --git a/test/functional/lua/help_spec.lua b/test/functional/lua/help_spec.lua new file mode 100644 index 0000000000..251275b5cc --- /dev/null +++ b/test/functional/lua/help_spec.lua @@ -0,0 +1,49 @@ +-- Tests for gen_help_html.lua. Validates :help tags/links and HTML doc generation. +-- +-- TODO: extract parts of gen_help_html.lua into Nvim stdlib? + +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local ok = helpers.ok + +describe(':help docs', function() + before_each(clear) + it('validate', function() + -- If this test fails, try these steps (in order): + -- 1. Fix/cleanup the :help docs. + -- 2. Fix the parser: https://github.com/neovim/tree-sitter-vimdoc + -- 3. File a parser bug, and adjust the tolerance of this test in the meantime. + + local rv = exec_lua([[return require('scripts.gen_help_html').validate('./build/runtime/doc')]]) + -- Check that we actually found helpfiles. + ok(rv.helpfiles > 100, '>100 :help files', rv.helpfiles) + -- Check that parse errors did not increase wildly. + -- TODO: Fix all parse errors in :help files. + ok(rv.err_count < 150, '<150 parse errors', rv.err_count) + eq({}, rv.invalid_links, exec_lua([[return 'found invalid :help tag links:\n'..vim.inspect(...)]], rv.invalid_links)) + end) + + it('gen_help_html.lua generates HTML', function() + -- 1. Test that gen_help_html.lua actually works. + -- 2. Test that parse errors did not increase wildly. Because we explicitly test only a few + -- :help files, we can be precise about the tolerances here. + + local tmpdir = exec_lua('return vim.fs.dirname(vim.fn.tempname())') + -- Because gen() is slow (~30s), this test is limited to a few files. + local rv = exec_lua([[ + local to_dir = ... + return require('scripts.gen_help_html').gen( + './build/runtime/doc', + to_dir, + { 'pi_health.txt', 'help.txt', 'index.txt', 'nvim.txt', } + ) + ]], + tmpdir + ) + eq(4, #rv.helpfiles) + ok(rv.err_count <= 1, '<=1 parse errors', rv.err_count) + eq({}, rv.invalid_links, exec_lua([[return 'found invalid :help tag links:\n'..vim.inspect(...)]], rv.invalid_links)) + end) +end) diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua new file mode 100644 index 0000000000..57ffcf7b4e --- /dev/null +++ b/test/functional/lua/ui_event_spec.lua @@ -0,0 +1,120 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local clear = helpers.clear +local feed = helpers.feed +local funcs = helpers.funcs +local inspect = require'vim.inspect' + +describe('vim.ui_attach', function() + local screen + before_each(function() + clear() + exec_lua [[ + ns = vim.api.nvim_create_namespace 'testspace' + events = {} + function on_event(event, ...) + events[#events+1] = {event, ...} + return true + end + + function get_events() + local ret_events = events + events = {} + return ret_events + end + ]] + + screen = Screen.new(40,5) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}; + [2] = {bold = true}; + [3] = {background = Screen.colors.Grey}; + [4] = {background = Screen.colors.LightMagenta}; + }) + screen:attach() + end) + + local function expect_events(expected) + local evs = exec_lua "return get_events(...)" + eq(expected, evs, inspect(evs)) + end + + it('can receive popupmenu events', function() + exec_lua [[ vim.ui_attach(ns, {ext_popupmenu=true}, on_event) ]] + feed('ifo') + screen:expect{grid=[[ + fo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]} + + funcs.complete(1, {'food', 'foobar', 'foo'}) + screen:expect{grid=[[ + food^ | + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]} + expect_events { + { "popupmenu_show", { { "food", "", "", "" }, { "foobar", "", "", "" }, { "foo", "", "", "" } }, 0, 0, 0, 1 }; + } + + feed '<c-n>' + screen:expect{grid=[[ + foobar^ | + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]]} + expect_events { + { "popupmenu_select", 1 }; + } + + feed '<c-y>' + screen:expect{grid=[[ + foobar^ | + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], intermediate=true} + expect_events { + { "popupmenu_hide" }; + } + + -- vim.ui_detach() stops events, and reenables builtin pum immediately + exec_lua [[ + vim.ui_detach(ns) + vim.fn.complete(1, {'food', 'foobar', 'foo'}) + ]] + + screen:expect{grid=[[ + food^ | + {3:food }{1: }| + {4:foobar }{1: }| + {4:foo }{1: }| + {2:-- INSERT --} | + ]]} + expect_events { + } + + end) + + it('does not crash on exit', function() + helpers.funcs.system({ + helpers.nvim_prog, + '-u', 'NONE', + '-i', 'NONE', + '--cmd', [[ lua ns = vim.api.nvim_create_namespace 'testspace' ]], + '--cmd', [[ lua vim.ui_attach(ns, {ext_popupmenu=true}, function() end) ]], + '--cmd', 'quitall!', + }) + eq(0, helpers.eval('v:shell_error')) + end) +end) diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index 4635f17557..2cb0b26c6d 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -11,7 +11,7 @@ describe('URI methods', function() describe('file path to uri', function() describe('encode Unix file path', function() - it('file path includes only ascii charactors', function() + it('file path includes only ascii characters', function() exec_lua("filepath = '/Foo/Bar/Baz.txt'") eq('file:///Foo/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)")) @@ -23,7 +23,7 @@ describe('URI methods', function() eq('file:///Foo%20/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)")) end) - it('file path including Unicode charactors', function() + it('file path including Unicode characters', function() exec_lua("filepath = '/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt'") -- The URI encoding should be case-insensitive @@ -32,7 +32,7 @@ describe('URI methods', function() end) describe('encode Windows filepath', function() - it('file path includes only ascii charactors', function() + it('file path includes only ascii characters', function() exec_lua([[filepath = 'C:\\Foo\\Bar\\Baz.txt']]) eq('file:///C:/Foo/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)")) @@ -44,7 +44,7 @@ describe('URI methods', function() eq('file:///C:/Foo%20/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)")) end) - it('file path including Unicode charactors', function() + it('file path including Unicode characters', function() exec_lua([[filepath = 'C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt']]) eq('file:///C:/xy/%c3%a5%c3%a4%c3%b6/%c9%a7/%e6%b1%89%e8%af%ad/%e2%86%a5/%f0%9f%a4%a6/%f0%9f%a6%84/a%cc%8a/%d8%a8%d9%90%d9%8a%d9%8e%d9%91.txt', exec_lua("return vim.uri_from_fname(filepath)")) @@ -72,7 +72,7 @@ describe('URI methods', function() eq('/Foo /Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)")) end) - it('file path including Unicode charactors', function() + it('file path including Unicode characters', function() local test_case = [[ local uri = 'file:///xy/%C3%A5%C3%A4%C3%B6/%C9%A7/%E6%B1%89%E8%AF%AD/%E2%86%A5/%F0%9F%A4%A6/%F0%9F%A6%84/a%CC%8A/%D8%A8%D9%90%D9%8A%D9%8E%D9%91.txt' return vim.uri_to_fname(uri) @@ -83,7 +83,7 @@ describe('URI methods', function() end) describe('decode Windows filepath', function() - it('file path includes only ascii charactors', function() + it('file path includes only ascii characters', function() local test_case = [[ local uri = 'file:///C:/Foo/Bar/Baz.txt' return vim.uri_to_fname(uri) @@ -119,7 +119,7 @@ describe('URI methods', function() eq('C:\\Foo \\Bar\\Baz.txt', exec_lua(test_case)) end) - it('file path including Unicode charactors', function() + it('file path including Unicode characters', function() local test_case = [[ local uri = 'file:///C:/xy/%C3%A5%C3%A4%C3%B6/%C9%A7/%E6%B1%89%E8%AF%AD/%E2%86%A5/%F0%9F%A4%A6/%F0%9F%A6%84/a%CC%8A/%D8%A8%D9%90%D9%8A%D9%8E%D9%91.txt' return vim.uri_to_fname(uri) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 2b249b7a69..47a0004183 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -24,6 +24,7 @@ local rmdir = helpers.rmdir local write_file = helpers.write_file local expect_exit = helpers.expect_exit local poke_eventloop = helpers.poke_eventloop +local assert_alive = helpers.assert_alive describe('lua stdlib', function() before_each(clear) @@ -158,17 +159,20 @@ describe('lua stdlib', function() end) it("vim.str_utfindex/str_byteindex", function() - exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ"]]) - local indicies32 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,29,33,34,35,37,38,40,42,44,46,48} - local indicies16 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,28,29,33,33,34,35,37,38,40,42,44,46,48} + exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ\000ъ"]]) + local indicies32 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,29,33,34,35,37,38,40,42,44,46,48,49,51} + local indicies16 = {[0]=0,1,2,3,5,7,9,10,12,13,16,19,20,23,24,28,28,29,33,33,34,35,37,38,40,42,44,46,48,49,51} for i,k in pairs(indicies32) do eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ...)", i), i) end for i,k in pairs(indicies16) do eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ..., true)", i), i) end + matches(": index out of range$", pcall_err(exec_lua, "return vim.str_byteindex(_G.test_text, ...)", #indicies32 + 1)) + matches(": index out of range$", pcall_err(exec_lua, "return vim.str_byteindex(_G.test_text, ..., true)", #indicies16 + 1)) local i32, i16 = 0, 0 - for k = 0,48 do + local len = 51 + for k = 0,len do if indicies32[i32] < k then i32 = i32 + 1 end @@ -180,6 +184,7 @@ describe('lua stdlib', function() end eq({i32, i16}, exec_lua("return {vim.str_utfindex(_G.test_text, ...)}", k), k) end + matches(": index out of range$", pcall_err(exec_lua, "return vim.str_utfindex(_G.test_text, ...)", len + 1)) end) it("vim.str_utf_start", function() @@ -788,6 +793,11 @@ describe('lua stdlib', function() local x = vim.fn.VarArg(function() return 'foo' end, function() return 'bar' end) return #x == 2 and x[1]() == 'foo' and x[2]() == 'bar' ]])) + + -- Test for #20211 + eq('a (b) c', exec_lua([[ + return vim.fn.substitute('a b c', 'b', function(m) return '(' .. m[1] .. ')' end, 'g') + ]])) end) it('vim.fn should error when calling API function', function() @@ -896,7 +906,7 @@ describe('lua stdlib', function() ]])) -- vim.empty_dict() gives new value each time - -- equality is not overriden (still by ref) + -- equality is not overridden (still by ref) -- non-empty table uses the usual heuristics (ignores the tag) eq({false, {"foo"}, {namey="bar"}}, exec_lua([[ local aa = vim.empty_dict() @@ -1029,6 +1039,7 @@ describe('lua stdlib', function() vim.g.AddCounter = add_counter vim.g.GetCounter = get_counter vim.g.funcs = {add = add_counter, get = get_counter} + vim.g.AddParens = function(s) return '(' .. s .. ')' end ]] eq(0, eval('g:GetCounter()')) @@ -1044,6 +1055,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.g.funcs.get()]])) exec_lua([[vim.api.nvim_get_var('funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_get_var('funcs').get()]])) + eq('((foo))', eval([['foo'->AddParens()->AddParens()]])) exec_lua [[ local counter = 0 @@ -1052,6 +1064,7 @@ describe('lua stdlib', function() vim.api.nvim_set_var('AddCounter', add_counter) vim.api.nvim_set_var('GetCounter', get_counter) vim.api.nvim_set_var('funcs', {add = add_counter, get = get_counter}) + vim.api.nvim_set_var('AddParens', function(s) return '(' .. s .. ')' end) ]] eq(0, eval('g:GetCounter()')) @@ -1067,6 +1080,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.g.funcs.get()]])) exec_lua([[vim.api.nvim_get_var('funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_get_var('funcs').get()]])) + eq('((foo))', eval([['foo'->AddParens()->AddParens()]])) exec([[ function Test() @@ -1133,6 +1147,7 @@ describe('lua stdlib', function() vim.b.AddCounter = add_counter vim.b.GetCounter = get_counter vim.b.funcs = {add = add_counter, get = get_counter} + vim.b.AddParens = function(s) return '(' .. s .. ')' end ]] eq(0, eval('b:GetCounter()')) @@ -1148,6 +1163,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.b.funcs.get()]])) exec_lua([[vim.api.nvim_buf_get_var(0, 'funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_buf_get_var(0, 'funcs').get()]])) + eq('((foo))', eval([['foo'->b:AddParens()->b:AddParens()]])) exec_lua [[ local counter = 0 @@ -1156,6 +1172,7 @@ describe('lua stdlib', function() vim.api.nvim_buf_set_var(0, 'AddCounter', add_counter) vim.api.nvim_buf_set_var(0, 'GetCounter', get_counter) vim.api.nvim_buf_set_var(0, 'funcs', {add = add_counter, get = get_counter}) + vim.api.nvim_buf_set_var(0, 'AddParens', function(s) return '(' .. s .. ')' end) ]] eq(0, eval('b:GetCounter()')) @@ -1171,6 +1188,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.b.funcs.get()]])) exec_lua([[vim.api.nvim_buf_get_var(0, 'funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_buf_get_var(0, 'funcs').get()]])) + eq('((foo))', eval([['foo'->b:AddParens()->b:AddParens()]])) exec([[ function Test() @@ -1227,6 +1245,7 @@ describe('lua stdlib', function() vim.w.AddCounter = add_counter vim.w.GetCounter = get_counter vim.w.funcs = {add = add_counter, get = get_counter} + vim.w.AddParens = function(s) return '(' .. s .. ')' end ]] eq(0, eval('w:GetCounter()')) @@ -1242,6 +1261,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.w.funcs.get()]])) exec_lua([[vim.api.nvim_win_get_var(0, 'funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_win_get_var(0, 'funcs').get()]])) + eq('((foo))', eval([['foo'->w:AddParens()->w:AddParens()]])) exec_lua [[ local counter = 0 @@ -1250,6 +1270,7 @@ describe('lua stdlib', function() vim.api.nvim_win_set_var(0, 'AddCounter', add_counter) vim.api.nvim_win_set_var(0, 'GetCounter', get_counter) vim.api.nvim_win_set_var(0, 'funcs', {add = add_counter, get = get_counter}) + vim.api.nvim_win_set_var(0, 'AddParens', function(s) return '(' .. s .. ')' end) ]] eq(0, eval('w:GetCounter()')) @@ -1265,6 +1286,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.w.funcs.get()]])) exec_lua([[vim.api.nvim_win_get_var(0, 'funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_win_get_var(0, 'funcs').get()]])) + eq('((foo))', eval([['foo'->w:AddParens()->w:AddParens()]])) exec([[ function Test() @@ -1316,6 +1338,7 @@ describe('lua stdlib', function() vim.t.AddCounter = add_counter vim.t.GetCounter = get_counter vim.t.funcs = {add = add_counter, get = get_counter} + vim.t.AddParens = function(s) return '(' .. s .. ')' end ]] eq(0, eval('t:GetCounter()')) @@ -1331,6 +1354,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.t.funcs.get()]])) exec_lua([[vim.api.nvim_tabpage_get_var(0, 'funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'funcs').get()]])) + eq('((foo))', eval([['foo'->t:AddParens()->t:AddParens()]])) exec_lua [[ local counter = 0 @@ -1339,6 +1363,7 @@ describe('lua stdlib', function() vim.api.nvim_tabpage_set_var(0, 'AddCounter', add_counter) vim.api.nvim_tabpage_set_var(0, 'GetCounter', get_counter) vim.api.nvim_tabpage_set_var(0, 'funcs', {add = add_counter, get = get_counter}) + vim.api.nvim_tabpage_set_var(0, 'AddParens', function(s) return '(' .. s .. ')' end) ]] eq(0, eval('t:GetCounter()')) @@ -1354,6 +1379,7 @@ describe('lua stdlib', function() eq(5, exec_lua([[return vim.t.funcs.get()]])) exec_lua([[vim.api.nvim_tabpage_get_var(0, 'funcs').add()]]) eq(6, exec_lua([[return vim.api.nvim_tabpage_get_var(0, 'funcs').get()]])) + eq('((foo))', eval([['foo'->t:AddParens()->t:AddParens()]])) exec_lua [[ vim.cmd "tabnew" @@ -1365,11 +1391,23 @@ describe('lua stdlib', function() end) it('vim.env', function() - exec_lua [[ - vim.fn.setenv("A", 123) - ]] - eq('123', funcs.luaeval "vim.env.A") - eq(true, funcs.luaeval "vim.env.B == nil") + exec_lua([[vim.fn.setenv('A', 123)]]) + eq('123', funcs.luaeval('vim.env.A')) + exec_lua([[vim.env.A = 456]]) + eq('456', funcs.luaeval('vim.env.A')) + exec_lua([[vim.env.A = nil]]) + eq(NIL, funcs.luaeval('vim.env.A')) + + eq(true, funcs.luaeval('vim.env.B == nil')) + + command([[let $HOME = 'foo']]) + eq('foo', funcs.expand('~')) + eq('foo', funcs.luaeval('vim.env.HOME')) + exec_lua([[vim.env.HOME = nil]]) + eq('foo', funcs.expand('~')) + exec_lua([[vim.env.HOME = 'bar']]) + eq('bar', funcs.expand('~')) + eq('bar', funcs.luaeval('vim.env.HOME')) end) it('vim.v', function() @@ -1396,7 +1434,7 @@ describe('lua stdlib', function() ]] eq('', funcs.luaeval "vim.bo.filetype") eq(true, funcs.luaeval "vim.bo[BUF].modifiable") - matches("unknown option 'nosuchopt'$", + matches("no such option: 'nosuchopt'$", pcall_err(exec_lua, 'return vim.bo.nosuchopt')) matches("Expected lua string$", pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) @@ -1417,7 +1455,7 @@ describe('lua stdlib', function() eq(0, funcs.luaeval "vim.wo.cole") eq(0, funcs.luaeval "vim.wo[0].cole") eq(0, funcs.luaeval "vim.wo[1001].cole") - matches("unknown option 'notanopt'$", + matches("no such option: 'notanopt'$", pcall_err(exec_lua, 'return vim.wo.notanopt')) matches("Expected lua string$", pcall_err(exec_lua, 'return vim.wo[0][0].list')) @@ -2173,6 +2211,10 @@ describe('lua stdlib', function() eq({3,7}, exec_lua[[return {re1:match_line(0, 1, 1, 8)}]]) eq({}, exec_lua[[return {re1:match_line(0, 1, 1, 7)}]]) eq({0,3}, exec_lua[[return {re1:match_line(0, 1, 0, 7)}]]) + + -- vim.regex() error inside :silent! should not crash. #20546 + command([[silent! lua vim.regex('\\z')]]) + assert_alive() end) it('vim.defer_fn', function() @@ -2721,6 +2763,57 @@ describe('lua stdlib', function() ]] end) end) + + describe('vim.iconv', function() + it('can convert strings', function() + eq('hello', exec_lua[[ + return vim.iconv('hello', 'latin1', 'utf-8') + ]]) + end) + + it('can validate arguments', function() + eq({false, 'Expected at least 3 arguments'}, exec_lua[[ + return {pcall(vim.iconv, 'hello')} + ]]) + + eq({false, 'bad argument #3 to \'?\' (expected string)'}, exec_lua[[ + return {pcall(vim.iconv, 'hello', 'utf-8', true)} + ]]) + end) + + it('can handle bad encodings', function() + eq(NIL, exec_lua[[ + return vim.iconv('hello', 'foo', 'bar') + ]]) + end) + + it('can handle strings with NUL bytes', function() + eq(7, exec_lua[[ + local a = string.char(97, 98, 99, 0, 100, 101, 102) -- abc\0def + return string.len(vim.iconv(a, 'latin1', 'utf-8')) + ]]) + end) + + end) + + describe("vim.defaulttable", function() + it("creates nested table by default", function() + eq({ b = {c = 1 } }, exec_lua[[ + local a = vim.defaulttable() + a.b.c = 1 + return a + ]]) + end) + + it("allows to create default objects", function() + eq({ b = 1 }, exec_lua[[ + local a = vim.defaulttable(function() return 0 end) + a.b = a.b + 1 + return a + ]]) + end) + end) + end) describe('lua: builtin modules', function() diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 4e2f2ab63e..130ed73c34 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -220,7 +220,9 @@ describe('startup defaults', function() end) it("'packpath'", function() - clear() + clear{ + args_rm={'runtimepath'}, + } -- Defaults to &runtimepath. eq(meths.get_option('runtimepath'), meths.get_option('packpath')) @@ -332,17 +334,19 @@ describe('XDG defaults', function() describe('with too long XDG variables', function() before_each(function() - clear({env={ - XDG_CONFIG_HOME=(root_path .. ('/x'):rep(4096)), - XDG_CONFIG_DIRS=(root_path .. ('/a'):rep(2048) - .. env_sep.. root_path .. ('/b'):rep(2048) - .. (env_sep .. root_path .. '/c'):rep(512)), - XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)), - XDG_RUNTIME_DIR=(root_path .. ('/X'):rep(4096)), - XDG_STATE_HOME=(root_path .. ('/X'):rep(4096)), - XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048) - .. env_sep .. root_path .. ('/B'):rep(2048) - .. (env_sep .. root_path .. '/C'):rep(512)), + clear({ + args_rm={'runtimepath'}, + env={ + XDG_CONFIG_HOME=(root_path .. ('/x'):rep(4096)), + XDG_CONFIG_DIRS=(root_path .. ('/a'):rep(2048) + .. env_sep.. root_path .. ('/b'):rep(2048) + .. (env_sep .. root_path .. '/c'):rep(512)), + XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)), + XDG_RUNTIME_DIR=(root_path .. ('/X'):rep(4096)), + XDG_STATE_HOME=(root_path .. ('/X'):rep(4096)), + XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048) + .. env_sep .. root_path .. ('/B'):rep(2048) + .. (env_sep .. root_path .. '/C'):rep(512)), }}) end) @@ -405,13 +409,15 @@ describe('XDG defaults', function() describe('with XDG variables that can be expanded', function() before_each(function() - clear({env={ - XDG_CONFIG_HOME='$XDG_DATA_HOME', - XDG_CONFIG_DIRS='$XDG_DATA_DIRS', - XDG_DATA_HOME='$XDG_CONFIG_HOME', - XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR', - XDG_STATE_HOME='$XDG_CONFIG_HOME', - XDG_DATA_DIRS='$XDG_CONFIG_DIRS', + clear({ + args_rm={'runtimepath'}, + env={ + XDG_CONFIG_HOME='$XDG_DATA_HOME', + XDG_CONFIG_DIRS='$XDG_DATA_DIRS', + XDG_DATA_HOME='$XDG_CONFIG_HOME', + XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR', + XDG_STATE_HOME='$XDG_CONFIG_HOME', + XDG_DATA_DIRS='$XDG_CONFIG_DIRS', }}) end) @@ -478,12 +484,14 @@ describe('XDG defaults', function() describe('with commas', function() before_each(function() - clear({env={ - XDG_CONFIG_HOME=', , ,', - XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-', - XDG_DATA_HOME=',=,=,', - XDG_STATE_HOME=',=,=,', - XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡', + clear({ + args_rm={'runtimepath'}, + env={ + XDG_CONFIG_HOME=', , ,', + XDG_CONFIG_DIRS=',-,-,' .. env_sep .. '-,-,-', + XDG_DATA_HOME=',=,=,', + XDG_STATE_HOME=',=,=,', + XDG_DATA_DIRS=',≡,≡,' .. env_sep .. '≡,≡,≡', }}) end) diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua index a814c35a39..4fdc6ef4be 100644 --- a/test/functional/options/keymap_spec.lua +++ b/test/functional/options/keymap_spec.lua @@ -184,7 +184,7 @@ describe("'keymap' / :lmap", function() feed('il<esc>') expect('alllaaa') end) - -- This is a problem introduced when introducting :lmap and macro + -- This is a problem introduced when introducing :lmap and macro -- compatibility. There are no plans to fix this as the complexity involved -- seems too great. pending('mappings not applied to macro replay of :lnoremap', function() diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index cd7415de90..425427be54 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -418,6 +418,43 @@ describe('LSP', function() } end) + it('should detach buffer on bufwipe', function() + clear() + local result = exec_lua([[ + local server = function(dispatchers) + local closing = false + return { + request = function(method, params, callback) + if method == 'initialize' then + callback(nil, { capabilities = {} }) + end + end, + notify = function(...) + end, + is_closing = function() return closing end, + terminate = function() closing = true end + } + end + local bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_set_current_buf(bufnr) + local client_id = vim.lsp.start({ name = 'detach-dummy', cmd = server }) + assert(client_id, "lsp.start must return client_id") + local client = vim.lsp.get_client_by_id(client_id) + local num_attached_before = vim.tbl_count(client.attached_buffers) + vim.api.nvim_buf_delete(bufnr, { force = true }) + local num_attached_after = vim.tbl_count(client.attached_buffers) + return { + bufnr = bufnr, + client_id = client_id, + num_attached_before = num_attached_before, + num_attached_after = num_attached_after, + } + ]]) + eq(true, result ~= nil, "exec_lua must return result") + eq(1, result.num_attached_before) + eq(0, result.num_attached_after) + end) + it('client should return settings via workspace/configuration handler', function() local expected_handlers = { {NIL, {}, {method="shutdown", client_id=1}}; @@ -1682,6 +1719,46 @@ describe('LSP', function() end) end) + describe('apply_text_edits regression tests for #20116', function() + before_each(function() + insert(dedent([[ + Test line one + Test line two 21 char]])) + end) + describe('with LSP end column out of bounds and start column at 0', function() + it('applies edits at the end of the buffer', function() + local edits = { + make_edit(0, 0, 1, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8") + eq({'#include "whatever.h"', '#include <algorithm>'}, buf_lines(1)) + end) + it('applies edits in the middle of the buffer', function() + local edits = { + make_edit(0, 0, 0, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8") + eq({'#include "whatever.h"', '#include <algorithm>', 'Test line two 21 char'}, buf_lines(1)) + end) + end) + describe('with LSP end column out of bounds and start column NOT at 0', function() + it('applies edits at the end of the buffer', function() + local edits = { + make_edit(0, 2, 1, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8") + eq({'Te#include "whatever.h"', '#include <algorithm>'}, buf_lines(1)) + end) + it('applies edits in the middle of the buffer', function() + local edits = { + make_edit(0, 2, 0, 22, {'#include "whatever.h"\r\n#include <algorithm>\r'}); + } + exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, "utf-8") + eq({'Te#include "whatever.h"', '#include <algorithm>', 'Test line two 21 char'}, buf_lines(1)) + end) + end) + end) + describe('apply_text_document_edit', function() local target_bufnr local text_document_edit = function(editVersion) @@ -1900,6 +1977,22 @@ describe('LSP', function() exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile)) end) + it('Supports file creation in folder that needs to be created with CreateFile payload', function() + local tmpfile = helpers.tmpname() + os.remove(tmpfile) -- Should not exist, only interested in a tmpname + tmpfile = tmpfile .. '/dummy/x/' + local uri = exec_lua('return vim.uri_from_fname(...)', tmpfile) + local edit = { + documentChanges = { + { + kind = 'create', + uri = uri, + }, + } + } + exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') + eq(true, exec_lua('return vim.loop.fs_stat(...) ~= nil', tmpfile)) + end) it('createFile does not touch file if it exists and ignoreIfExists is set', function() local tmpfile = helpers.tmpname() write_file(tmpfile, 'Dummy content') @@ -2493,7 +2586,7 @@ describe('LSP', function() local mark = funcs.nvim_buf_get_mark(target_bufnr, "'") eq({ 1, 0 }, mark) - funcs.nvim_win_set_cursor(0, {2, 3}) + funcs.nvim_win_set_cursor(0, { 2, 3 }) jump(location(0, 9, 0, 9)) mark = funcs.nvim_buf_get_mark(target_bufnr, "'") @@ -2501,6 +2594,166 @@ describe('LSP', function() end) end) + describe('lsp.util.show_document', function() + local target_bufnr + local target_bufnr2 + + before_each(function() + target_bufnr = exec_lua([[ + 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 + ]]) + + target_bufnr2 = exec_lua([[ + local bufnr = vim.uri_to_bufnr("file:///fake/uri2") + local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"} + vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) + return bufnr + ]]) + end) + + local location = function(start_line, start_char, end_line, end_char, second_uri) + return { + uri = second_uri and 'file:///fake/uri2' or 'file:///fake/uri', + range = { + start = { line = start_line, character = start_char }, + ['end'] = { line = end_line, character = end_char }, + }, + } + end + + local show_document = function(msg, focus, reuse_win) + eq( + true, + exec_lua( + 'return vim.lsp.util.show_document(...)', + msg, + 'utf-16', + { reuse_win = reuse_win, focus = focus } + ) + ) + if focus == true or focus == nil then + eq(target_bufnr, exec_lua([[return vim.fn.bufnr('%')]])) + end + return { + line = exec_lua([[return vim.fn.line('.')]]), + col = exec_lua([[return vim.fn.col('.')]]), + } + end + + it('jumps to a Location if focus is true', function() + local pos = show_document(location(0, 9, 0, 9), true, true) + eq(1, pos.line) + eq(10, pos.col) + end) + + it('jumps to a Location if focus not set', function() + local pos = show_document(location(0, 9, 0, 9), nil, true) + eq(1, pos.line) + eq(10, pos.col) + end) + + it('does not add current position to jumplist if not focus', function() + funcs.nvim_win_set_buf(0, target_bufnr) + local mark = funcs.nvim_buf_get_mark(target_bufnr, "'") + eq({ 1, 0 }, mark) + + funcs.nvim_win_set_cursor(0, { 2, 3 }) + show_document(location(0, 9, 0, 9), false, true) + show_document(location(0, 9, 0, 9, true), false, true) + + mark = funcs.nvim_buf_get_mark(target_bufnr, "'") + eq({ 1, 0 }, mark) + end) + + it('does not change cursor position if not focus and not reuse_win', function() + funcs.nvim_win_set_buf(0, target_bufnr) + local cursor = funcs.nvim_win_get_cursor(0) + + show_document(location(0, 9, 0, 9), false, false) + eq(cursor, funcs.nvim_win_get_cursor(0)) + end) + + it('does not change window if not focus', function() + funcs.nvim_win_set_buf(0, target_bufnr) + local win = funcs.nvim_get_current_win() + + -- same document/bufnr + show_document(location(0, 9, 0, 9), false, true) + eq(win, funcs.nvim_get_current_win()) + + -- different document/bufnr, new window/split + show_document(location(0, 9, 0, 9, true), false, true) + eq(2, #funcs.nvim_list_wins()) + eq(win, funcs.nvim_get_current_win()) + end) + + it("respects 'reuse_win' parameter", function() + funcs.nvim_win_set_buf(0, target_bufnr) + + -- does not create a new window if the buffer is already open + show_document(location(0, 9, 0, 9), false, true) + eq(1, #funcs.nvim_list_wins()) + + -- creates a new window even if the buffer is already open + show_document(location(0, 9, 0, 9), false, false) + eq(2, #funcs.nvim_list_wins()) + end) + + it('correctly sets the cursor of the split if range is given without focus', function() + funcs.nvim_win_set_buf(0, target_bufnr) + + show_document(location(0, 9, 0, 9, true), false, true) + + local wins = funcs.nvim_list_wins() + eq(2, #wins) + table.sort(wins) + + eq({ 1, 0 }, funcs.nvim_win_get_cursor(wins[1])) + eq({ 1, 9 }, funcs.nvim_win_get_cursor(wins[2])) + end) + + it('does not change cursor of the split if not range and not focus', function() + funcs.nvim_win_set_buf(0, target_bufnr) + funcs.nvim_win_set_cursor(0, { 2, 3 }) + + exec_lua([[vim.cmd.new()]]) + funcs.nvim_win_set_buf(0, target_bufnr2) + funcs.nvim_win_set_cursor(0, { 2, 3 }) + + show_document({ uri = 'file:///fake/uri2' }, false, true) + + local wins = funcs.nvim_list_wins() + eq(2, #wins) + eq({ 2, 3 }, funcs.nvim_win_get_cursor(wins[1])) + eq({ 2, 3 }, funcs.nvim_win_get_cursor(wins[2])) + end) + + it('respects existing buffers', function() + funcs.nvim_win_set_buf(0, target_bufnr) + local win = funcs.nvim_get_current_win() + + exec_lua([[vim.cmd.new()]]) + funcs.nvim_win_set_buf(0, target_bufnr2) + funcs.nvim_win_set_cursor(0, { 2, 3 }) + local split = funcs.nvim_get_current_win() + + -- reuse win for open document/bufnr if called from split + show_document(location(0, 9, 0, 9, true), false, true) + eq({ 1, 9 }, funcs.nvim_win_get_cursor(split)) + eq(2, #funcs.nvim_list_wins()) + + funcs.nvim_set_current_win(win) + + -- reuse win for open document/bufnr if called outside the split + show_document(location(0, 9, 0, 9, true), false, true) + eq({ 1, 9 }, funcs.nvim_win_get_cursor(split)) + eq(2, #funcs.nvim_list_wins()) + end) + end) + describe('lsp.util._make_floating_popup_size', function() before_each(function() exec_lua [[ contents = @@ -3180,5 +3433,150 @@ describe('LSP', function() end, } end) + it('format formats range in visual mode', function() + local result = exec_lua([[ + local messages = {} + local server = function(dispatchers) + local closing = false + return { + request = function(method, params, callback) + table.insert(messages, { + method = method, + params = params, + }) + if method == 'initialize' then + callback(nil, { + capabilities = { + documentFormattingProvider = true, + documentRangeFormattingProvider = true, + } + }) + end + end, + notify = function(...) + end, + is_closing = function() + return closing + end, + terminate = function() + closing = true + end + } + end + local bufnr = vim.api.nvim_get_current_buf() + local client_id = vim.lsp.start({ name = 'dummy', cmd = server }) + vim.api.nvim_win_set_buf(0, bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, {'foo', 'bar'}) + vim.api.nvim_win_set_cursor(0, { 1, 0 }) + vim.cmd.normal('v') + vim.api.nvim_win_set_cursor(0, { 2, 3 }) + vim.lsp.buf.format({ bufnr = bufnr, false }) + return messages + ]]) + eq("textDocument/rangeFormatting", result[2].method) + local expected_range = { + start = { line = 0, character = 0 }, + ['end'] = { line = 1, character = 4 }, + } + eq(expected_range, result[2].params.range) + end) + end) + describe('cmd', function() + it('can connect to lsp server via rpc.connect', function() + local result = exec_lua [[ + local uv = vim.loop + local server = uv.new_tcp() + local init = nil + server:bind('127.0.0.1', 0) + server:listen(127, function(err) + assert(not err, err) + local socket = uv.new_tcp() + server:accept(socket) + socket:read_start(require('vim.lsp.rpc').create_read_loop(function(body) + init = body + socket:close() + end)) + end) + local port = server:getsockname().port + vim.lsp.start({ name = 'dummy', cmd = vim.lsp.rpc.connect('127.0.0.1', port) }) + vim.wait(1000, function() return init ~= nil end) + assert(init, "server must receive `initialize` request") + server:close() + server:shutdown() + return vim.json.decode(init) + ]] + eq(result.method, "initialize") + end) + end) + + describe('handlers', function() + it('handler can return false as response', function() + local result = exec_lua [[ + local uv = vim.loop + local server = uv.new_tcp() + local messages = {} + local responses = {} + server:bind('127.0.0.1', 0) + server:listen(127, function(err) + assert(not err, err) + local socket = uv.new_tcp() + server:accept(socket) + socket:read_start(require('vim.lsp.rpc').create_read_loop(function(body) + local payload = vim.json.decode(body) + if payload.method then + table.insert(messages, payload.method) + if payload.method == 'initialize' then + local msg = vim.json.encode({ + id = payload.id, + jsonrpc = '2.0', + result = { + capabilities = {} + }, + }) + socket:write(table.concat({'Content-Length: ', tostring(#msg), '\r\n\r\n', msg})) + elseif payload.method == 'initialized' then + local msg = vim.json.encode({ + id = 10, + jsonrpc = '2.0', + method = 'dummy', + params = {}, + }) + socket:write(table.concat({'Content-Length: ', tostring(#msg), '\r\n\r\n', msg})) + end + else + table.insert(responses, payload) + socket:close() + end + end)) + end) + local port = server:getsockname().port + local handler_called = false + vim.lsp.handlers['dummy'] = function(err, result) + handler_called = true + return false + end + local client_id = vim.lsp.start({ name = 'dummy', cmd = vim.lsp.rpc.connect('127.0.0.1', port) }) + local client = vim.lsp.get_client_by_id(client_id) + vim.wait(1000, function() return #messages == 2 and handler_called and #responses == 1 end) + server:close() + server:shutdown() + return { + messages = messages, + handler_called = handler_called, + responses = responses } + ]] + local expected = { + messages = { 'initialize', 'initialized' }, + handler_called = true, + responses = { + { + id = 10, + jsonrpc = '2.0', + result = false + } + } + } + eq(expected, result) + end) end) end) diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index c8da5a711f..3e63c5df9a 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -1,11 +1,18 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local command, eval, rawfeed = helpers.command, helpers.eval, helpers.rawfeed +local command, rawfeed = helpers.command, helpers.rawfeed local clear = helpers.clear +local exec_lua = helpers.exec_lua local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog local matches = helpers.matches +clear() +if funcs.executable('man') == 0 then + pending('missing "man" command', function() end) + return +end + describe(':Man', function() before_each(function() clear() @@ -44,7 +51,7 @@ describe(':Man', function() | ]]} - eval('man#init_pager()') + exec_lua[[require'man'.init_pager()]] screen:expect([[ ^this {b:is} {b:a} test | @@ -68,7 +75,7 @@ describe(':Man', function() | ]=]} - eval('man#init_pager()') + exec_lua[[require'man'.init_pager()]] screen:expect([[ ^this {b:is }{bi:a }{biu:test} | @@ -83,7 +90,7 @@ describe(':Man', function() rawfeed([[ ithis i<C-v><C-h>is<C-v><C-h>s あ<C-v><C-h>あ test with _<C-v><C-h>ö_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>̃_<C-v><C-h>c_<C-v><C-h>k te<C-v><ESC>[3mxt¶<C-v><ESC>[0m<ESC>]]) - eval('man#init_pager()') + exec_lua[[require'man'.init_pager()]] screen:expect([[ ^this {b:is} {b:あ} test | @@ -99,7 +106,7 @@ describe(':Man', function() i_<C-v><C-h>_b<C-v><C-h>be<C-v><C-h>eg<C-v><C-h>gi<C-v><C-h>in<C-v><C-h>ns<C-v><C-h>s m<C-v><C-h>mi<C-v><C-h>id<C-v><C-h>d_<C-v><C-h>_d<C-v><C-h>dl<C-v><C-h>le<C-v><C-h>e _<C-v><C-h>m_<C-v><C-h>i_<C-v><C-h>d_<C-v><C-h>__<C-v><C-h>d_<C-v><C-h>l_<C-v><C-h>e<ESC>]]) - eval('man#init_pager()') + exec_lua[[require'man'.init_pager()]] screen:expect([[ {b:^_begins} | @@ -115,7 +122,7 @@ describe(':Man', function() i· ·<C-v><C-h>· +<C-v><C-h>o +<C-v><C-h>+<C-v><C-h>o<C-v><C-h>o double<ESC>]]) - eval('man#init_pager()') + exec_lua[[require'man'.init_pager()]] screen:expect([[ ^· {b:·} | @@ -132,7 +139,7 @@ describe(':Man', function() <C-v><C-[>[44m 4 <C-v><C-[>[45m 5 <C-v><C-[>[46m 6 <C-v><C-[>[47m 7 <C-v><C-[>[100m 8 <C-v><C-[>[101m 9 <C-v><C-[>[102m 10 <C-v><C-[>[103m 11 <C-v><C-[>[104m 12 <C-v><C-[>[105m 13 <C-v><C-[>[106m 14 <C-v><C-[>[107m 15 <C-v><C-[>[48:5:16m 16 <ESC>]]) - eval('man#init_pager()') + exec_lua[[require'man'.init_pager()]] screen:expect([[ ^ 0 1 2 3 | diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 6f22f865e6..dda8077f05 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local clear = helpers.clear local eq, meths, nvim_eval, nvim_command, nvim, exc_exec, funcs, nvim_feed, curbuf = helpers.eq, helpers.meths, helpers.eval, helpers.command, helpers.nvim, helpers.exc_exec, @@ -2538,13 +2539,26 @@ describe('ftplugin/shada.vim', function() end) describe('syntax/shada.vim', function() - local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) + local epoch = os.date('!%Y-%m-%dT%H:%M:%S', 0) before_each(reset) it('works', function() nvim_command('syntax on') nvim_command('setlocal syntax=shada') nvim_command('set laststatus&') + local screen = Screen.new(60, 37) + screen:set_default_attr_ids { + [1] = {bold = true, foreground = Screen.colors.Brown}; + [2] = {foreground = tonumber('0x6a0dad')}; + [3] = {foreground = Screen.colors.Fuchsia}; + [4] = {foreground = Screen.colors.Blue1}; + [5] = {bold = true, foreground = Screen.colors.SeaGreen4}; + [6] = {foreground = Screen.colors.SlateBlue}; + [7] = {bold = true, reverse = true}; + [8] = {bold = true, foreground = Screen.colors.Blue}; + } + screen:attach() + curbuf('set_lines', 0, 1, true, { 'Header with timestamp ' .. epoch .. ':', ' % Key Value', @@ -2580,6 +2594,46 @@ describe('syntax/shada.vim', function() ' % Key Description________ Value', ' + se place cursor at end TRUE', }) + screen:expect{grid=[=[ + {1:^Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | + {2: % Key Value} | + {1: +} {3:t } {1:"}{3:test}{1:"} | + {1:Jump} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | + {2: % Key________ Description Value} | + {1: +} {3:n } {4:name } {3:'A'} | + {1: +} {3:f } {4:file name } {1:["}{3:foo}{1:"]} | + {1: +} {3:l } {4:line number} {3:2} | + {1: +} {3:c } {4:column } {3:-200} | + {1:Register} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | + {2: % Key Description Value} | + {1: +} {3:rc } {4:contents } {1:@} | + {1: | -} {1:{"}{3:abcdefghijklmnopqrstuvwxyz}{1:":} {3:1.0}{1:}} | + {1: +} {3:rt } {4:type } {1:CHARACTERWISE} | + {1: +} {3:rt } {4:type } {1:LINEWISE} | + {1: +} {3:rt } {4:type } {1:BLOCKWISE} | + {1:Replacement string} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | + {2: @ Description__________ Value} | + {1: -} {4::s replacement string} {1:CMD} | + {1: -} {4::s replacement string} {1:SEARCH} | + {1: -} {4::s replacement string} {1:EXPR} | + {1: -} {4::s replacement string} {1:INPUT} | + {1: -} {4::s replacement string} {1:DEBUG} | + {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | + {4: # Expected array of maps} | + = {1:[{="}{3:a}{1:":} {1:+(}{5:10}{1:)"}{3:ac}{6:\0}{3:df}{6:\n}{3:gi}{6:\"}{3:tt\.}{1:",} {1:TRUE:} {1:FALSE},} {1:[NIL,} {1:+(}{5:-}| + {5:10}{1:)""]]} | + {1:Buffer list} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | + {2: % Key Description Value} | + | + {2: % Key Description Value} | + {1:Header} with timestamp 1970{1:-}01{1:-}01{1:T}00{1::}00{1::}00: | + {2: % Key Description________ Value} | + {1: +} {3:se } {4:place cursor at end} {1:TRUE} | + {8:~ }| + {7:[No Name] [+] }| + | + ]=]} + nvim_command([[ function GetSyntax() let lines = [] @@ -2613,7 +2667,7 @@ describe('syntax/shada.vim', function() year = htsnum(os.date('%Y', 0)), month = htsnum(os.date('%m', 0)), day = htsnum(os.date('%d', 0)), - hour = htsnum(os.date('%H', 0)), + hour = htsnum(os.date('!%H', 0)), minute = htsnum(os.date('%M', 0)), second = htsnum(os.date('%S', 0)), } @@ -2768,9 +2822,8 @@ describe('syntax/shada.vim', function() {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'INPUT'}, }, { - {{'ShaDaEntryArrayEntryStart'}, ' - '}, - {{'ShaDaEntryArrayDescription'}, ':s replacement string '}, - {{'ShaDaMsgpackShaDaKeyword'}, 'DEBUG'}, + as(), ad(':s replacement string '), + {{'ShaDaEntryArray', 'ShaDaMsgpackShaDaKeyword'}, 'DEBUG'}, }, { hname('Buffer list'), h(' with timestamp '), @@ -2872,10 +2925,10 @@ describe('syntax/shada.vim', function() mlh(' % Key Description________ Value'), }, { - {{'ShaDaEntryMapLongEntryStart'}, ' + '}, - {{'ShaDaEntryMapLongKey'}, 'se '}, - {{'ShaDaEntryMapLongDescription'}, 'place cursor at end '}, - {{'ShaDaMsgpackKeyword'}, 'TRUE'}, + mles(' + '), + mlk('se '), + mld('place cursor at end '), + {{'ShaDaEntryMapLong', 'ShaDaMsgpackKeyword'}, 'TRUE'}, }, } eq(exp, act) diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index fbaef3ae00..401dc84ccc 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -310,18 +310,18 @@ describe('clipboard (with fake clipboard.vim)', function() insert([[ text: first line - secound line + second line third line]]) feed('G"+dd"*dddd"+p"*pp') expect([[ text: third line - secound line + second line first line]]) -- linewise selection should be encoded as an extra newline eq({{'third line', ''}, 'V'}, eval("g:test_clip['+']")) - eq({{'secound line', ''}, 'V'}, eval("g:test_clip['*']")) + eq({{'second line', ''}, 'V'}, eval("g:test_clip['*']")) end) it('handles null bytes when pasting and in getreg', function() @@ -477,7 +477,7 @@ describe('clipboard (with fake clipboard.vim)', function() expect("indeed star") end) - it('unamed operations work even if the provider fails', function() + it('unnamed operations work even if the provider fails', function() insert('the text') feed('yy') feed_command("let g:cliperror = 1") @@ -511,7 +511,7 @@ describe('clipboard (with fake clipboard.vim)', function() eq('textstar', meths.get_current_line()) end) - it('Block paste works currectly', function() + it('Block paste works correctly', function() insert([[ aabbcc ddeeff @@ -559,7 +559,7 @@ describe('clipboard (with fake clipboard.vim)', function() eq({{'really unnamed', ''}, 'V'}, eval("g:test_clip['+']")) eq({{'really unnamed', ''}, 'V'}, eval("g:test_clip['*']")) - -- unnamedplus takes predecence when pasting + -- unnamedplus takes precedence when pasting eq('+', eval('v:register')) feed_command("let g:test_clip['+'] = ['the plus','']") feed_command("let g:test_clip['*'] = ['the star','']") diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index d10a2facbb..f5a81eb2ef 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -238,6 +238,15 @@ describe('ShaDa support code', function() eq('', meths.get_option('shada')) end) + it('setting &shada gives proper error message on missing number', function() + eq([[Vim(set):E526: Missing number after <">: shada="]], + exc_exec([[set shada=\"]])) + for _, c in ipairs({"'", "/", ":", "<", "@", "s"}) do + eq(([[Vim(set):E526: Missing number after <%s>: shada=%s]]):format(c, c), + exc_exec(([[set shada=%s]]):format(c))) + end + end) + it('does not crash when ShaDa file directory is not writable', function() if helpers.pending_win32(pending) then return end diff --git a/test/functional/terminal/altscreen_spec.lua b/test/functional/terminal/altscreen_spec.lua index 155a156d15..e4e1aa5fa2 100644 --- a/test/functional/terminal/altscreen_spec.lua +++ b/test/functional/terminal/altscreen_spec.lua @@ -126,13 +126,13 @@ describe(':terminal altscreen', function() wait_removal() feed('<c-\\><c-n>4k') screen:expect([[ - ^line3 | + ^ | | | rows: 4, cols: 50 | | ]]) - eq(8, curbuf('line_count')) + eq(9, curbuf('line_count')) end) describe('and after exit', function() @@ -142,15 +142,11 @@ describe(':terminal altscreen', function() end) it('restore buffer state', function() - -- FIXME(tarruda): Note that the last line was lost after restoring the - -- screen. This is a libvterm bug: When the main screen is restored it - -- seems to "cut" lines that would have been left below the new visible - -- screen. screen:expect([[ - line4 | line5 | line6 | line7 | + line8 | {3:-- TERMINAL --} | ]]) end) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 23430a620b..9d10f43ec6 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -4,6 +4,7 @@ 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 +local pcall_err = helpers.pcall_err local eq, neq = helpers.eq, helpers.neq local meths = helpers.meths local retry = helpers.retry @@ -339,6 +340,11 @@ describe(':terminal buffer', function() ]]} eq('t', funcs.mode(1)) end) + + it('writing to an existing file with :w fails #13549', function() + eq('Vim(write):E13: File exists (add ! to override)', + pcall_err(command, 'write test/functional/fixtures/tty-test.c')) + end) end) describe('No heap-buffer-overflow when using', function() diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 23b69319f0..36f9f90143 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -10,6 +10,7 @@ local retry = helpers.retry local ok = helpers.ok local iswin = helpers.iswin local command = helpers.command +local uname = helpers.uname describe(':terminal', function() local screen @@ -45,6 +46,9 @@ describe(':terminal', function() end) it("reads output buffer on terminal reporting #4151", function() + if uname() == 'freebsd' then + pending('Failing FreeBSD test') + end if helpers.pending_win32(pending) then return end if iswin() then feed_command([[terminal powershell -NoProfile -NoLogo -Command Write-Host -NoNewline "\"$([char]27)[6n\""; Start-Sleep -Milliseconds 500 ]]) diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index bcfd3559e6..d69f3207f1 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -31,6 +31,8 @@ local function set_bg(num) feed_termcode('[48;5;'..num..'m') end local function set_bold() feed_termcode('[1m') end local function set_italic() feed_termcode('[3m') end local function set_underline() feed_termcode('[4m') end +local function set_underdouble() feed_termcode('[4:2m') end +local function set_undercurl() feed_termcode('[4:3m') end local function set_strikethrough() feed_termcode('[9m') end local function clear_attrs() feed_termcode('[0;10m') end -- mouse @@ -116,6 +118,8 @@ return { set_bold = set_bold, set_italic = set_italic, set_underline = set_underline, + set_underdouble = set_underdouble, + set_undercurl = set_undercurl, set_strikethrough = set_strikethrough, clear_attrs = clear_attrs, enable_mouse = enable_mouse, diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index 28ca07d815..a119d4acd3 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -26,6 +26,8 @@ describe(':terminal highlight', function() [9] = {foreground = 130}, [10] = {reverse = true}, [11] = {background = 11}, + [12] = {bold = true, underdouble = true}, + [13] = {italic = true, undercurl = true}, }) screen:attach({rgb=false}) command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) @@ -114,6 +116,14 @@ describe(':terminal highlight', function() thelpers.set_underline() thelpers.set_strikethrough() end) + descr('bold and underdouble', 12, function() + thelpers.set_bold() + thelpers.set_underdouble() + end) + descr('italics and undercurl', 13, function() + thelpers.set_italic() + thelpers.set_undercurl() + end) end) it(':terminal highlight has lower precedence than editor #9964', function() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 99f69ef556..3c56ad5f79 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -21,6 +21,7 @@ local nvim_set = helpers.nvim_set local ok = helpers.ok local read_file = helpers.read_file local funcs = helpers.funcs +local meths = helpers.meths if helpers.pending_win32(pending) then return end @@ -297,6 +298,199 @@ describe('TUI', function() ]], attrs) end) + it('accepts mouse wheel events #19992', function() + child_session:request('nvim_command', [[ + set number nostartofline nowrap mousescroll=hor:1,ver:1 + call setline(1, repeat([join(range(10), '----')], 10)) + vsplit + ]]) + screen:expect([[ + {11: 1 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----| + {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelDown> in active window + feed_data('\027[<65;8;1M') + screen:expect([[ + {11: 2 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 4 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 5 }0----1----2----3----4│{11: 4 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelDown> in inactive window + feed_data('\027[<65;48;1M') + screen:expect([[ + {11: 2 }{1:0}----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----| + {11: 5 }0----1----2----3----4│{11: 5 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelRight> in active window + feed_data('\027[<67;8;1M') + screen:expect([[ + {11: 2 }{1:-}---1----2----3----4-│{11: 2 }0----1----2----3----| + {11: 3 }----1----2----3----4-│{11: 3 }0----1----2----3----| + {11: 4 }----1----2----3----4-│{11: 4 }0----1----2----3----| + {11: 5 }----1----2----3----4-│{11: 5 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelRight> in inactive window + feed_data('\027[<67;48;1M') + screen:expect([[ + {11: 2 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4| + {11: 3 }----1----2----3----4-│{11: 3 }----1----2----3----4| + {11: 4 }----1----2----3----4-│{11: 4 }----1----2----3----4| + {11: 5 }----1----2----3----4-│{11: 5 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelDown> in active window + feed_data('\027[<69;8;1M') + screen:expect([[ + {11: 5 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4| + {11: 6 }----1----2----3----4-│{11: 3 }----1----2----3----4| + {11: 7 }----1----2----3----4-│{11: 4 }----1----2----3----4| + {11: 8 }----1----2----3----4-│{11: 5 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelDown> in inactive window + feed_data('\027[<69;48;1M') + screen:expect([[ + {11: 5 }{1:-}---1----2----3----4-│{11: 5 }----1----2----3----4| + {11: 6 }----1----2----3----4-│{11: 6 }----1----2----3----4| + {11: 7 }----1----2----3----4-│{11: 7 }----1----2----3----4| + {11: 8 }----1----2----3----4-│{11: 8 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelRight> in active window + feed_data('\027[<71;8;1M') + screen:expect([[ + {11: 5 }{1:-}---6----7----8----9 │{11: 5 }----1----2----3----4| + {11: 6 }----6----7----8----9 │{11: 6 }----1----2----3----4| + {11: 7 }----6----7----8----9 │{11: 7 }----1----2----3----4| + {11: 8 }----6----7----8----9 │{11: 8 }----1----2----3----4| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelRight> in inactive window + feed_data('\027[<71;48;1M') + screen:expect([[ + {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----| + {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----| + {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----| + {11: 8 }----6----7----8----9 │{11: 8 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelUp> in active window + feed_data('\027[<64;8;1M') + screen:expect([[ + {11: 4 }----6----7----8----9 │{11: 5 }5----6----7----8----| + {11: 5 }{1:-}---6----7----8----9 │{11: 6 }5----6----7----8----| + {11: 6 }----6----7----8----9 │{11: 7 }5----6----7----8----| + {11: 7 }----6----7----8----9 │{11: 8 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelUp> in inactive window + feed_data('\027[<64;48;1M') + screen:expect([[ + {11: 4 }----6----7----8----9 │{11: 4 }5----6----7----8----| + {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----| + {11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----| + {11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelLeft> in active window + feed_data('\027[<66;8;1M') + screen:expect([[ + {11: 4 }5----6----7----8----9│{11: 4 }5----6----7----8----| + {11: 5 }5{1:-}---6----7----8----9│{11: 5 }5----6----7----8----| + {11: 6 }5----6----7----8----9│{11: 6 }5----6----7----8----| + {11: 7 }5----6----7----8----9│{11: 7 }5----6----7----8----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <ScrollWheelLeft> in inactive window + feed_data('\027[<66;48;1M') + screen:expect([[ + {11: 4 }5----6----7----8----9│{11: 4 }-5----6----7----8---| + {11: 5 }5{1:-}---6----7----8----9│{11: 5 }-5----6----7----8---| + {11: 6 }5----6----7----8----9│{11: 6 }-5----6----7----8---| + {11: 7 }5----6----7----8----9│{11: 7 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelUp> in active window + feed_data('\027[<68;8;1M') + screen:expect([[ + {11: 1 }5----6----7----8----9│{11: 4 }-5----6----7----8---| + {11: 2 }5----6----7----8----9│{11: 5 }-5----6----7----8---| + {11: 3 }5----6----7----8----9│{11: 6 }-5----6----7----8---| + {11: 4 }5{1:-}---6----7----8----9│{11: 7 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelUp> in inactive window + feed_data('\027[<68;48;1M') + screen:expect([[ + {11: 1 }5----6----7----8----9│{11: 1 }-5----6----7----8---| + {11: 2 }5----6----7----8----9│{11: 2 }-5----6----7----8---| + {11: 3 }5----6----7----8----9│{11: 3 }-5----6----7----8---| + {11: 4 }5{1:-}---6----7----8----9│{11: 4 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelLeft> in active window + feed_data('\027[<70;8;1M') + screen:expect([[ + {11: 1 }0----1----2----3----4│{11: 1 }-5----6----7----8---| + {11: 2 }0----1----2----3----4│{11: 2 }-5----6----7----8---| + {11: 3 }0----1----2----3----4│{11: 3 }-5----6----7----8---| + {11: 4 }0----1----2----3----{1:4}│{11: 4 }-5----6----7----8---| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- <S-ScrollWheelLeft> in inactive window + feed_data('\027[<70;48;1M') + screen:expect([[ + {11: 1 }0----1----2----3----4│{11: 1 }0----1----2----3----| + {11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----| + {11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----| + {11: 4 }0----1----2----3----{1:4}│{11: 4 }0----1----2----3----| + {5:[No Name] [+] }{1:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + end) + it('accepts keypad keys from kitty keyboard protocol #19180', function() feed_data('i') feed_data(funcs.nr2char(57399)) -- KP_0 @@ -473,6 +667,57 @@ describe('TUI', function() ]], attrs) end) + it('mouse events work with right-click menu', function() + child_session:request('nvim_command', [[ + call setline(1, 'popup menu test') + set mouse=a mousemodel=popup + + aunmenu PopUp + menu PopUp.foo :let g:menustr = 'foo'<CR> + menu PopUp.bar :let g:menustr = 'bar'<CR> + menu PopUp.baz :let g:menustr = 'baz'<CR> + highlight Pmenu ctermbg=NONE ctermfg=NONE cterm=underline,reverse + highlight PmenuSel ctermbg=NONE ctermfg=NONE cterm=underline,reverse,bold + ]]) + local attrs = screen:get_default_attr_ids() + attrs[11] = {underline = true, reverse = true} + attrs[12] = {underline = true, reverse = true, bold = true} + meths.input_mouse('right', 'press', '', 0, 0, 4) + screen:expect([[ + {1:p}opup menu test | + {4:~ }{11: foo }{4: }| + {4:~ }{11: bar }{4: }| + {4:~ }{11: baz }{4: }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]], attrs) + meths.input_mouse('right', 'release', '', 0, 0, 4) + screen:expect_unchanged() + meths.input_mouse('move', '', '', 0, 3, 6) + screen:expect([[ + {1:p}opup menu test | + {4:~ }{11: foo }{4: }| + {4:~ }{11: bar }{4: }| + {4:~ }{12: baz }{4: }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]], attrs) + meths.input_mouse('left', 'press', '', 0, 2, 6) + screen:expect([[ + {1:p}opup menu test | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + :let g:menustr = 'bar' | + {3:-- TERMINAL --} | + ]], attrs) + meths.input_mouse('left', 'release', '', 0, 2, 6) + screen:expect_unchanged() + end) + it('paste: Insert mode', function() -- "bracketed paste" feed_data('i""\027i\027[200~') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 5ec0a8a060..ae3f42ff0a 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -5,12 +5,14 @@ local clear = helpers.clear local insert = helpers.insert local exec_lua = helpers.exec_lua local feed = helpers.feed -local pending_c_parser = helpers.pending_c_parser +local command = helpers.command +local meths = helpers.meths +local eq = helpers.eq before_each(clear) local hl_query = [[ - (ERROR) @ErrorMsg + (ERROR) @error "if" @keyword "else" @keyword @@ -23,23 +25,24 @@ local hl_query = [[ "enum" @type "extern" @type - (string_literal) @string.nonexistent-specializer-for-string.should-fallback-to-string + ; nonexistent specializer for string should fallback to string + (string_literal) @string.nonexistent_specializer (number_literal) @number (char_literal) @string (type_identifier) @type - ((type_identifier) @Special (#eq? @Special "LuaRef")) + ((type_identifier) @constant.builtin (#eq? @constant.builtin "LuaRef")) (primitive_type) @type (sized_type_specifier) @type ; Use lua regexes - ((identifier) @Identifier (#contains? @Identifier "lua_")) + ((identifier) @function (#contains? @function "lua_")) ((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$")) - ((identifier) @Normal (#vim-match? @Constant "^lstate$")) + ((identifier) @Normal (#vim-match? @Normal "^lstate$")) - ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right)) + ((binary_expression left: (identifier) @warning.left right: (identifier) @warning.right) (#eq? @warning.left @warning.right)) (comment) @comment ]] @@ -103,11 +106,11 @@ describe('treesitter highlighting', function() } exec_lua([[ hl_query = ... ]], hl_query) + command [[ hi link @error ErrorMsg ]] + command [[ hi link @warning WarningMsg ]] end) it('is updated with edits', function() - if pending_c_parser(pending) then return end - insert(hl_text) screen:expect{grid=[[ /// Schedule Lua callback on main loop's event queue | @@ -271,8 +274,6 @@ describe('treesitter highlighting', function() end) it('is updated with :sort', function() - if pending_c_parser(pending) then return end - insert(test_text) exec_lua [[ local parser = vim.treesitter.get_parser(0, "c") @@ -346,8 +347,6 @@ describe('treesitter highlighting', function() end) it("supports with custom parser", function() - if pending_c_parser(pending) then return end - screen:set_default_attr_ids { [1] = {bold = true, foreground = Screen.colors.SeaGreen4}; } @@ -412,8 +411,6 @@ describe('treesitter highlighting', function() end) it("supports injected languages", 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)) @@ -474,8 +471,6 @@ describe('treesitter highlighting', function() end) it("supports overriding queries, like ", 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)) @@ -515,8 +510,6 @@ describe('treesitter highlighting', function() end) it("supports highlighting with custom highlight groups", function() - if pending_c_parser(pending) then return end - insert(hl_text) exec_lua [[ @@ -547,7 +540,7 @@ describe('treesitter highlighting', function() -- This will change ONLY the literal strings to look like comments -- The only literal string is the "vim.schedule: expected function" in this test. - exec_lua [[vim.cmd("highlight link cString comment")]] + exec_lua [[vim.cmd("highlight link @string.nonexistent_specializer comment")]] screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | @@ -572,8 +565,6 @@ describe('treesitter highlighting', function() 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)) @@ -589,9 +580,9 @@ describe('treesitter highlighting', function() -- 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:#define READ_STRING(x, y) (}{12:char_u}{8: *)read_string((x), (}{12:size_t}{8:)(y))}| + {8:#define foo }{12:void}{8: main() { \} | + {8: }{12:return}{8: 42; \} | {8: }} | ^ | {1:~ }| @@ -612,11 +603,14 @@ describe('treesitter highlighting', function() -- bold will not be overwritten at the moment [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Grey100}; }} + + eq({ + {capture='Error', metadata = { priority='101' }}; + {capture='type', metadata = { } }; + }, exec_lua [[ return vim.treesitter.get_captures_at_pos(0, 0, 2) ]]) end) it("allows to use captures with dots (don't use fallback when specialization of foo exists)", function() - if pending_c_parser(pending) then return end - insert([[ char* x = "Will somebody ever read this?"; ]]) @@ -642,11 +636,13 @@ describe('treesitter highlighting', function() | ]]} + command [[ + hi link @foo.bar Type + hi link @foo String + ]] exec_lua [[ local parser = vim.treesitter.get_parser(0, "c", {}) local highlighter = vim.treesitter.highlighter - highlighter.hl_map['foo.bar'] = 'Type' - highlighter.hl_map['foo'] = 'String' test_hl = highlighter.new(parser, {queries = {c = "(primitive_type) @foo.bar (string_literal) @foo"}}) ]] @@ -670,10 +666,32 @@ describe('treesitter highlighting', function() {1:~ }| | ]]} + + -- clearing specialization reactivates fallback + command [[ hi clear @foo.bar ]] + screen:expect{grid=[[ + {5:char}* x = {5:"Will somebody ever read this?"}; | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} end) it("supports conceal attribute", function() - if pending_c_parser(pending) then return end insert(hl_text) -- conceal can be empty or a single cchar. @@ -712,32 +730,26 @@ describe('treesitter highlighting', function() ]]} end) - it("hl_map has the correct fallback behavior", function() - exec_lua [[ - local hl_map = vim.treesitter.highlighter.hl_map - hl_map["foo"] = 1 - hl_map["foo.bar"] = 2 - hl_map["foo.bar.baz"] = 3 - - assert(hl_map["foo"] == 1) - assert(hl_map["foo.a.b.c.d"] == 1) - assert(hl_map["foo.bar"] == 2) - assert(hl_map["foo.bar.a.b.c.d"] == 2) - assert(hl_map["foo.bar.baz"] == 3) - assert(hl_map["foo.bar.baz.d"] == 3) - - hl_map["FOO"] = 1 - hl_map["FOO.BAR"] = 2 - assert(hl_map["FOO.BAR.BAZ"] == 2) - - hl_map["foo.missing.exists"] = 3 - assert(hl_map["foo.missing"] == 1) - assert(hl_map["foo.missing.exists"] == 3) - assert(hl_map["foo.missing.exists.bar"] == 3) - assert(hl_map["total.nonsense.but.a.lot.of.dots"] == nil) - -- It will not perform a second look up of this variable but return a sentinel value - assert(hl_map["total.nonsense.but.a.lot.of.dots"] == "__notfound") - ]] - + it("@foo.bar groups has the correct fallback behavior", function() + local get_hl = function(name) return meths.get_hl_by_name(name,1).foreground end + meths.set_hl(0, "@foo", {fg = 1}) + meths.set_hl(0, "@foo.bar", {fg = 2}) + meths.set_hl(0, "@foo.bar.baz", {fg = 3}) + + eq(1, get_hl"@foo") + eq(1, get_hl"@foo.a.b.c.d") + eq(2, get_hl"@foo.bar") + eq(2, get_hl"@foo.bar.a.b.c.d") + eq(3, get_hl"@foo.bar.baz") + eq(3, get_hl"@foo.bar.baz.d") + + -- lookup is case insensitive + eq(2, get_hl"@FOO.BAR.SPAM") + + meths.set_hl(0, "@foo.missing.exists", {fg = 3}) + eq(1, get_hl"@foo.missing") + eq(3, get_hl"@foo.missing.exists") + eq(3, get_hl"@foo.missing.exists.bar") + eq(nil, get_hl"@total.nonsense.but.a.lot.of.dots") end) end) diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 30585be328..ed84dedb5a 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -6,11 +6,11 @@ local command = helpers.command local exec_lua = helpers.exec_lua local pcall_err = helpers.pcall_err local matches = helpers.matches -local pending_c_parser = helpers.pending_c_parser +local insert = helpers.insert before_each(clear) -describe('treesitter API', function() +describe('treesitter language API', function() -- error tests not requiring a parser library it('handles missing language', function() eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", @@ -26,11 +26,12 @@ describe('treesitter API', function() eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')")) + + matches("Error executing lua: Failed to load parser: uv_dlsym: .+", + pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "borklang")')) end) it('inspects language', function() - if pending_c_parser(pending) then return end - local keys, fields, symbols = unpack(exec_lua([[ local lang = vim.treesitter.inspect_language('c') local keys, symbols = {}, {} @@ -70,7 +71,6 @@ describe('treesitter API', function() end) it('checks if vim.treesitter.get_parser tries to create a new parser on filetype change', function () - if pending_c_parser(pending) then return end command("set filetype=c") -- Should not throw an error when filetype is c eq('c', exec_lua("return vim.treesitter.get_parser(0):lang()")) @@ -79,5 +79,33 @@ describe('treesitter API', function() eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0)")) end) + + it('retrieve the tree given a range', function () + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + langtree = vim.treesitter.get_parser(0, "c") + tree = langtree:tree_for_range({1, 3, 1, 3}) + ]]) + + eq('<node translation_unit>', exec_lua('return tostring(tree:root())')) + end) + + it('retrieve the node given a range', function () + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + langtree = vim.treesitter.get_parser(0, "c") + node = langtree:named_node_for_range({1, 3, 1, 3}) + ]]) + + eq('<node primitive_type>', exec_lua('return tostring(node)')) + end) end) diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index 21c287644e..a82dce47b7 100644 --- a/test/functional/treesitter/node_spec.lua +++ b/test/functional/treesitter/node_spec.lua @@ -4,7 +4,6 @@ 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) @@ -15,10 +14,6 @@ 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) { @@ -59,4 +54,53 @@ describe('treesitter node API', function() exec_lua 'node = node:prev_named_sibling()' eq('int x', lua_eval('node_text(node)')) end) + + it('can retrieve the children of a node', function() + insert([[ + int main() { + int x = 3; + }]]) + + local len = exec_lua([[ + tree = vim.treesitter.get_parser(0, "c"):parse()[1] + node = tree:root():child(0) + children = node:named_children() + + return #children + ]]) + + eq(3, len) + eq('<node compound_statement>', lua_eval('tostring(children[3])')) + end) + + it('can retrieve the tree root given a node', function() + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + tree = vim.treesitter.get_parser(0, "c"):parse()[1] + root = tree:root() + node = root:child(0):child(2) + ]]) + + eq(lua_eval('tostring(root)'), lua_eval('tostring(node:root())')) + end) + + it('can compute the byte length of a node', function() + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + tree = vim.treesitter.get_parser(0, "c"):parse()[1] + root = tree:root() + child = root:child(0):child(0) + ]]) + + eq(28, lua_eval('root:byte_length()')) + eq(3, lua_eval('child:byte_length()')) + end) end) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 7f3b0e770a..ccbd55df0e 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -5,13 +5,11 @@ local eq = helpers.eq local insert = helpers.insert local exec_lua = helpers.exec_lua local feed = helpers.feed -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) then return end @@ -249,7 +247,6 @@ void ui_refresh(void) end) it('supports getting text of multiline node', function() - if pending_c_parser(pending) then return end insert(test_text) local res = exec_lua([[ local parser = vim.treesitter.get_parser(0, "c") diff --git a/test/functional/treesitter/utils_spec.lua b/test/functional/treesitter/utils_spec.lua new file mode 100644 index 0000000000..7f5a864c3d --- /dev/null +++ b/test/functional/treesitter/utils_spec.lua @@ -0,0 +1,31 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local insert = helpers.insert +local eq = helpers.eq +local exec_lua = helpers.exec_lua + +before_each(clear) + +describe('treesitter utils', function() + before_each(clear) + + it('can find an ancestor', function() + + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + root = tree:root() + ancestor = root:child(0) + child = ancestor:child(0) + ]]) + + eq(true, exec_lua('return vim.treesitter.is_ancestor(ancestor, child)')) + eq(false, exec_lua('return vim.treesitter.is_ancestor(child, ancestor)')) + end) +end) diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index 7c0831bd09..46bfae8de2 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -762,7 +762,7 @@ describe('Buffer highlighting', function() local s1 = {{'Köttbullar', 'Comment'}, {'Kräuterbutter'}} local s2 = {{'こんにちは', 'Comment'}} - -- TODO: only a virtual text from the same ns curretly overrides + -- TODO: only a virtual text from the same ns currently overrides -- an existing virtual text. We might add a prioritation system. set_virtual_text(id1, 0, s1, {}) eq({{1, 0, 0, { diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index db13647cc6..8003947078 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -5,6 +5,7 @@ local source = helpers.source local command = helpers.command local assert_alive = helpers.assert_alive local uname = helpers.uname +local exec = helpers.exec local eval = helpers.eval local eq = helpers.eq @@ -821,16 +822,27 @@ describe('statusline is redrawn on entering cmdline', function() ]]} end) - it('but not with scrolled messages', function() - command('set statusline=%{mode()}') - screen:try_resize(35,10) + it('with scrolled messages', function() + screen:try_resize(35,14) + exec([[ + let g:count = 0 + autocmd CmdlineEnter * let g:count += 1 + split + resize 1 + setlocal statusline=%{mode()}%{g:count} + setlocal winbar=%{mode()}%{g:count} + ]]) feed(':echoerr doesnotexist<cr>') screen:expect{grid=[[ + {9:c1 }| + | + {3:c1 }| | {1:~ }| {1:~ }| {1:~ }| {1:~ }| + {1:~ }| {3: }| {4:E121: Undefined variable: doesnotex}| {4:ist} | @@ -839,8 +851,12 @@ describe('statusline is redrawn on entering cmdline', function() ]]} feed(':echoerr doesnotexist<cr>') screen:expect{grid=[[ + {9:c2 }| + | + {3:c2 }| | {1:~ }| + {1:~ }| {3: }| {4:E121: Undefined variable: doesnotex}| {4:ist} | @@ -853,6 +869,10 @@ describe('statusline is redrawn on entering cmdline', function() feed(':echoerr doesnotexist<cr>') screen:expect{grid=[[ + {9:c3 }| + | + {3:c3 }| + {3: }| {4:E121: Undefined variable: doesnotex}| {4:ist} | {5:Press ENTER or type command to cont}| @@ -867,7 +887,11 @@ describe('statusline is redrawn on entering cmdline', function() feed('<cr>') screen:expect{grid=[[ + {9:n3 }| ^ | + {3:n3 }| + | + {1:~ }| {1:~ }| {1:~ }| {1:~ }| @@ -875,7 +899,7 @@ describe('statusline is redrawn on entering cmdline', function() {1:~ }| {1:~ }| {1:~ }| - {3:n }| + {2:[No Name] }| | ]]} end) @@ -934,6 +958,15 @@ describe('cmdheight=0', function() before_each(function() clear() screen = Screen.new(25, 5) + screen:set_default_attr_ids { + [1] = {bold = true, foreground = Screen.colors.Blue}; + [2] = {bold = true, reverse = true}; + [3] = {bold = true}; + [4] = {foreground = Screen.colors.White, background = Screen.colors.Red}; + [5] = {foreground = Screen.colors.SeaGreen4, bold = true}; + [6] = {reverse = true}; + [7] = {background = Screen.colors.Yellow}; + } screen:attach() end) @@ -941,9 +974,9 @@ describe('cmdheight=0', function() command("set cmdheight=1 noruler laststatus=2") screen:expect{grid=[[ ^ | - ~ | - ~ | - [No Name] | + {1:~ }| + {1:~ }| + {2:[No Name] }| | ]]} end) @@ -952,10 +985,10 @@ describe('cmdheight=0', function() command("set cmdheight=0 noruler laststatus=2") screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - [No Name] | + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] }| ]]} end) @@ -963,10 +996,10 @@ describe('cmdheight=0', function() command("set cmdheight=0 ruler laststatus=0") screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| ]]} end) @@ -975,10 +1008,10 @@ describe('cmdheight=0', function() feed('i') screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| ]], showmode={}} feed('<Esc>') eq(0, eval('&cmdheight')) @@ -989,10 +1022,10 @@ describe('cmdheight=0', function() feed('i') screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| ]], showmode={}} feed('<Esc>') eq(0, eval('&cmdheight')) @@ -1003,10 +1036,10 @@ describe('cmdheight=0', function() feed('i') screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - -- INSERT -- | + {1:~ }| + {1:~ }| + {1:~ }| + {3:-- INSERT --} | ]]} feed('<Esc>') eq(1, eval('&cmdheight')) @@ -1017,19 +1050,19 @@ describe('cmdheight=0', function() feed(':') screen:expect{grid=[[ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| :^ | ]]} - eq(1, eval('&cmdheight')) + eq(0, eval('&cmdheight')) feed('<cr>') screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| ]], showmode={}} eq(0, eval('&cmdheight')) end) @@ -1039,19 +1072,19 @@ describe('cmdheight=0', function() feed(':call input("foo >")<cr>') screen:expect{grid=[[ | - ~ | - ~ | - ~ | + {1:~ }| + {2: }| + :call input("foo >") | foo >^ | ]]} - eq(1, eval('&cmdheight')) + eq(0, eval('&cmdheight')) feed('<cr>') screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - ~ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| ]], showmode={}} eq(0, eval('&cmdheight')) end) @@ -1060,35 +1093,35 @@ describe('cmdheight=0', function() command("set cmdheight=0 noruler laststatus=3 winbar=foo") feed(':split<CR>') screen:expect{grid=[[ - foo | - | - E36: Not enough room | - Press ENTER or type comma| - nd to continue^ | + {2: }| + :split | + {4:E36: Not enough room} | + {5:Press ENTER or type comma}| + {5:nd to continue}^ | ]]} feed('<CR>') screen:expect{grid=[[ - foo | + {3:foo }| ^ | - ~ | - ~ | - [No Name] | + {1:~ }| + {1:~ }| + {2:[No Name] }| ]]} feed(':') screen:expect{grid=[[ - foo | + {3:foo }| | - ~ | - [No Name] | + {1:~ }| + {1:~ }| :^ | ]]} feed('<Esc>') screen:expect{grid=[[ - foo | + {3:foo }| ^ | - ~ | - ~ | - [No Name] | + {1:~ }| + {1:~ }| + {2:[No Name] }| ]], showmode={}} eq(0, eval('&cmdheight')) @@ -1100,19 +1133,19 @@ describe('cmdheight=0', function() feed('qq') screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - recording @q | - ]], showmode={}} + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} feed('q') screen:expect{grid=[[ ^ | - ~ | - ~ | - ~ | - ~ | - ]], showmode={}} + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]], unchanged=true} end) it("when substitute text", function() @@ -1120,28 +1153,28 @@ describe('cmdheight=0', function() feed('ifoo<ESC>') screen:expect{grid=[[ fo^o | - ~ | - ~ | - ~ | - [No Name] [+] | + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] [+] }| ]]} feed(':%s/foo/bar/gc<CR>') screen:expect{grid=[[ - foo | - ~ | - ~ | - [No Name] [+] | - replace wi...q/l/^E/^Y)?^ | + {6:foo} | + {1:~ }| + {1:~ }| + {1:~ }| + {5:replace wi...q/l/^E/^Y)?}^ | ]]} feed('y') screen:expect{grid=[[ ^bar | - ~ | - ~ | - ~ | - [No Name] [+] | + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] [+] }| ]]} assert_alive() @@ -1152,4 +1185,170 @@ describe('cmdheight=0', function() feed('<C-w>+') eq(0, eval('&cmdheight')) end) + + it("with non-silent mappings with cmdline", function() + command("set cmdheight=0") + command("map <f3> :nohlsearch<cr>") + feed('iaabbaa<esc>/aa<cr>') + screen:expect{grid=[[ + {7:^aa}bb{7:aa} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + feed('<f3>') + screen:expect{grid=[[ + ^aabbaa | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + end) + + it('with silent! at startup', function() + clear{args={'-c', 'set cmdheight=0', '-c', 'autocmd VimEnter * silent! call Foo()'}} + screen:attach() + -- doesn't crash while not displaying silent! error message + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + end) + + it('with multigrid', function() + clear{args={'--cmd', 'set cmdheight=0'}} + screen:attach{ext_multigrid=true} + screen:expect{grid=[[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + }} + + feed '/p' + screen:expect{grid=[[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [3:-------------------------]| + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + /p^ | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + }} + end) + + it('winbar is redrawn on entering cmdline and :redrawstatus #20336', function() + exec([[ + set cmdheight=0 + set winbar=%{mode()}%=:%{getcmdline()} + ]]) + feed(':') + screen:expect([[ + {3:c :}| + | + {1:~ }| + {1:~ }| + :^ | + ]]) + feed('echo') + -- not redrawn yet + screen:expect([[ + {3:c :}| + | + {1:~ }| + {1:~ }| + :echo^ | + ]]) + command('redrawstatus') + screen:expect([[ + {3:c :echo}| + | + {1:~ }| + {1:~ }| + :echo^ | + ]]) + end) + + it('window equalization with laststatus=0 #20367', function() + screen:try_resize(60, 9) + command('set cmdheight=0 laststatus=0') + command('vsplit') + screen:expect([[ + ^ │ | + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + ]]) + feed(':') + command('split') + feed('<Esc>') + screen:expect([[ + ^ │ | + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {2:[No Name] }│{1:~ }| + │{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + ]]) + command('resize 2') + screen:expect([[ + ^ │ | + {1:~ }│{1:~ }| + {2:[No Name] }│{1:~ }| + │{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + ]]) + feed(':') + command('wincmd =') + feed('<Esc>') + screen:expect([[ + ^ │ | + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {2:[No Name] }│{1:~ }| + │{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + ]]) + end) end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 789f1c6487..0a5eefbf38 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -31,6 +31,8 @@ describe('decorations providers', function() [12] = {foreground = tonumber('0x990000')}; [13] = {background = Screen.colors.LightBlue}; [14] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue}; + [15] = {special = Screen.colors.Blue1, undercurl = true}, + [16] = {special = Screen.colors.Red, undercurl = true}, } end) @@ -56,7 +58,7 @@ describe('decorations providers', function() a.nvim_set_decoration_provider(_G.ns1, { on_start = on_do; on_buf = on_do; on_win = on_do; on_line = on_do; - on_end = on_do; + on_end = on_do; _on_spell_nav = on_do; }) return _G.ns1 ]]) @@ -95,7 +97,7 @@ describe('decorations providers', function() | ]]} check_trace { - { "start", 4, 40 }; + { "start", 4 }; { "win", 1000, 1, 0, 8 }; { "line", 1000, 1, 0 }; { "line", 1000, 1, 1 }; @@ -119,7 +121,7 @@ describe('decorations providers', function() | ]]} check_trace { - { "start", 5, 10 }; + { "start", 5 }; { "buf", 1 }; { "win", 1000, 1, 0, 8 }; { "line", 1000, 1, 6 }; @@ -156,6 +158,84 @@ describe('decorations providers', function() ]]} end) + it('can indicate spellchecked points', function() + exec [[ + set spell + set spelloptions=noplainbuffer + syntax off + ]] + + insert [[ + I am well written text. + i am not capitalized. + I am a speling mistakke. + ]] + + setup_provider [[ + local ns = a.nvim_create_namespace "spell" + beamtrace = {} + local function on_do(kind, ...) + if kind == 'win' or kind == 'spell' then + a.nvim_buf_set_extmark(0, ns, 0, 0, { end_row = 2, end_col = 23, spell = true, ephemeral = true }) + end + table.insert(beamtrace, {kind, ...}) + end + ]] + + check_trace { + { "start", 5 }; + { "win", 1000, 1, 0, 5 }; + { "line", 1000, 1, 0 }; + { "line", 1000, 1, 1 }; + { "line", 1000, 1, 2 }; + { "line", 1000, 1, 3 }; + { "end", 5 }; + } + + feed "gg0" + + screen:expect{grid=[[ + ^I am well written text. | + {15:i} am not capitalized. | + I am a {16:speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed "]s" + check_trace { + { "spell", 1000, 1, 1, 0, 1, -1 }; + } + screen:expect{grid=[[ + I am well written text. | + {15:^i} am not capitalized. | + I am a {16:speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed "]s" + check_trace { + { "spell", 1000, 1, 2, 7, 2, -1 }; + } + screen:expect{grid=[[ + I am well written text. | + {15:i} am not capitalized. | + I am a {16:^speling} {16:mistakke}. | + | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + it('can predefine highlights', function() screen:try_resize(40, 16) insert(mulholland) @@ -606,15 +686,15 @@ end]] screen:expect{grid=[[ {5:^for} _,item {5:in} {6:ipairs}(items) {5:do} | - {5:local} text, hl_id_cell, count = unpack(item) | - {5:if} hl_id_cell ~= {13:nil} {5:then} | - hl_id = hl_id_cell | + {5:local} text, hl_id_cell, count {5:=} unpack(item) | + {5:if} hl_id_cell {5:~=} {13:nil} {5:then} | + hl_id {5:=} hl_id_cell | {5:end} | - {5:for} _ = {13:1}, (count {5:or} {13:1}) {5:do} | - {5:local} cell = line[colpos] | - cell.text = text | - cell.hl_id = hl_id | - colpos = colpos+{13:1} | + {5:for} _ {5:=} {13:1}, (count {5:or} {13:1}) {5:do} | + {5:local} cell {5:=} line[colpos] | + cell.text {5:=} text | + cell.hl_id {5:=} hl_id | + colpos {5:=} colpos{5:+}{13:1} | {5:end} | {5:end} | {1:~ }| @@ -633,15 +713,15 @@ end]] screen:expect{grid=[[ {5:^for} _,item {5:in} {6:ipairs}(items) {5:do} | - {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} | + {5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count {5:=} unpack(item) | + {5:i}{12:c}{11:ombining col}{12:or} {13:nil} {5:then} | {11:replacing color}d_cell | {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} | + {5:f}{12:co}{11:mbi}{12:n}{11:i}{16:n}{11:g color}t {5:or} {13:1}) {5:do} | {11:replacing color} line[colpos] | - cell.text = text | - cell.hl_id = hl_id | - colpos = colpos+{13:1} | + cell.text {5:=} text | + cell.hl_id {5:=} hl_id | + colpos {5:=} colpos{5:+}{13:1} | {5:end} | {5:end} | {1:~ }| @@ -652,15 +732,15 @@ end]] feed 'V5G' screen:expect{grid=[[ {17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} | - {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} | - {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} | + {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count }{17:=}{18: unpack(item)} | + {18: }{17:i}{12:c}{11:ombining col}{12:or}{18: }{23:nil}{18: }{17:then} | {18: }{11:replacing color}{18:d_cell} | {18: }{5:^e}{17:nd} | - {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} | + {5:f}{12:co}{11:mbi}{12:n}{11:i}{16:n}{11:g color}t {5:or} {13:1}) {5:do} | {11:replacing color} line[colpos] | - cell.text = text | - cell.hl_id = hl_id | - colpos = colpos+{13:1} | + cell.text {5:=} text | + cell.hl_id {5:=} hl_id | + colpos {5:=} colpos{5:+}{13:1} | {5:end} | {5:end} | {1:~ }| @@ -671,15 +751,15 @@ end]] feed 'jj' screen:expect{grid=[[ {17:for}{18: _,item }{17:in}{18: }{19:ipairs}{18:(items) }{17:do} | - {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count = unpack(item)} | - {18: }{17:i}{12:c}{11:ombining color}{18: }{23:nil}{18: }{17:then} | + {18: }{17:l}{20:blen}{21:dy}{22:e}{21:text}{22:h}{21:-}{22:_}{21:here}{18:ell, count }{17:=}{18: unpack(item)} | + {18: }{17:i}{12:c}{11:ombining col}{12:or}{18: }{23:nil}{18: }{17:then} | {18: }{11:replacing color}{18:d_cell} | {18: }{17:end} | - {18: }{17:for}{18: _ = }{23:1}{18:, (count }{17:or}{18: }{23:1}{18:) }{17:do} | - {18: }^ {18: }{17:local}{18: cell = line[colpos]} | - cell.text = text | - cell.hl_id = hl_id | - colpos = colpos+{13:1} | + {18: }{17:for}{18: _ }{17:=}{18: }{23:1}{18:, (count }{17:or}{18: }{23:1}{18:) }{17:do} | + {18: }^ {18: }{17:local}{18: cell }{17:=}{18: line[colpos]} | + cell.text {5:=} text | + cell.hl_id {5:=} hl_id | + colpos {5:=} colpos{5:+}{13:1} | {5:end} | {5:end} | {1:~ }| @@ -1201,6 +1281,110 @@ if (h->n_buckets < new_n_buckets) { // expand ]]} end) + it('works beyond end of the buffer with virt_lines_above', 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, 8, 0, { + virt_lines={{{"Grugg"}}}; + virt_lines_above = true, + }) + + 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 | + | + ]]} + + feed('dd') + 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 | + {1:~ }| + | + ]]} + + feed('dk') + 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); | + Grugg | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed('dgg') + screen:expect{grid=[[ + ^ | + Grugg | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + --No lines in buffer-- | + ]]} + + meths.buf_del_extmark(0, ns, id) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + --No lines in buffer-- | + ]]} + end) + it('does not cause syntax ml_get error at the end of a buffer #17816', function() command([[syntax region foo keepend start='^foo' end='^$']]) command('syntax sync minlines=100') @@ -1470,6 +1654,38 @@ if (h->n_buckets < new_n_buckets) { // expand } | | ]]} + + command 'set number' + screen:expect{grid=[[ + {9: 1 }^if (h->n_buckets < new_n_buckets) { // expand | + {9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi| + {9: }d *)h->keys, new_n_buckets * sizeof(khkey_t));| + {9: }{1:>>}{2: very tabby}text with tabs | + {9: 3 } h->keys = new_keys; | + {9: 4 } if (kh_is_map && val_size) { | + {9: 5 } char *new_vals = krealloc( h->vals_buf, ne| + {9: }w_n_buckets * val_size); | + {9: 6 } h->vals_buf = new_vals; | + {9: 7 } } | + {9: 8 }} | + | + ]]} + + command 'set tabstop&' + screen:expect{grid=[[ + {9: 1 }^if (h->n_buckets < new_n_buckets) { // expand | + {9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi| + {9: }d *)h->keys, new_n_buckets * sizeof(khkey_t));| + {9: }{1:>>}{2: very tabby}text with tabs | + {9: 3 } h->keys = new_keys; | + {9: 4 } if (kh_is_map && val_size) { | + {9: 5 } char *new_vals = krealloc( h->vals_buf, ne| + {9: }w_n_buckets * val_size); | + {9: 6 } h->vals_buf = new_vals; | + {9: 7 } } | + {9: 8 }} | + | + ]]} end) end) diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua index 92f5beebf5..e7addd1b85 100644 --- a/test/functional/ui/embed_spec.lua +++ b/test/functional/ui/embed_spec.lua @@ -82,20 +82,6 @@ local function test_embed(ext_linegrid) eq(Screen.colors.Green, screen.default_colors.rgb_bg) end} end) - - it("set display-=msgsep before first redraw", function() - startup('--cmd', 'set display-=msgsep') - screen:expect{grid=[[ - ^ | - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - | - ]]} - end) end describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 5967b630f6..1a9a13f7d4 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -569,7 +569,7 @@ describe('float window', function() end) end) - describe('with mulitple tabpages but only one listed buffer,', function() + describe('with multiple tabpages but only one listed buffer,', function() local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1} local unlisted_buf, old_buf, old_win before_each(function() @@ -596,6 +596,11 @@ describe('float window', function() meths.buf_delete(old_buf, {force = true}) eq(old_win, curwin().id) end) + it('if called from non-floating window in another tabpage', function() + command('tab split') + eq(3, #meths.list_tabpages()) + meths.buf_delete(old_buf, {force = true}) + end) it('if called from floating window with the same buffer', function() meths.set_current_win(same_buf_float) command('autocmd WinLeave * let g:win_leave = nvim_get_current_win()') @@ -2756,7 +2761,7 @@ describe('float window', function() }, "NW", 2, 1, 32, true } }} else - -- note: appears misalinged due to cursor + -- note: appears misaligned due to cursor screen:expect{grid=[[ ^example text that is wider than the window | {1:some info! } | @@ -8147,6 +8152,366 @@ describe('float window', function() ]]} end end) + + it('it can be resized with messages and cmdheight=0 #20106', function() + screen:try_resize(40,9) + command 'set cmdheight=0' + local buf = meths.create_buf(false,true) + local win = meths.open_win(buf, false, {relative='editor', width=40, height=4, anchor='SW', row=9, col=0, style='minimal', border="single", noautocmd=true}) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + ## grid 5 + {5:┌────────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└────────────────────────────────────────┘}| + ]], float_pos={ + [5] = {{id = 1002}, "SW", 1, 9, 0, true, 50}; + }, win_viewport={ + [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 = 1}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {5:┌──────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└──────────────────────────────────────┘}| + ]]} + end + + exec_lua([[ + local win = ... + vim.api.nvim_win_set_height(win, 2) + vim.api.nvim_echo({ { "" } }, false, {}) + ]], win) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + ## grid 5 + {5:┌────────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└────────────────────────────────────────┘}| + ]], float_pos={ + [5] = {{id = 1002}, "SW", 1, 9, 0, true, 50}; + }, win_viewport={ + [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 = 1}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {5:┌──────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└──────────────────────────────────────┘}| + ]]} + + end + + meths.win_close(win, true) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]]} + end + end) + + it('it can be resized with messages and cmdheight=1', function() + screen:try_resize(40,9) + local buf = meths.create_buf(false,true) + local win = meths.open_win(buf, false, {relative='editor', width=40, height=4, anchor='SW', row=8, col=0, style='minimal', border="single", noautocmd=true}) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {5:┌────────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└────────────────────────────────────────┘}| + ]], float_pos={ + [5] = {{id = 1002}, "SW", 1, 8, 0, true, 50}; + }, win_viewport={ + [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 = 1}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {5:┌──────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└──────────────────────────────────────┘}| + | + ]]} + end + + exec_lua([[ + -- echo prompt is blocking, so schedule + local win = ... + vim.schedule(function() + vim.api.nvim_win_set_height(win, 2) + vim.api.nvim_echo({ { "\n" } }, false, {}) + end) + ]], win) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + {8:Press ENTER or type command to continue}^ | + ## grid 5 + {5:┌────────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└────────────────────────────────────────┘}| + ]], float_pos={ + [5] = {{id = 1002}, "SW", 1, 8, 0, true, 50}; + }, win_viewport={ + [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 = 1}; + }} + else + screen:expect{grid=[[ + | + {0:~ }| + {5:┌──────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {4: }| + | + {8:Press ENTER or type command to continue}^ | + ]]} + end + + feed('<cr>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {5:┌────────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└────────────────────────────────────────┘}| + ]], float_pos={ + [5] = {{id = 1002}, "SW", 1, 8, 0, true, 50}; + }, win_viewport={ + [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 = 1}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {5:┌──────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{1: }{5:│}| + {5:└──────────────────────────────────────┘}| + | + ]]} + end + + meths.win_close(win, true) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + }} + + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end + end) end describe('with ext_multigrid', function() diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 6bb8bb81c6..3c143d87ca 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -4,6 +4,7 @@ local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq local command = helpers.command local feed_command = helpers.feed_command local insert = helpers.insert +local expect = helpers.expect local funcs = helpers.funcs local meths = helpers.meths local source = helpers.source @@ -1911,4 +1912,19 @@ describe("folded lines", function() command('%delete') eq(0, funcs.foldlevel(1)) end) + + it('multibyte fold markers work #20438', function() + meths.win_set_option(0, 'foldmethod', 'marker') + meths.win_set_option(0, 'foldmarker', '«,»') + insert([[ + bbbbb + bbbbb + bbbbb]]) + feed('zfgg') + expect([[ + bbbbb/*«*/ + bbbbb + bbbbb/*»*/]]) + eq(1, funcs.foldlevel(1)) + end) end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 4e3d62509c..504a38d591 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -1560,17 +1560,6 @@ describe("MsgSeparator highlight and msgsep fillchar", function() 1 %a "[No Name]" line 1 | {3:Press ENTER or type command to continue}^ | ]]) - - -- when display doesn't contain msgsep, these options have no effect - feed_command("set display-=msgsep") - feed_command("ls") - screen:expect([[ - {1:~ }| - {1:~ }| - :ls | - 1 %a "[No Name]" line 1 | - {3:Press ENTER or type command to continue}^ | - ]]) end) it("and MsgArea", function() @@ -2333,6 +2322,51 @@ describe("'winhighlight' highlight", function() helpers.assert_alive() end) + + it('can redraw statusline on cursor movement', function() + screen:try_resize(40, 8) + exec [[ + set statusline=%f%=%#Background1#%l,%c%V\ %P + split + ]] + insert [[ + some text + more text]] + screen:expect{grid=[[ + some text | + more tex^t | + {0:~ }| + {3:[No Name] }{1:2,9 All}| + some text | + more text | + {4:[No Name] }{1:1,1 All}| + | + ]]} + + command 'set winhl=Background1:Background2' + screen:expect{grid=[[ + some text | + more tex^t | + {0:~ }| + {3:[No Name] }{5:2,9 All}| + some text | + more text | + {4:[No Name] }{1:1,1 All}| + | + ]]} + + feed 'k' + screen:expect{grid=[[ + some tex^t | + more text | + {0:~ }| + {3:[No Name] }{5:1,9 All}| + some text | + more text | + {4:[No Name] }{1:1,1 All}| + | + ]]} + end) end) describe('highlight namespaces', function() diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 9ca4673efe..6fbf9b72c8 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -1190,6 +1190,8 @@ describe(":substitute, inccommand=split", function() end) it("deactivates if 'redrawtime' is exceeded #5602", function() + -- prevent redraws from 'incsearch' + meths.set_option('incsearch', false) -- Assert that 'inccommand' is ENABLED initially. eq("split", eval("&inccommand")) -- Set 'redrawtime' to minimal value, to ensure timeout is triggered. @@ -2972,6 +2974,59 @@ it(':substitute with inccommand, does not crash if range contains invalid marks' ]]) end) +it(':substitute with inccommand, no unnecessary redraw if preview is not shown', function() + local screen = Screen.new(60, 6) + clear() + common_setup(screen, 'split', 'test') + feed(':ls<CR>') + screen:expect([[ + test | + {15:~ }| + {11: }| + :ls | + 1 %a + "[No Name]" line 1 | + {13:Press ENTER or type command to continue}^ | + ]]) + feed(':s') + -- no unnecessary redraw, so messages are still shown + screen:expect([[ + test | + {15:~ }| + {11: }| + :ls | + 1 %a + "[No Name]" line 1 | + :s^ | + ]]) + feed('o') + screen:expect([[ + test | + {15:~ }| + {11: }| + :ls | + 1 %a + "[No Name]" line 1 | + :so^ | + ]]) + feed('<BS>') + screen:expect([[ + test | + {15:~ }| + {11: }| + :ls | + 1 %a + "[No Name]" line 1 | + :s^ | + ]]) + feed('/test') + -- now inccommand is shown, so screen is redrawn + screen:expect([[ + {12:test} | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :s/test^ | + ]]) +end) + it(":substitute doesn't crash with inccommand, if undo is empty #12932", function() local screen = Screen.new(10,5) clear() @@ -2992,6 +3047,43 @@ it(":substitute doesn't crash with inccommand, if undo is empty #12932", functio assert_alive() end) +it(':substitute with inccommand works properly if undo is not synced #20029', function() + local screen = Screen.new(30, 6) + clear() + common_setup(screen, 'nosplit', 'foo\nbar\nbaz') + meths.set_keymap('x', '<F2>', '<Esc>`<Oaaaaa asdf<Esc>`>obbbbb asdf<Esc>V`<k:s/asdf/', {}) + feed('gg0<C-V>lljj<F2>') + screen:expect([[ + aaaaa | + foo | + bar | + baz | + bbbbb | + :'<,'>s/asdf/^ | + ]]) + feed('hjkl') + screen:expect([[ + aaaaa {12:hjkl} | + foo | + bar | + baz | + bbbbb {12:hjkl} | + :'<,'>s/asdf/hjkl^ | + ]]) + feed('<CR>') + expect([[ + aaaaa hjkl + foo + bar + baz + bbbbb hjkl]]) + feed('u') + expect([[ + foo + bar + baz]]) +end) + it('long :%s/ with inccommand does not collapse cmdline', function() local screen = Screen.new(10,5) clear() diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua index 0b25d4f8d2..43e9b94feb 100644 --- a/test/functional/ui/inccommand_user_spec.lua +++ b/test/functional/ui/inccommand_user_spec.lua @@ -220,7 +220,7 @@ local setup_replace_cmd = [[ end -- ":<range>Replace <pat1> <pat2>" - -- Replaces all occurences of <pat1> in <range> with <pat2> + -- Replaces all occurrences of <pat1> in <range> with <pat2> vim.api.nvim_create_user_command( 'Replace', replace, diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 2cff7c1cf4..0898d7fe75 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -869,7 +869,7 @@ stack traceback: {1:~ }| {1:~ }| ]], messages={ - { content = { { "wow, ", 7 }, { "such\n\nvery ", 2 }, { "color", 10 } }, kind = "" } + { content = { { "wow, ", 7 }, { "such\n\nvery ", 2 }, { "color", 10 } }, kind = "echomsg" } }} feed ':ls<cr>' @@ -880,7 +880,7 @@ stack traceback: {1:~ }| {1:~ }| ]], messages={ - { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = "echomsg" } + { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = "" } }} feed ':messages<cr>' @@ -1077,10 +1077,10 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim ]]} end) - it('redraws NOT_VALID correctly after message', function() - -- edge case: only one window was set NOT_VALID. Original report + it('redraws UPD_NOT_VALID correctly after message', function() + -- edge case: only one window was set UPD_NOT_VALID. Original report -- used :make, but fake it using one command to set the current - -- window NOT_VALID and another to show a long message. + -- window UPD_NOT_VALID and another to show a long message. command("set more") feed(':new<cr><c-w><c-w>') screen:expect{grid=[[ @@ -1200,28 +1200,6 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim it('prints lines in Ex mode correctly with a burst of carriage returns #19341', function() command('set number') meths.buf_set_lines(0, 0, 0, true, {'aaa', 'bbb', 'ccc'}) - command('set display-=msgsep') - feed('gggQ<CR><CR>1<CR><CR>vi') - screen:expect([[ - Entering Ex mode. Type "visual" to go to Normal mode. | - {11: 2 }bbb | - {11: 3 }ccc | - :1 | - {11: 1 }aaa | - {11: 2 }bbb | - :vi^ | - ]]) - feed('<CR>') - screen:expect([[ - {11: 1 }aaa | - {11: 2 }^bbb | - {11: 3 }ccc | - {11: 4 } | - {1:~ }| - {1:~ }| - | - ]]) - command('set display+=msgsep') feed('gggQ<CR><CR>1<CR><CR>vi') screen:expect([[ Entering Ex mode. Type "visual" to go to Normal mode. | @@ -2020,4 +1998,55 @@ aliquip ex ea commodo consequat.]]) | ]]} end) + + it('with cmdheight=0 does not crash with g<', function() + command('set cmdheight=0') + feed(':ls<cr>') + screen:expect{grid=[[ + | + {1:~ }| + {12: }| + :ls | + 1 %a "[No Name]" | + line 1 | + {4:Press ENTER or type command to cont}| + {4:inue}^ | + ]]} + + feed('<cr>') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + feed('g<lt>') + screen:expect{grid=[[ + | + {1:~ }| + {12: }| + :ls | + 1 %a "[No Name]" | + line 1 | + {4:Press ENTER or type command to cont}| + {4:inue}^ | + ]]} + + feed('<cr>') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + end) end) diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 9896b11218..cb8dfdb8e1 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -648,8 +648,10 @@ describe('ui/mouse/input', function() ]]} end) - it('two clicks will select the word and enter VISUAL', function() - feed('<LeftMouse><2,2><LeftMouse><2,2>') + it('two clicks will enter VISUAL and dragging selects words', function() + feed('<LeftMouse><2,2>') + feed('<LeftRelease><2,2>') + feed('<LeftMouse><2,2>') screen:expect([[ testing | mouse | @@ -657,10 +659,38 @@ describe('ui/mouse/input', function() {0:~ }| {2:-- VISUAL --} | ]]) + feed('<LeftDrag><0,1>') + screen:expect([[ + testing | + ^m{1:ouse} | + {1:support} and selection | + {0:~ }| + {2:-- VISUAL --} | + ]]) + feed('<LeftDrag><4,0>') + screen:expect([[ + ^t{1:esting} | + {1:mouse} | + {1:support} and selection | + {0:~ }| + {2:-- VISUAL --} | + ]]) + feed('<LeftDrag><14,2>') + screen:expect([[ + testing | + mouse | + {1:support and selectio}^n | + {0:~ }| + {2:-- VISUAL --} | + ]]) end) - it('three clicks will select the line and enter VISUAL LINE', function() - feed('<LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2>') + it('three clicks will enter VISUAL LINE and dragging selects lines', function() + feed('<LeftMouse><2,2>') + feed('<LeftRelease><2,2>') + feed('<LeftMouse><2,2>') + feed('<LeftRelease><2,2>') + feed('<LeftMouse><2,2>') screen:expect([[ testing | mouse | @@ -668,10 +698,40 @@ describe('ui/mouse/input', function() {0:~ }| {2:-- VISUAL LINE --} | ]]) + feed('<LeftDrag><0,1>') + screen:expect([[ + testing | + ^m{1:ouse} | + {1:support and selection} | + {0:~ }| + {2:-- VISUAL LINE --} | + ]]) + feed('<LeftDrag><4,0>') + screen:expect([[ + {1:test}^i{1:ng} | + {1:mouse} | + {1:support and selection} | + {0:~ }| + {2:-- VISUAL LINE --} | + ]]) + feed('<LeftDrag><14,2>') + screen:expect([[ + testing | + mouse | + {1:support and se}^l{1:ection} | + {0:~ }| + {2:-- VISUAL LINE --} | + ]]) end) - it('four clicks will enter VISUAL BLOCK', function() - feed('<LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2>') + it('four clicks will enter VISUAL BLOCK and dragging selects blockwise', function() + feed('<LeftMouse><2,2>') + feed('<LeftRelease><2,2>') + feed('<LeftMouse><2,2>') + feed('<LeftRelease><2,2>') + feed('<LeftMouse><2,2>') + feed('<LeftRelease><2,2>') + feed('<LeftMouse><2,2>') screen:expect([[ testing | mouse | @@ -679,6 +739,30 @@ describe('ui/mouse/input', function() {0:~ }| {2:-- VISUAL BLOCK --} | ]]) + feed('<LeftDrag><0,1>') + screen:expect([[ + testing | + ^m{1:ou}se | + {1:sup}port and selection | + {0:~ }| + {2:-- VISUAL BLOCK --} | + ]]) + feed('<LeftDrag><4,0>') + screen:expect([[ + te{1:st}^ing | + mo{1:use} | + su{1:ppo}rt and selection | + {0:~ }| + {2:-- VISUAL BLOCK --} | + ]]) + feed('<LeftDrag><14,2>') + screen:expect([[ + testing | + mouse | + su{1:pport and se}^lection | + {0:~ }| + {2:-- VISUAL BLOCK --} | + ]]) end) it('right click extends visual selection to the clicked location', function() @@ -1585,7 +1669,20 @@ describe('ui/mouse/input', function() eq(0, meths.get_var('mouse_up2')) end) - it('feeding <MouseMove> does not use uninitialized memory #19480', function() + it('<MouseMove> is not translated into multiclicks and can be mapped', function() + meths.set_var('mouse_move', 0) + meths.set_var('mouse_move2', 0) + command('nnoremap <MouseMove> <Cmd>let g:mouse_move += 1<CR>') + command('nnoremap <2-MouseMove> <Cmd>let g:mouse_move2 += 1<CR>') + feed('<MouseMove><0,0>') + feed('<MouseMove><0,0>') + meths.input_mouse('move', '', '', 0, 0, 0) + meths.input_mouse('move', '', '', 0, 0, 0) + eq(4, meths.get_var('mouse_move')) + eq(0, meths.get_var('mouse_move2')) + end) + + it('feeding <MouseMove> in Normal mode does not use uninitialized memory #19480', function() feed('<MouseMove>') helpers.poke_eventloop() helpers.assert_alive() diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index b30aa67fd3..78a1e8c677 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -3,7 +3,9 @@ local Screen = require('test.functional.ui.screen') local clear = helpers.clear local feed, command, insert = helpers.feed, helpers.command, helpers.insert local eq = helpers.eq +local funcs = helpers.funcs local meths = helpers.meths +local curwin = helpers.curwin local poke_eventloop = helpers.poke_eventloop @@ -871,6 +873,15 @@ describe('ext_multigrid', function() before_each(function() screen:try_resize_grid(2, 60, 20) end) + + it('winwidth() winheight() getwininfo() return inner width and height #19743', function() + eq(60, funcs.winwidth(0)) + eq(20, funcs.winheight(0)) + local win_info = funcs.getwininfo(curwin().id)[1] + eq(60, win_info.width) + eq(20, win_info.height) + end) + it('gets written till grid width', function() insert(('a'):rep(60).."\n") diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index 8d7c404637..6f9cea8f24 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -19,6 +19,7 @@ describe('UI receives option updates', function() linespace=0, pumblend=0, mousefocus=false, + mousemoveevent=false, showtabline=1, termguicolors=false, ttimeout=true, @@ -131,6 +132,12 @@ describe('UI receives option updates', function() eq(expected, screen.options) end) + command("set mousemoveevent") + expected.mousemoveevent = true + screen:expect(function() + eq(expected, screen.options) + end) + command("set nottimeout") expected.ttimeout = false screen:expect(function() @@ -187,7 +194,7 @@ end) describe('UI can set terminal option', function() local screen before_each(function() - -- by default we implicity "--cmd 'set bg=light'" which ruins everything + -- by default we implicitly "--cmd 'set bg=light'" which ruins everything clear{args_rm={'--cmd'}} screen = Screen.new(20,5) end) diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index e11cd1e859..3c752875f0 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1990,6 +1990,22 @@ describe('builtin popupmenu', function() efine unplace^ | ]]) + -- Pressing <Left> after that should move the cursor + feed('<Left>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4: }| + :sign define jump list place und| + efine unplac^e | + ]]) + feed('<End>') + -- Pressing <C-D> when the popup menu is displayed should remove the popup -- menu feed('<C-U>sign <Tab><C-D>') @@ -2739,7 +2755,7 @@ describe('builtin popupmenu', function() menu PopUp.bar :let g:menustr = 'bar'<CR> menu PopUp.baz :let g:menustr = 'baz'<CR> ]]) - meths.input_mouse('right', 'press', '', 0, 0, 4) + feed('<RightMouse><4,0>') screen:expect([[ ^popup menu test | {1:~ }{n: foo }{1: }| @@ -2776,7 +2792,7 @@ describe('builtin popupmenu', function() :let g:menustr = 'bar' | ]]) eq('bar', meths.get_var('menustr')) - meths.input_mouse('right', 'press', '', 0, 1, 20) + feed('<RightMouse><20,1>') screen:expect([[ ^popup menu test | {1:~ }| @@ -2785,7 +2801,7 @@ describe('builtin popupmenu', function() {1:~ }{n: baz }{1: }| :let g:menustr = 'bar' | ]]) - meths.input_mouse('left', 'press', '', 0, 4, 22) + feed('<LeftMouse><22,4>') screen:expect([[ ^popup menu test | {1:~ }| @@ -2795,7 +2811,7 @@ describe('builtin popupmenu', function() :let g:menustr = 'baz' | ]]) eq('baz', meths.get_var('menustr')) - meths.input_mouse('right', 'press', '', 0, 0, 4) + feed('<RightMouse><4,0>') screen:expect([[ ^popup menu test | {1:~ }{n: foo }{1: }| @@ -2804,7 +2820,7 @@ describe('builtin popupmenu', function() {1:~ }| :let g:menustr = 'baz' | ]]) - meths.input_mouse('right', 'drag', '', 0, 3, 6) + feed('<RightDrag><6,3>') screen:expect([[ ^popup menu test | {1:~ }{n: foo }{1: }| @@ -2813,7 +2829,7 @@ describe('builtin popupmenu', function() {1:~ }| :let g:menustr = 'baz' | ]]) - meths.input_mouse('right', 'release', '', 0, 1, 6) + feed('<RightRelease><6,1>') screen:expect([[ ^popup menu test | {1:~ }| @@ -2823,6 +2839,38 @@ describe('builtin popupmenu', function() :let g:menustr = 'foo' | ]]) eq('foo', meths.get_var('menustr')) + eq(false, screen.options.mousemoveevent) + feed('<RightMouse><4,0>') + screen:expect([[ + ^popup menu test | + {1:~ }{n: foo }{1: }| + {1:~ }{n: bar }{1: }| + {1:~ }{n: baz }{1: }| + {1:~ }| + :let g:menustr = 'foo' | + ]]) + eq(true, screen.options.mousemoveevent) + feed('<MouseMove><6,3>') + screen:expect([[ + ^popup menu test | + {1:~ }{n: foo }{1: }| + {1:~ }{n: bar }{1: }| + {1:~ }{s: baz }{1: }| + {1:~ }| + :let g:menustr = 'foo' | + ]]) + eq(true, screen.options.mousemoveevent) + feed('<LeftMouse><6,2>') + screen:expect([[ + ^popup menu test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + :let g:menustr = 'bar' | + ]]) + eq(false, screen.options.mousemoveevent) + eq('bar', meths.get_var('menustr')) end) end) @@ -3031,5 +3079,72 @@ describe('builtin popupmenu with ui/ext_multigrid', function() :let g:menustr = 'foo' | ]]}) eq('foo', meths.get_var('menustr')) + eq(false, screen.options.mousemoveevent) + meths.input_mouse('right', 'press', '', 2, 0, 4) + screen:expect({grid=[[ + ## grid 1 + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [3:--------------------------------]| + ## grid 2 + ^popup menu test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + :let g:menustr = 'foo' | + ## grid 4 + {n: foo }| + {n: bar }| + {n: baz }| + ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}}) + eq(true, screen.options.mousemoveevent) + meths.input_mouse('move', '', '', 2, 3, 6) + screen:expect({grid=[[ + ## grid 1 + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [3:--------------------------------]| + ## grid 2 + ^popup menu test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + :let g:menustr = 'foo' | + ## grid 4 + {n: foo }| + {n: bar }| + {s: baz }| + ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 3, false, 100}}}) + eq(true, screen.options.mousemoveevent) + meths.input_mouse('left', 'press', '', 2, 2, 6) + screen:expect({grid=[[ + ## grid 1 + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [2:--------------------------------]| + [3:--------------------------------]| + ## grid 2 + ^popup menu test | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + :let g:menustr = 'bar' | + ]]}) + eq(false, screen.options.mousemoveevent) + eq('bar', meths.get_var('menustr')) end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 6ee9e7b393..c44e147c4d 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -519,7 +519,7 @@ function Screen:_wait(check, flags) end assert(timeout >= minimal_timeout) - local did_miminal_timeout = false + local did_minimal_timeout = false local function notification_cb(method, args) assert(method == 'redraw', string.format( @@ -536,7 +536,7 @@ function Screen:_wait(check, flags) if not err then success_seen = true - if did_miminal_timeout then + if did_minimal_timeout then self._session:stop() end elseif success_seen and #args > 0 then @@ -558,7 +558,7 @@ function Screen:_wait(check, flags) end if not success_seen and not eof then - did_miminal_timeout = true + did_minimal_timeout = true eof = run_session(self._session, flags.request_cb, notification_cb, nil, timeout-minimal_timeout) end @@ -769,6 +769,7 @@ end function Screen:_handle_grid_cursor_goto(grid, row, col) self._cursor.grid = grid + assert(row >= 0 and col >= 0) self._cursor.row = row + 1 self._cursor.col = col + 1 end diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 6c872e52d3..58c8238c35 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -682,30 +682,7 @@ local function screen_tests(linegrid) ]]) end) - it('execute command with multi-line output without msgsep', function() - command("set display-=msgsep") - feed(':ls<cr>') - screen:expect([[ - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - :ls | - 1 %a "[No Name]" line 1 | - {7:Press ENTER or type command to continue}^ | - ]]) - feed('<cr>') -- skip the "Press ENTER..." state or tests will hang - end) - - it('execute command with multi-line output and with msgsep', function() - command("set display+=msgsep") + it('execute command with multi-line output', function() feed(':ls<cr>') screen:expect([[ | @@ -1050,39 +1027,3 @@ describe('Screen default colors', function() end} end) end) - - -describe('screen with msgsep deactivated on startup', function() - local screen - - before_each(function() - clear('--cmd', 'set display-=msgsep') - screen = Screen.new() - screen:attach() - screen:set_default_attr_ids { - [0] = {bold=true, foreground=255}; - [7] = {bold = true, foreground = Screen.colors.SeaGreen}; - } - end) - - it('execute command with multi-line output', function() - feed ':ls<cr>' - screen:expect([[ - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - :ls | - 1 %a "[No Name]" line 1 | - {7:Press ENTER or type command to continue}^ | - ]]) - feed '<cr>' -- skip the "Press ENTER..." state or tests will hang - end) -end) diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index c5c88323a2..18bbb56a61 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -221,10 +221,10 @@ describe('search highlighting', function() feed('gg/foo\\nbar<CR>') screen:expect([[ one | - {2:^foo} | + {2:^foo } | {2:bar} | baz | - {1:foo} | + {1:foo } | {1:bar} | /foo\nbar | ]]) @@ -232,20 +232,20 @@ describe('search highlighting', function() feed('gg/efg\\nhij<CR>') screen:expect([[ --- | - abcd{2:^efg} | + abcd{2:^efg } | {2:hij}kl | --- | - abcd{1:efg} | + abcd{1:efg } | {1:hij}kl | /efg\nhij | ]]) feed('n') screen:expect([[ --- | - abcd{1:efg} | + abcd{1:efg } | {1:hij}kl | --- | - abcd{2:^efg} | + abcd{2:^efg } | {2:hij}kl | /efg\nhij | ]]) @@ -548,9 +548,9 @@ describe('search highlighting', function() feed('/line\\na<cr>') screen:expect([[ | - a repeated {2:^line} | - {2:a} repeated {2:line} | - {2:a} repeated {2:line} | + a repeated {2:^line } | + {2:a} repeated {2:line } | + {2:a} repeated {2:line } | {2:a} repeated line | {1:~ }| {4:search hit BOTTOM, continuing at TOP} | @@ -560,9 +560,9 @@ describe('search highlighting', function() feed('4Grb') screen:expect([[ | - a repeated {2:line} | + a repeated {2:line } | {2:a} repeated line | - ^b repeated {2:line} | + ^b repeated {2:line } | {2:a} repeated line | {1:~ }| {4:search hit BOTTOM, continuing at TOP} | diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index dbc92ca222..ff3e143126 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, command = helpers.clear, helpers.feed, helpers.command local source = helpers.source +local meths = helpers.meths describe('Signs', function() local screen @@ -592,4 +593,88 @@ describe('Signs', function() ]]) end) end) + + it('signcolumn width is updated when removing all signs after deleting lines', function() + meths.buf_set_lines(0, 0, 1, true, {'a', 'b', 'c', 'd', 'e'}) + command('sign define piet text=>>') + command('sign place 10001 line=1 name=piet') + command('sign place 10002 line=5 name=piet') + command('2delete') + command('sign unplace 10001') + screen:expect([[ + {2: }a | + {2: }^c | + {2: }d | + >>e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + command('sign unplace 10002') + screen:expect([[ + a | + ^c | + d | + e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + + it('signcolumn width is updated when removing all signs after inserting lines', function() + meths.buf_set_lines(0, 0, 1, true, {'a', 'b', 'c', 'd', 'e'}) + command('sign define piet text=>>') + command('sign place 10001 line=1 name=piet') + command('sign place 10002 line=5 name=piet') + command('copy .') + command('sign unplace 10001') + screen:expect([[ + {2: }a | + {2: }^a | + {2: }b | + {2: }c | + {2: }d | + >>e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + command('sign unplace 10002') + screen:expect([[ + a | + ^a | + b | + c | + d | + e | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) end) diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua index de77100cc0..1aa73e7b13 100644 --- a/test/functional/ui/spell_spec.lua +++ b/test/functional/ui/spell_spec.lua @@ -19,6 +19,10 @@ describe("'spell'", function() [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {special = Screen.colors.Red, undercurl = true}, [2] = {special = Screen.colors.Blue1, undercurl = true}, + [3] = {foreground = tonumber('0x6a0dad')}, + [4] = {foreground = Screen.colors.Magenta}, + [5] = {bold = true, foreground = Screen.colors.SeaGreen}, + [6] = {foreground = Screen.colors.Red}, }) end) @@ -68,4 +72,94 @@ describe("'spell'", function() | ]]) end) + + it('"noplainbuffer" and syntax #20385', function() + command('set filetype=c') + command('syntax on') + command('set spell') + insert([[ + #include <stdbool.h> + bool func(void);]]) + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void})^; | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + feed('[s') + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void})^; | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {6:search hit TOP, continuing at BOTTOM} | + ]]) + -- "noplainbuffer" shouldn't change spellchecking behavior with syntax enabled + command('set spelloptions+=noplainbuffer') + screen:expect_unchanged() + feed(']s') + screen:expect([[ + {3:#include }{4:<stdbool.h>} | + {5:bool} func({5:void})^; | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {6:search hit BOTTOM, continuing at TOP} | + ]]) + -- no spellchecking with "noplainbuffer" and syntax disabled + command('syntax off') + screen:expect([[ + #include <stdbool.h> | + bool func(void)^; | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {6:search hit BOTTOM, continuing at TOP} | + ]]) + feed('[s') + screen:expect([[ + #include <stdbool.h> | + bool func(void)^; | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {6:search hit TOP, continuing at BOTTOM} | + ]]) + -- everything is spellchecked without "noplainbuffer" with syntax disabled + command('set spelloptions&') + screen:expect([[ + #include <{1:stdbool}.h> | + {1:bool} {1:func}(void)^; | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {6:search hit TOP, continuing at BOTTOM} | + ]]) + feed(']s') + screen:expect([[ + #include <{1:^stdbool}.h> | + {1:bool} {1:func}(void); | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {6:search hit BOTTOM, continuing at TOP} | + ]]) + end) end) diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index add5144e1b..18391a575d 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -178,6 +178,7 @@ describe('global statusline', function() [2] = {bold = true, reverse = true}; [3] = {bold = true}; [4] = {reverse = true}; + [5] = {bold = true, foreground = Screen.colors.Fuchsia}; }) command('set laststatus=3') command('set ruler') @@ -398,6 +399,106 @@ describe('global statusline', function() meths.input_mouse('left', 'drag', '', 0, 14, 10) eq(1, meths.get_option('cmdheight')) end) + + it('cmdline row is correct after setting cmdheight #20514', function() + command('botright split test/functional/fixtures/bigfile.txt') + meths.set_option('cmdheight', 1) + feed('L') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ────────────────────────────────────────────────────────────| + 0000;<control>;Cc;0;BN;;;;;N;NULL;;;; | + 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | + 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | + ^0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + {2:test/functional/fixtures/bigfile.txt 7,1 Top}| + | + ]]) + feed('j') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ────────────────────────────────────────────────────────────| + 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | + 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | + 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | + {2:test/functional/fixtures/bigfile.txt 8,1 0%}| + | + ]]) + meths.set_option('showtabline', 2) + screen:expect([[ + {3: }{5:2}{3: t/f/f/bigfile.txt }{4: }| + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ────────────────────────────────────────────────────────────| + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | + 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | + 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | + {2:test/functional/fixtures/bigfile.txt 8,1 0%}| + | + ]]) + meths.set_option('cmdheight', 0) + screen:expect([[ + {3: }{5:2}{3: t/f/f/bigfile.txt }{4: }| + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ────────────────────────────────────────────────────────────| + 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | + 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | + 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | + {2:test/functional/fixtures/bigfile.txt 8,1 0%}| + ]]) + meths.set_option('cmdheight', 1) + screen:expect([[ + {3: }{5:2}{3: t/f/f/bigfile.txt }{4: }| + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ────────────────────────────────────────────────────────────| + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | + 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | + 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | + ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | + {2:test/functional/fixtures/bigfile.txt 8,1 0%}| + | + ]]) + end) end) it('statusline does not crash if it has Arabic characters #19447', function() diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index f790597140..e8798ddd93 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -947,7 +947,7 @@ describe('Screen', function() {0:~ }| | ]]} - eq({{2, 0, {{'c', 0, 3}}}}, grid_lines) + eq({{2, 0, {{'c', 0, 3}, {' ', 0, 50}}}, {3, 0, {{' ', 0, 53}}}}, grid_lines) end) it('K_EVENT should not cause extra redraws with concealcursor #13196', function() @@ -994,10 +994,11 @@ describe('Screen', function() {0:~ }| | ]]} - eq({{2, 0, {{'c', 0, 3}}}}, grid_lines) + eq({{2, 0, {{'c', 0, 3}, {' ', 0, 50}}}}, grid_lines) + grid_lines = {} poke_eventloop() -- causes K_EVENT key screen:expect_unchanged() - eq({{2, 0, {{'c', 0, 3}}}}, grid_lines) + eq({}, grid_lines) -- no redraw was done end) -- Copy of Test_cursor_column_in_concealed_line_after_window_scroll in diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 98398bc7a1..f1e818119e 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -184,11 +184,10 @@ describe("'wildmenu'", function() screen:expect_unchanged() end) - it('wildmode=list,full and display+=msgsep interaction #10092', function() + it('wildmode=list,full and messages interaction #10092', function() -- Need more than 5 rows, else tabline is covered and will be redrawn. screen:try_resize(25, 7) - command('set display+=msgsep') command('set wildmenu wildmode=list,full') command('set showtabline=2') feed(':set wildm<tab>') @@ -223,44 +222,6 @@ describe("'wildmenu'", function() ]]) end) - it('wildmode=list,full and display-=msgsep interaction', function() - -- Need more than 5 rows, else tabline is covered and will be redrawn. - screen:try_resize(25, 7) - - command('set display-=msgsep') - command('set wildmenu wildmode=list,full') - feed(':set wildm<tab>') - screen:expect([[ - ~ | - ~ | - ~ | - ~ | - :set wildm | - wildmenu wildmode | - :set wildm^ | - ]]) - feed('<tab>') -- trigger wildmode full - screen:expect([[ - ~ | - ~ | - ~ | - :set wildm | - wildmenu wildmode | - wildmenu wildmode | - :set wildmenu^ | - ]]) - feed('<Esc>') - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - ~ | - | - ]]) - end) - it('wildmode=longest,list', function() -- Need more than 5 rows, else tabline is covered and will be redrawn. screen:try_resize(25, 7) @@ -365,7 +326,6 @@ describe("'wildmenu'", function() screen:try_resize(25, 7) command('set laststatus=2') - command('set display+=msgsep') feed(':set wildm') feed('<c-d>') screen:expect([[ @@ -461,20 +421,20 @@ end) describe('command line completion', function() local screen before_each(function() + clear() screen = Screen.new(40, 5) screen:set_default_attr_ids({ [1] = {bold = true, foreground = Screen.colors.Blue1}, [2] = {foreground = Screen.colors.Grey0, background = Screen.colors.Yellow}, [3] = {bold = true, reverse = true}, }) + screen:attach() end) after_each(function() os.remove('Xtest-functional-viml-compl-dir') end) it('lists directories with empty PATH', function() - clear() - screen:attach() local tmp = funcs.tempname() command('e '.. tmp) command('cd %:h') @@ -491,8 +451,6 @@ describe('command line completion', function() end) it('completes env var names #9681', function() - clear() - screen:attach() command('let $XTEST_1 = "foo" | let $XTEST_2 = "bar"') command('set wildmenu wildmode=full') feed(':!echo $XTEST_<tab>') @@ -521,6 +479,58 @@ describe('command line completion', function() :!echo $XTEST_1AaあB^ | ]]) end) + + it('does not leak memory with <S-Tab> with wildmenu and only one match #19874', function() + meths.set_option('wildmenu', true) + meths.set_option('wildmode', 'full') + meths.set_option('wildoptions', 'pum') + + feed(':sign unpla<S-Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace^ | + ]]) + + feed('<Space>buff<Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace buffer=^ | + ]]) + end) + + it('does not show matches with <S-Tab> without wildmenu with wildmode=full', function() + meths.set_option('wildmenu', false) + meths.set_option('wildmode', 'full') + + feed(':sign <S-Tab>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }| + :sign unplace^ | + ]]) + end) + + it('shows matches with <S-Tab> without wildmenu with wildmode=list', function() + meths.set_option('wildmenu', false) + meths.set_option('wildmode', 'list') + + feed(':sign <S-Tab>') + screen:expect([[ + {3: }| + :sign define | + define list undefine | + jump place unplace | + :sign unplace^ | + ]]) + end) end) describe('ui/ext_wildmenu', function() diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua index 8976c4371f..ece27ec3ff 100644 --- a/test/functional/ui/winbar_spec.lua +++ b/test/functional/ui/winbar_spec.lua @@ -7,6 +7,8 @@ local meths = helpers.meths local eq = helpers.eq local poke_eventloop = helpers.poke_eventloop local feed = helpers.feed +local funcs = helpers.funcs +local curwin = helpers.curwin local pcall_err = helpers.pcall_err describe('winbar', function() @@ -48,6 +50,11 @@ describe('winbar', function() {3:~ }| | ]]) + -- winbar is excluded from the heights returned by winheight() and getwininfo() + eq(11, funcs.winheight(0)) + local win_info = funcs.getwininfo(curwin().id)[1] + eq(11, win_info.height) + eq(1, win_info.winbar) end) it('works with custom \'fillchars\' value', function() diff --git a/test/functional/vimscript/container_functions_spec.lua b/test/functional/vimscript/container_functions_spec.lua index 04a3248c49..5bef3fce05 100644 --- a/test/functional/vimscript/container_functions_spec.lua +++ b/test/functional/vimscript/container_functions_spec.lua @@ -8,7 +8,7 @@ local clear = helpers.clear before_each(clear) describe('extend()', function() - it('suceeds to extend list with itself', function() + it('succeeds to extend list with itself', function() meths.set_var('l', {1, {}}) eq({1, {}, 1, {}}, eval('extend(l, l)')) eq({1, {}, 1, {}}, meths.get_var('l')) diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua index 0c2ca8de78..1fbdedb815 100644 --- a/test/functional/vimscript/eval_spec.lua +++ b/test/functional/vimscript/eval_spec.lua @@ -10,11 +10,14 @@ -- test/functional/vimscript/functions_spec.lua local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local lfs = require('lfs') local clear = helpers.clear local eq = helpers.eq local exc_exec = helpers.exc_exec +local exec = helpers.exec +local exec_capture = helpers.exec_capture local eval = helpers.eval local command = helpers.command local write_file = helpers.write_file @@ -144,3 +147,117 @@ describe('List support code', function() end end) end) + +-- oldtest: Test_deep_nest() +it('Error when if/for/while/try/function is nested too deep',function() + clear() + local screen = Screen.new(80, 24) + screen:attach() + meths.set_option('laststatus', 2) + exec([[ + " Deep nesting of if ... endif + func Test1() + let @a = join(repeat(['if v:true'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endif'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of for ... endfor + func Test2() + let @a = join(repeat(['for i in [1]'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endfor'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of while ... endwhile + func Test3() + let @a = join(repeat(['while v:true'], 51), "\n") + let @a ..= "\n" + let @a ..= join(repeat(['endwhile'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of try ... endtry + func Test4() + let @a = join(repeat(['try'], 51), "\n") + let @a ..= "\necho v:true\n" + let @a ..= join(repeat(['endtry'], 51), "\n") + @a + let @a = '' + endfunc + + " Deep nesting of function ... endfunction + func Test5() + let @a = join(repeat(['function X()'], 51), "\n") + let @a ..= "\necho v:true\n" + let @a ..= join(repeat(['endfunction'], 51), "\n") + @a + let @a = '' + endfunc + ]]) + screen:expect({any = '%[No Name%]'}) + feed(':call Test1()<CR>') + screen:expect({any = 'E579: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test2()<CR>') + screen:expect({any = 'E585: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test3()<CR>') + screen:expect({any = 'E585: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test4()<CR>') + screen:expect({any = 'E601: '}) + feed('<C-C>') + screen:expect({any = '%[No Name%]'}) + feed(':call Test5()<CR>') + screen:expect({any = 'E1058: '}) +end) + +describe("uncaught exception", function() + before_each(clear) + after_each(function() + os.remove('throw1.vim') + os.remove('throw2.vim') + os.remove('throw3.vim') + end) + + it('is not forgotten #13490', function() + command('autocmd BufWinEnter * throw "i am error"') + eq('i am error', exc_exec('try | new | endtry')) + + -- Like Vim, throwing here aborts the processing of the script, but does not stop :runtime! + -- from processing the others. + -- Only the first thrown exception should be rethrown from the :try below, though. + for i = 1, 3 do + write_file('throw' .. i .. '.vim', ([[ + let result ..= '%d' + throw 'throw%d' + let result ..= 'X' + ]]):format(i, i)) + end + command('set runtimepath+=. | let result = ""') + eq('throw1', exc_exec('try | runtime! throw*.vim | endtry')) + eq('123', eval('result')) + end) +end) + +describe('lambda function', function() + before_each(clear) + + it('can be shown using :function followed by <lambda> #20466', function() + command('let A = {-> 1}') + local num = exec_capture('echo A'):match("function%('<lambda>(%d+)'%)") + eq(([[ + function <lambda>%s(...) +1 return 1 + endfunction]]):format(num), exec_capture(('function <lambda>%s'):format(num))) + end) +end) diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua index b4162b2336..b49eb09512 100644 --- a/test/functional/vimscript/executable_spec.lua +++ b/test/functional/vimscript/executable_spec.lua @@ -34,11 +34,13 @@ describe('executable()', function() it('fails for invalid values', function() for _, input in ipairs({'v:null', 'v:true', 'v:false', '{}', '[]'}) do - eq('Vim(call):E928: String required', exc_exec('call executable('..input..')')) + eq('Vim(call):E1174: String required for argument 1', + exc_exec('call executable('..input..')')) end command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do - eq('Vim(call):E928: String required', exc_exec('call executable('..input..')')) + eq('Vim(call):E1174: String required for argument 1', + exc_exec('call executable('..input..')')) end end) diff --git a/test/functional/vimscript/exepath_spec.lua b/test/functional/vimscript/exepath_spec.lua index bbca954511..439dd96fcd 100644 --- a/test/functional/vimscript/exepath_spec.lua +++ b/test/functional/vimscript/exepath_spec.lua @@ -21,12 +21,12 @@ describe('exepath()', function() it('fails for invalid values', function() for _, input in ipairs({'v:null', 'v:true', 'v:false', '{}', '[]'}) do - eq('Vim(call):E928: String required', exc_exec('call exepath('..input..')')) + eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')')) end - eq('Vim(call):E1142: Non-empty string required', exc_exec('call exepath("")')) + eq('Vim(call):E1142: Non-empty string required for argument 1', exc_exec('call exepath("")')) command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do - eq('Vim(call):E928: String required', exc_exec('call exepath('..input..')')) + eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')')) end end) diff --git a/test/functional/vimscript/input_spec.lua b/test/functional/vimscript/input_spec.lua index 554d15e550..f50b39c2c5 100644 --- a/test/functional/vimscript/input_spec.lua +++ b/test/functional/vimscript/input_spec.lua @@ -8,7 +8,8 @@ local clear = helpers.clear local source = helpers.source local command = helpers.command local exc_exec = helpers.exc_exec -local nvim_async = helpers.nvim_async +local pcall_err = helpers.pcall_err +local async_meths = helpers.async_meths local NIL = helpers.NIL local screen @@ -449,6 +450,78 @@ describe('inputdialog()', function() end) describe('confirm()', function() + -- oldtest: Test_confirm() + it('works', function() + meths.set_option('more', false) -- Avoid hit-enter prompt + meths.set_option('laststatus', 2) + -- screen:expect() calls are needed to avoid feeding input too early + screen:expect({any = '%[No Name%]'}) + + async_meths.command([[let a = confirm('Press O to proceed')]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('o') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + + async_meths.command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('y') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('n') + screen:expect({any = '%[No Name%]'}) + eq(2, meths.get_var('a')) + + -- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim. + + -- confirm() should return 0 when pressing ESC. + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<Esc>') + screen:expect({any = '%[No Name%]'}) + eq(0, meths.get_var('a')) + + -- Default choice is returned when pressing <CR>. + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<CR>') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<CR>') + screen:expect({any = '%[No Name%]'}) + eq(2, meths.get_var('a')) + + async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]]) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('<CR>') + screen:expect({any = '%[No Name%]'}) + eq(0, meths.get_var('a')) + + -- Test with the {type} 4th argument + for _, type in ipairs({'Error', 'Question', 'Info', 'Warning', 'Generic'}) do + async_meths.command(([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type)) + screen:expect({any = '{CONFIRM:.+: }'}) + feed('y') + screen:expect({any = '%[No Name%]'}) + eq(1, meths.get_var('a')) + end + + eq('Vim(call):E730: using List as a String', + pcall_err(command, 'call confirm([])')) + eq('Vim(call):E730: using List as a String', + pcall_err(command, 'call confirm("Are you sure?", [])')) + eq('Vim(call):E745: Using a List as a Number', + pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", [])')) + eq('Vim(call):E730: using List as a String', + pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", 0, [])')) + end) + it("shows dialog even if :silent #8788", function() command("autocmd BufNewFile * call confirm('test')") @@ -483,7 +556,7 @@ describe('confirm()', function() feed(':call nvim_command("edit x")<cr>') check_and_clear(':call nvim_command("edit |\n') - nvim_async('command', 'edit x') + async_meths.command('edit x') check_and_clear(' |\n') end) end) diff --git a/test/functional/vimscript/json_functions_spec.lua b/test/functional/vimscript/json_functions_spec.lua index 5d1597f53d..70d0934756 100644 --- a/test/functional/vimscript/json_functions_spec.lua +++ b/test/functional/vimscript/json_functions_spec.lua @@ -343,7 +343,7 @@ describe('json_decode() function', function() exc_exec('call json_decode("\\t\\"abc\\\\u0000")')) end) - it('fails to parse unknown escape sequnces', function() + it('fails to parse unknown escape sequences', function() eq('Vim(call):E474: Unknown escape sequence: \\a"', exc_exec('call json_decode("\\t\\"\\\\a\\"")')) end) diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua index aa64006de0..8645b1e506 100644 --- a/test/functional/vimscript/map_functions_spec.lua +++ b/test/functional/vimscript/map_functions_spec.lua @@ -3,6 +3,8 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval +local exec = helpers.exec +local exec_lua = helpers.exec_lua local expect = helpers.expect local feed = helpers.feed local funcs = helpers.funcs @@ -10,6 +12,8 @@ local meths = helpers.meths local nvim = helpers.nvim local source = helpers.source local command = helpers.command +local exec_capture = helpers.exec_capture +local pcall_err = helpers.pcall_err describe('maparg()', function() before_each(clear) @@ -172,14 +176,12 @@ describe('mapset()', function() it('can restore mapping description from the dict returned by maparg()', function() meths.set_keymap('n', 'lhs', 'rhs', {desc = 'map description'}) - eq('\nn lhs rhs\n map description', - helpers.exec_capture("nmap lhs")) + eq('\nn lhs rhs\n map description', exec_capture("nmap lhs")) local mapargs = funcs.maparg('lhs', 'n', false, true) - meths.del_keymap('n', 'lhs') - eq('\nNo mapping found', helpers.exec_capture("nmap lhs")) + meths.set_keymap('n', 'lhs', 'rhs', {desc = 'MAP DESCRIPTION'}) + eq('\nn lhs rhs\n MAP DESCRIPTION', exec_capture("nmap lhs")) funcs.mapset('n', false, mapargs) - eq('\nn lhs rhs\n map description', - helpers.exec_capture("nmap lhs")) + eq('\nn lhs rhs\n map description', exec_capture("nmap lhs")) end) it('can restore "replace_keycodes" from the dict returned by maparg()', function() @@ -194,4 +196,59 @@ describe('mapset()', function() feed('foo') expect('<<lt><') end) + + it('replaces an abbreviation of the same lhs #20320', function() + command('inoreabbr foo bar') + eq('\ni foo * bar', exec_capture('iabbr foo')) + feed('ifoo ') + expect('bar ') + local mapargs = funcs.maparg('foo', 'i', true, true) + command('inoreabbr foo BAR') + eq('\ni foo * BAR', exec_capture('iabbr foo')) + feed('foo ') + expect('bar BAR ') + funcs.mapset('i', true, mapargs) + eq('\ni foo * bar', exec_capture('iabbr foo')) + feed('foo<Esc>') + expect('bar BAR bar') + end) + + it('can restore Lua callback from the dict returned by maparg()', function() + eq(0, exec_lua([[ + GlobalCount = 0 + vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + return GlobalCount + ]])) + feed('asdf') + eq(1, exec_lua([[return GlobalCount]])) + + exec_lua([[ + _G.saved_asdf_map = vim.fn.maparg('asdf', 'n', false, true) + vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 10 end }) + ]]) + feed('asdf') + eq(11, exec_lua([[return GlobalCount]])) + + exec_lua([[vim.fn.mapset('n', false, _G.saved_asdf_map)]]) + feed('asdf') + eq(12, exec_lua([[return GlobalCount]])) + + exec([[ + let g:saved_asdf_map = maparg('asdf', 'n', v:false, v:true) + lua vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 10 end }) + ]]) + feed('asdf') + eq(22, exec_lua([[return GlobalCount]])) + + command([[call mapset('n', v:false, g:saved_asdf_map)]]) + feed('asdf') + eq(23, exec_lua([[return GlobalCount]])) + end) + + it('does not leak memory if lhs is missing', function() + eq('Error executing lua: Vim:E460: entries missing in mapset() dict argument', + pcall_err(exec_lua, [[vim.fn.mapset('n', false, {rhs = 'foo'})]])) + eq('Error executing lua: Vim:E460: entries missing in mapset() dict argument', + pcall_err(exec_lua, [[vim.fn.mapset('n', false, {callback = function() end})]])) + end) end) diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index a778e2f435..ed822add72 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -642,12 +642,12 @@ describe('shell :!', function() if iswin() then feed(':4verbose %!sort /R<cr>') screen:expect{ - any=[[Executing command: .?Start%-Process sort %-ArgumentList "/R" %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]] + any=[[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | Out%-File %-Encoding UTF8 .*; exit $LastExitCode"]] } else feed(':4verbose %!sort -r<cr>') screen:expect{ - any=[[Executing command: .?Start%-Process sort %-ArgumentList "%-r" %-RedirectStandardInput .* %-RedirectStandardOutput .* %-NoNewWindow %-Wait]] + any=[[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | Out%-File %-Encoding UTF8 .*; exit $LastExitCode"]] } end feed('<CR>') diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua index 5463cfb234..1818a71ea2 100644 --- a/test/functional/vimscript/timer_spec.lua +++ b/test/functional/vimscript/timer_spec.lua @@ -131,34 +131,34 @@ describe('timers', function() nvim_async("command", "call timer_start("..load_adjust(100)..", 'AddItem', {'repeat': -1})") screen:expect([[ - ITEM 1 | + ^ITEM 1 | ITEM 2 | {1:~ }| {1:~ }| {1:~ }| - ^ | + | ]]) nvim_async("command", "let g:cont = 1") screen:expect([[ - ITEM 1 | + ^ITEM 1 | ITEM 2 | ITEM 3 | {1:~ }| {1:~ }| - ^ | + | ]]) feed("3") eq(51, eval("g:c2")) - screen:expect([[ + screen:expect{grid=[[ ^ITEM 1 | ITEM 2 | ITEM 3 | {1:~ }| {1:~ }| | - ]]) + ]], unchanged=true} end) it('can be stopped', function() |