diff options
Diffstat (limited to 'test/functional')
25 files changed, 1734 insertions, 156 deletions
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 058706718a..21e3094f8e 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -158,3 +158,98 @@ describe('API: highlight',function() assert_alive() end) end) + +describe("API: set highlight", function() + local highlight_color = { + fg = tonumber('0xff0000'), + bg = tonumber('0x0032aa'), + ctermfg = 8, + ctermbg = 15, + } + local highlight1 = { + background = highlight_color.bg, + foreground = highlight_color.fg, + bold = true, + italic = true, + } + local highlight2_config = { + ctermbg = highlight_color.ctermbg, + ctermfg = highlight_color.ctermfg, + underline = true, + reverse = true, + } + local highlight2_result = { + background = highlight_color.ctermbg, + foreground = highlight_color.ctermfg, + underline = true, + reverse = true, + } + local highlight3_config = { + background = highlight_color.bg, + foreground = highlight_color.fg, + ctermbg = highlight_color.ctermbg, + ctermfg = highlight_color.ctermfg, + bold = true, + italic = true, + reverse = true, + undercurl = true, + underline = true, + cterm = { + italic = true, + reverse = true, + undercurl = true, + } + } + local highlight3_result_gui = { + background = highlight_color.bg, + foreground = highlight_color.fg, + bold = true, + italic = true, + reverse = true, + undercurl = true, + underline = true, + } + local highlight3_result_cterm = { + background = highlight_color.ctermbg, + foreground = highlight_color.ctermfg, + italic = true, + reverse = true, + undercurl = true, + } + + local function get_ns() + local ns = meths.create_namespace('Test_set_hl') + meths._set_hl_ns(ns) + return ns + end + + before_each(clear) + + it ("can set gui highlight", function() + local ns = get_ns() + meths.set_hl(ns, 'Test_hl', highlight1) + eq(highlight1, meths.get_hl_by_name('Test_hl', true)) + end) + + it ("can set cterm highlight", function() + local ns = get_ns() + meths.set_hl(ns, 'Test_hl', highlight2_config) + eq(highlight2_result, meths.get_hl_by_name('Test_hl', false)) + end) + + it ("cterm attr defaults to gui attr", function() + local ns = get_ns() + meths.set_hl(ns, 'Test_hl', highlight1) + eq({ + bold = true, + italic = true, + }, meths.get_hl_by_name('Test_hl', false)) + end) + + it ("can overwrite attr for cterm", function() + local ns = get_ns() + meths.set_hl(ns, 'Test_hl', highlight3_config) + eq(highlight3_result_gui, meths.get_hl_by_name('Test_hl', true)) + eq(highlight3_result_cterm, meths.get_hl_by_name('Test_hl', false)) + end) +end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 6926022ee3..0c0f610401 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1986,6 +1986,10 @@ describe('API', function() eq(meths.get_option_info'winhighlight', options_info.winhighlight) end) + + it('should not crash when echoed', function() + meths.exec("echo nvim_get_all_options_info()", true) + end) end) describe('nvim_get_option_info', function() diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index ceeb84cec9..bb72b63b6c 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -387,4 +387,50 @@ describe('API/win', function() eq({oldbuf}, meths.list_bufs()) end) end) + + describe('open_win', function() + it('noautocmd option works', function() + command('autocmd BufEnter,BufLeave,BufWinEnter * let g:fired = 1') + meths.open_win(meths.create_buf(true, true), true, { + relative='win', row=3, col=3, width=12, height=3, noautocmd=true + }) + eq(0, funcs.exists('g:fired')) + meths.open_win(meths.create_buf(true, true), true, { + relative='win', row=3, col=3, width=12, height=3 + }) + eq(1, funcs.exists('g:fired')) + end) + end) + + describe('get_config', function() + it('includes border', function() + local b = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' } + local win = meths.open_win(0, true, { + relative='win', row=3, col=3, width=12, height=3, + border = b, + }) + + local cfg = meths.win_get_config(win) + eq(b, cfg.border) + end) + it('includes border with highlight group', function() + local b = { + {'a', 'Normal'}, + {'b', 'Special'}, + {'c', 'String'}, + {'d', 'Comment'}, + {'e', 'Visual'}, + {'f', 'Error'}, + {'g', 'Constant'}, + {'h', 'PreProc'}, + } + local win = meths.open_win(0, true, { + relative='win', row=3, col=3, width=12, height=3, + border = b, + }) + + local cfg = meths.win_get_config(win) + eq(b, cfg.border) + end) + end) end) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 9de0d08e79..34ab90d760 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -118,6 +118,32 @@ describe('jobs', function() end end) + it('handles case-insensitively matching #env vars', function() + nvim('command', "let $TOTO = 'abc'") + -- Since $Toto is being set in the job, it should take precedence over the + -- global $TOTO on Windows + nvim('command', "let g:job_opts = {'env': {'Toto': 'def'}, 'stdout_buffered': v:true}") + if iswin() then + nvim('command', [[let j = jobstart('set | find /I "toto="', g:job_opts)]]) + else + nvim('command', [[let j = jobstart('env | grep -i toto=', g:job_opts)]]) + end + nvim('command', "call jobwait([j])") + nvim('command', "let g:output = Normalize(g:job_opts.stdout)") + local actual = eval('g:output') + local expected + if iswin() then + -- Toto is normalized to TOTO so we can detect duplicates, and because + -- Windows doesn't care about case + expected = {'TOTO=def', ''} + else + expected = {'TOTO=abc', 'Toto=def', ''} + end + table.sort(actual) + table.sort(expected) + eq(expected, actual) + end) + it('uses &shell and &shellcmdflag if passed a string', function() nvim('command', "let $VAR = 'abc'") if iswin() then diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index d5f03db03a..658dfbda60 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -11,6 +11,7 @@ local exec_lua = helpers.exec_lua local feed = helpers.feed local funcs = helpers.funcs local mkdir = helpers.mkdir +local mkdir_p = helpers.mkdir_p local nvim_prog = helpers.nvim_prog local nvim_set = helpers.nvim_set local read_file = helpers.read_file @@ -494,6 +495,75 @@ describe('user config init', function() end) end) +describe('runtime:', function() + local xhome = 'Xhome' + local pathsep = helpers.get_pathsep() + local xconfig = xhome .. pathsep .. 'Xconfig' + + setup(function() + mkdir_p(xconfig .. pathsep .. 'nvim') + end) + + teardown(function() + rmdir(xhome) + end) + + it('loads plugin/*.lua from XDG config home', function() + local plugin_folder_path = table.concat({xconfig, 'nvim', 'plugin'}, pathsep) + local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) + mkdir_p(plugin_folder_path) + write_file(plugin_file_path, [[ vim.g.lua_plugin = 1 ]]) + + clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig }} + + eq(1, eval('g:lua_plugin')) + rmdir(plugin_folder_path) + end) + + it('loads plugin/*.lua from start plugins', function() + local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'catagory', + 'start', 'test_plugin'}, pathsep) + local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep) + local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, + pathsep) + local profiler_file = 'test_startuptime.log' + + mkdir_p(plugin_folder_path) + write_file(plugin_file_path, [[vim.g.lua_plugin = 2]]) + + clear{ args_rm={'-u'}, args={'--startuptime', profiler_file}, env={ XDG_CONFIG_HOME=xconfig }} + + eq(2, eval('g:lua_plugin')) + -- Check if plugin_file_path is listed in :scriptname + local scripts = meths.exec(':scriptnames', true) + assert.Truthy(scripts:find(plugin_file_path)) + + -- Check if plugin_file_path is listed in startup profile + local profile_reader = io.open(profiler_file, 'r') + local profile_log = profile_reader:read('*a') + profile_reader:close() + assert.Truthy(profile_log :find(plugin_file_path)) + + os.remove(profiler_file) + rmdir(plugin_path) + end) + + it('loads ftdetect/*.lua', function() + local ftdetect_folder = table.concat({xconfig, 'nvim', 'ftdetect'}, pathsep) + local ftdetect_file = table.concat({ftdetect_folder , 'new-ft.lua'}, pathsep) + mkdir_p(ftdetect_folder) + write_file(ftdetect_file , [[vim.g.lua_ftdetect = 1]]) + + -- TODO(shadmansaleh): Figure out why this test fails without + -- setting VIMRUNTIME + clear{ args_rm={'-u'}, env={XDG_CONFIG_HOME=xconfig, + VIMRUNTIME='runtime/'}} + + eq(1, eval('g:lua_ftdetect')) + rmdir(ftdetect_folder) + end) +end) + describe('user session', function() local xhome = 'Xhome' local pathsep = helpers.get_pathsep() diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index d403fbc878..b1ceff9115 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -73,6 +73,7 @@ describe('NULL', function() null_expr_test('does not crash col()', 'col(L)', 0, 0) null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) null_expr_test('does not crash line()', 'line(L)', 0, 0) + null_expr_test('does not crash line() with window id', 'line(L, 1000)', 0, 0) null_expr_test('does not crash count()', 'count(L, 1)', 0, 0) null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) null_expr_test('does not crash map()', 'map(L, "v:val")', 0, {}) @@ -96,7 +97,7 @@ describe('NULL', function() null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1) null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items') null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', - 'Type number and <Enter> or click with mouse (empty cancels): ', {0, 0}) + 'Type number and <Enter> or click with the mouse (q or empty cancels): ', {0, 0}) null_expr_test('is accepted as an empty list by writefile()', ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), 0, {0, {}}) @@ -149,6 +150,7 @@ describe('NULL', function() null_test('does not crash :execute', 'execute S', 0) null_expr_test('does not crash execute()', 'execute(S)', 0, '') null_expr_test('makes executable() error out', 'executable(S)', 'E928: String required', 0) + null_expr_test('makes timer_start() error out', 'timer_start(0, S)', 'E921: Invalid callback argument', -1) null_expr_test('does not crash filereadable()', 'filereadable(S)', 0, 0) null_expr_test('does not crash filewritable()', 'filewritable(S)', 0, 0) null_expr_test('does not crash fnamemodify()', 'fnamemodify(S, S)', 0, '') @@ -161,7 +163,6 @@ describe('NULL', function() null_expr_test('does not crash mkdir()', 'mkdir(S)', 0, 0) null_expr_test('does not crash sort()', 'sort(["b", S, "a"])', 0, {'', 'a', 'b'}) null_expr_test('does not crash split()', 'split(S)', 0, {}) - null_test('can be used to set an option', 'let &grepprg = S', 0) null_expr_test('is equal to non-existent variable', 'S == V', 0, 1) diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index 16d0dfb6a1..a03e1ae9ce 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -6,6 +6,9 @@ local clear = helpers.clear local meths = helpers.meths local feed = helpers.feed local feed_command = helpers.feed_command +local write_file = helpers.write_file +local exec = helpers.exec +local eval = helpers.eval describe(':source', function() before_each(function() @@ -44,4 +47,47 @@ describe(':source', function() command('source') eq('4', meths.exec('echo luaeval("y")', true)) end) + + it('can source lua files', function() + local test_file = 'test.lua' + write_file (test_file, [[vim.g.sourced_lua = 1]]) + + exec('source ' .. test_file) + + eq(1, eval('g:sourced_lua')) + os.remove(test_file) + end) + + it('can source selected region in lua file', function() + local test_file = 'test.lua' + + write_file (test_file, [[ + vim.g.b = 5 + vim.g.b = 6 + vim.g.b = 7 + ]]) + + command('edit '..test_file) + feed('ggjV') + feed_command(':source') + + eq(6, eval('g:b')) + os.remove(test_file) + end) + + it('can source current lua buffer without argument', function() + local test_file = 'test.lua' + + write_file (test_file, [[ + vim.g.c = 10 + vim.g.c = 11 + vim.g.c = 12 + ]]) + + command('edit '..test_file) + feed_command(':source') + + eq(12, eval('g:c')) + os.remove(test_file) + end) end) diff --git a/test/functional/fixtures/streams-test.c b/test/functional/fixtures/streams-test.c index eec447153c..be40edfe7e 100644 --- a/test/functional/fixtures/streams-test.c +++ b/test/functional/fixtures/streams-test.c @@ -6,23 +6,22 @@ #include <uv.h> -uv_loop_t *loop; -uv_process_t child_req; -uv_process_options_t options; - int main(int argc, char **argv) { - loop = uv_default_loop(); + uv_loop_t *loop = uv_default_loop(); + uv_process_t child_req; char * args[3]; args[0] = "sleep"; args[1] = "10"; args[2] = NULL; - options.exit_cb = NULL; - options.file = "sleep"; - options.args = args; - options.flags = UV_PROCESS_DETACHED; + uv_process_options_t options = { + .exit_cb = NULL, + .file = "sleep", + .args = args, + .flags = UV_PROCESS_DETACHED, + }; int r; if ((r = uv_spawn(loop, &child_req, &options))) { diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 4acb1a7d8d..08ca14c3df 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -878,6 +878,11 @@ function module.os_kill(pid) or 'kill -9 '..pid..' > /dev/null')) end +-- Create directories with non exsisting intermidiate directories +function module.mkdir_p(path) + return module.meths.call_function('mkdir', {path, 'p'}) +end + module = global_helpers.tbl_extend('error', module, global_helpers) return function(after_each) diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index 7cc31dc787..7b6f2f63e9 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -17,6 +17,7 @@ local lfs = require('lfs') local clear, feed_command, expect, eq, neq, dedent, write_file, feed = helpers.clear, helpers.feed_command, helpers.expect, helpers.eq, helpers.neq, helpers.dedent, helpers.write_file, helpers.feed +local command = helpers.command local iswin = helpers.iswin local read_file = helpers.read_file @@ -28,7 +29,7 @@ end local function prepare_gz_file(name, text) write_file(name, text..'\n') -- Compress the file with gzip. - os.execute('gzip --force '..name) + command([[call system(['gzip', '--force', ']]..name..[['])]]) -- This should create the .gz file and delete the original. neq(nil, lfs.attributes(name..'.gz')) eq(nil, lfs.attributes(name)) @@ -54,7 +55,9 @@ describe('file reading, writing and bufnew and filter autocommands', function() */ ]]) end) - before_each(clear) + before_each(function () + clear({env={GZIP=nil}}) + end) teardown(function() os.remove('Xtestfile.gz') os.remove('Xtest.c') @@ -67,7 +70,6 @@ describe('file reading, writing and bufnew and filter autocommands', function() it('FileReadPost (using gzip)', function() prepare_gz_file('Xtestfile', text1) - feed_command('let $GZIP = ""') --execute('au FileChangedShell * echo "caught FileChangedShell"') feed_command('set bin') feed_command("au FileReadPost *.gz '[,']!gzip -d") @@ -79,7 +81,6 @@ describe('file reading, writing and bufnew and filter autocommands', function() it('BufReadPre, BufReadPost (using gzip)', function() prepare_gz_file('Xtestfile', text1) local gzip_data = read_file('Xtestfile.gz') - feed_command('let $GZIP = ""') -- Setup autocommands to decompress before reading and re-compress afterwards. feed_command("au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand('<afile>'))") feed_command("au BufReadPre *.gz call rename(expand('<afile>:r'), expand('<afile>'))") diff --git a/test/functional/legacy/mksession_spec.lua b/test/functional/legacy/mksession_spec.lua new file mode 100644 index 0000000000..a2af891107 --- /dev/null +++ b/test/functional/legacy/mksession_spec.lua @@ -0,0 +1,42 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local command = helpers.command +local funcs = helpers.funcs +local eq = helpers.eq + +describe('mksession', function() + before_each(clear) + + after_each(function() + os.remove('Xtest_mks.out') + end) + + it('supports "skiprtp" value', function() + command('set sessionoptions&vi') + command('set rtp+=$HOME') + command('set pp+=$HOME') + command('mksession! Xtest_mks.out') + local found_rtp = 0 + local found_pp = 0 + for _, line in pairs(funcs.readfile('Xtest_mks.out', 'b')) do + if line:find('set runtimepath') then + found_rtp = found_rtp + 1 + end + if line:find('set packpath') then + found_pp = found_pp + 1 + end + end + eq(1, found_rtp) + eq(1, found_pp) + + command('set sessionoptions+=skiprtp') + command('mksession! Xtest_mks.out') + local found_rtp_or_pp = 0 + for _, line in pairs(funcs.readfile('Xtest_mks.out', 'b')) do + if line:find('set runtimepath') or line:find('set packpath') then + found_rtp_or_pp = found_rtp_or_pp + 1 + end + end + eq(0, found_rtp_or_pp) + end) +end) diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index 609f825177..48cd3ef9f8 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -101,9 +101,14 @@ describe('packadd', function() call setline(1, 'let g:plugin_works = 24') wq + exe 'split ' . plugdir . '/plugin/test.lua' + call setline(1, 'vim.g.plugin_lua_works = 24') + wq + packadd other call assert_equal(24, g:plugin_works) + call assert_equal(24, g:plugin_lua_works) call assert_true(len(&rtp) > len(rtp)) call assert_match(Escape(plugdir) . '\($\|,\)', &rtp) endfunc @@ -117,13 +122,18 @@ describe('packadd', function() exe 'split ' . s:plugdir . '/plugin/test.vim' call setline(1, 'let g:plugin_works = 42') wq + exe 'split ' . s:plugdir . '/plugin/test.lua' + call setline(1, 'let g:plugin_lua_works = 42') + wq let g:plugin_works = 0 + let g:plugin_lua_works = 0 packadd! mytest call assert_true(len(&rtp) > len(rtp)) call assert_match(Escape(s:plugdir) . '\($\|,\)', &rtp) call assert_equal(0, g:plugin_works) + call assert_equal(0, g:plugin_lua_works) " check the path is not added twice let new_rtp = &rtp @@ -267,6 +277,8 @@ describe('packadd', function() call assert_match('look-here', tags1[0]) let tags2 = readfile(docdir2 . '/tags') call assert_match('look-away', tags2[0]) + + call assert_fails('helptags abcxyz', 'E150:') endfunc func Test_colorscheme() diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 5da8452a51..1b2c21783e 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -401,8 +401,8 @@ describe('lua: nvim_buf_attach on_bytes', function() } feed '<cr>' check_events { - { "test1", "bytes", 1, 4, 8, 0, 115, 0, 4, 4, 0, 0, 0 }; - { "test1", "bytes", 1, 5, 7, 4, 118, 0, 0, 0, 1, 4, 5 }; + { "test1", "bytes", 1, 4, 7, 0, 114, 0, 4, 4, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 7, 0, 114, 0, 0, 0, 1, 4, 5 }; } end) @@ -447,8 +447,8 @@ describe('lua: nvim_buf_attach on_bytes', function() feed '<CR>' check_events { - { "test1", "bytes", 1, 6, 2, 2, 16, 0, 1, 1, 0, 0, 0 }; - { "test1", "bytes", 1, 7, 1, 3, 14, 0, 0, 0, 1, 3, 4 }; + { "test1", "bytes", 1, 6, 1, 2, 13, 0, 1, 1, 0, 0, 0 }; + { "test1", "bytes", 1, 7, 1, 2, 13, 0, 0, 0, 1, 3, 4 }; } end) @@ -936,6 +936,71 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + it("virtual edit", function () + local check_events = setup_eventcheck(verify, { "", " " }) + + meths.set_option("virtualedit", "all") + + feed [[<Right><Right>iab<ESC>]] + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 0, 0, 0, 2, 2 }; + { "test1", "bytes", 1, 4, 0, 2, 2, 0, 0, 0, 0, 2, 2 }; + } + + feed [[j<Right><Right>iab<ESC>]] + + check_events { + { "test1", "bytes", 1, 5, 1, 0, 5, 0, 1, 1, 0, 8, 8 }; + { "test1", "bytes", 1, 6, 1, 5, 10, 0, 0, 0, 0, 2, 2 }; + } + end) + + it("block visual paste", function() + local check_events = setup_eventcheck(verify, {"AAA", + "BBB", + "CCC", + "DDD", + "EEE", + "FFF"}) + funcs.setreg("a", "___") + feed([[gg0l<c-v>3jl"ap]]) + + check_events { + { "test1", "bytes", 1, 3, 0, 1, 1, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 3, 1, 1, 3, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 3, 2, 1, 5, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 3, 3, 1, 7, 0, 2, 2, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 0, 0, 0, 3, 3 }; + { "test1", "bytes", 1, 6, 1, 1, 6, 0, 0, 0, 0, 3, 3 }; + { "test1", "bytes", 1, 7, 2, 1, 11, 0, 0, 0, 0, 3, 3 }; + { "test1", "bytes", 1, 8, 3, 1, 16, 0, 0, 0, 0, 3, 3 }; + } + end) + + it("nvim_buf_set_lines", function() + local check_events = setup_eventcheck(verify, {"AAA", "BBB"}) + + -- delete + meths.buf_set_lines(0, 0, 1, true, {}) + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 1, 0, 4, 0, 0, 0 }; + } + + -- add + meths.buf_set_lines(0, 0, 0, true, {'asdf'}) + check_events { + { "test1", "bytes", 1, 4, 0, 0, 0, 0, 0, 0, 1, 0, 5 }; + } + + -- replace + meths.buf_set_lines(0, 0, 1, true, {'asdf', 'fdsa'}) + check_events { + { "test1", "bytes", 1, 5, 0, 0, 0, 1, 0, 5, 2, 0, 10 }; + } + end) + teardown(function() os.remove "Xtest-reload" os.remove "Xtest-undofile" diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua new file mode 100644 index 0000000000..e9c34c9228 --- /dev/null +++ b/test/functional/lua/runtime_spec.lua @@ -0,0 +1,141 @@ +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 mkdir_p = helpers.mkdir_p +local rmdir = helpers.rmdir +local write_file = helpers.write_file + +describe('runtime:', function() + local plug_dir = 'Test_Plugin' + local sep = helpers.get_pathsep() + local init = 'dummy_init.lua' + + setup(function() + io.open(init, 'w'):close() -- touch init file + clear{args = {'-u', init}} + exec('set rtp+=' .. plug_dir) + end) + + teardown(function() + os.remove(init) + end) + + before_each(function() + mkdir_p(plug_dir) + end) + + after_each(function() + rmdir(plug_dir) + end) + + describe('colors', function() + local colorscheme_folder = plug_dir .. sep .. 'colors' + + it('loads lua colorscheme', function() + local colorscheme_file = colorscheme_folder .. sep .. 'new_colorscheme.lua' + mkdir_p(colorscheme_folder) + write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]]) + + exec('colorscheme new_colorscheme') + + eq(1, eval('g:lua_colorscheme')) + rmdir(colorscheme_folder) + end) + + it('loads vim colorscheme when both lua and vim version exist', function() + local colorscheme_file = colorscheme_folder .. sep .. 'new_colorscheme' + mkdir_p(colorscheme_folder) + write_file(colorscheme_file..'.vim', [[let g:colorscheme = 'vim']]) + write_file(colorscheme_file..'.lua', [[vim.g.colorscheme = 'lua']]) + + exec('colorscheme new_colorscheme') + + eq('vim', eval('g:colorscheme')) + rmdir(colorscheme_folder) + end) + end) + + describe('compiler', function() + local compiler_folder = plug_dir .. sep .. 'compiler' + + it('loads lua compilers', function() + local compiler_file = compiler_folder .. sep .. 'new_compiler.lua' + mkdir_p(compiler_folder) + write_file(compiler_file, [[vim.g.lua_compiler = 1]]) + + exec('compiler new_compiler') + + eq(1, eval('g:lua_compiler')) + rmdir(compiler_folder) + end) + + it('loads vim compilers when both lua and vim version exist', function() + local compiler_file = compiler_folder .. sep .. 'new_compiler' + mkdir_p(compiler_folder) + write_file(compiler_file..'.vim', [[let g:compiler = 'vim']]) + write_file(compiler_file..'.lua', [[vim.g.compiler = 'lua']]) + + exec('compiler new_compiler') + + eq('vim', eval('g:compiler')) + rmdir(compiler_folder) + end) + end) + + describe('ftplugin', function() + local ftplugin_folder = table.concat({plug_dir, 'ftplugin'}, sep) + + it('loads lua ftplugins', function() + local ftplugin_file = table.concat({ftplugin_folder , 'new-ft.lua'}, sep) + mkdir_p(ftplugin_folder) + write_file(ftplugin_file , [[vim.g.lua_ftplugin = 1]]) + + exec [[set filetype=new-ft]] + eq(1, eval('g:lua_ftplugin')) + rmdir(ftplugin_folder) + end) + end) + + describe('indent', function() + local indent_folder = table.concat({plug_dir, 'indent'}, sep) + + it('loads lua indents', function() + local indent_file = table.concat({indent_folder , 'new-ft.lua'}, sep) + mkdir_p(indent_folder) + write_file(indent_file , [[vim.g.lua_indent = 1]]) + + exec [[set filetype=new-ft]] + eq(1, eval('g:lua_indent')) + rmdir(indent_folder) + end) + end) + + describe('syntax', function() + local syntax_folder = table.concat({plug_dir, 'syntax'}, sep) + + it('loads lua syntaxes on filetype change', function() + local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep) + mkdir_p(syntax_folder) + write_file(syntax_file , [[vim.g.lua_syntax = 1]]) + + exec('set filetype=my-lang') + eq(1, eval('g:lua_syntax')) + rmdir(syntax_folder) + end) + + it('loads lua syntaxes on syntax change', function() + local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep) + mkdir_p(syntax_folder) + write_file(syntax_file , [[vim.g.lua_syntax = 5]]) + + exec('set syntax=my-lang') + eq(5, eval('g:lua_syntax')) + rmdir(syntax_folder) + end) + end) + +end) + diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 9bf00b594b..836f514433 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -945,12 +945,20 @@ describe('lua stdlib', function() exec_lua [[ vim.api.nvim_set_var("testing", "hi") vim.api.nvim_set_var("other", 123) + vim.api.nvim_set_var("floaty", 5120.1) + vim.api.nvim_set_var("nullvar", vim.NIL) vim.api.nvim_set_var("to_delete", {hello="world"}) ]] eq('hi', funcs.luaeval "vim.g.testing") eq(123, funcs.luaeval "vim.g.other") + eq(5120.1, funcs.luaeval "vim.g.floaty") eq(NIL, funcs.luaeval "vim.g.nonexistant") + eq(NIL, funcs.luaeval "vim.g.nullvar") + -- lost over RPC, so test locally: + eq({false, true}, exec_lua [[ + return {vim.g.nonexistant == vim.NIL, vim.g.nullvar == vim.NIL} + ]]) eq({hello="world"}, funcs.luaeval "vim.g.to_delete") exec_lua [[ @@ -963,12 +971,20 @@ describe('lua stdlib', function() exec_lua [[ vim.api.nvim_buf_set_var(0, "testing", "hi") vim.api.nvim_buf_set_var(0, "other", 123) + vim.api.nvim_buf_set_var(0, "floaty", 5120.1) + vim.api.nvim_buf_set_var(0, "nullvar", vim.NIL) vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"}) ]] eq('hi', funcs.luaeval "vim.b.testing") eq(123, funcs.luaeval "vim.b.other") + eq(5120.1, funcs.luaeval "vim.b.floaty") eq(NIL, funcs.luaeval "vim.b.nonexistant") + eq(NIL, funcs.luaeval "vim.b.nullvar") + -- lost over RPC, so test locally: + eq({false, true}, exec_lua [[ + return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL} + ]]) eq({hello="world"}, funcs.luaeval "vim.b.to_delete") exec_lua [[ @@ -1097,6 +1113,464 @@ describe('lua stdlib', function() eq(0, funcs.luaeval "vim.wo[1000].cole") end) + describe('vim.opt', function() + -- TODO: We still need to write some tests for optlocal, opt and then getting the options + -- Probably could also do some stuff with getting things from viml side as well to confirm behavior is the same. + + it('should allow setting number values', function() + local scrolloff = exec_lua [[ + vim.opt.scrolloff = 10 + return vim.o.scrolloff + ]] + eq(scrolloff, 10) + end) + + pending('should handle STUPID window things', function() + local result = exec_lua [[ + local result = {} + + table.insert(result, vim.api.nvim_get_option('scrolloff')) + table.insert(result, vim.api.nvim_win_get_option(0, 'scrolloff')) + + return result + ]] + + eq({}, result) + end) + + it('should allow setting tables', function() + local wildignore = exec_lua [[ + vim.opt.wildignore = { 'hello', 'world' } + return vim.o.wildignore + ]] + eq(wildignore, "hello,world") + end) + + it('should allow setting tables with shortnames', function() + local wildignore = exec_lua [[ + vim.opt.wig = { 'hello', 'world' } + return vim.o.wildignore + ]] + eq(wildignore, "hello,world") + end) + + it('should error when you attempt to set string values to numeric options', function() + local result = exec_lua [[ + return { + pcall(function() vim.opt.textwidth = 'hello world' end) + } + ]] + + eq(false, result[1]) + end) + + it('should error when you attempt to setlocal a global value', function() + local result = exec_lua [[ + return pcall(function() vim.opt_local.clipboard = "hello" end) + ]] + + eq(false, result) + end) + + it('should allow you to set boolean values', function() + eq({true, false, true}, exec_lua [[ + local results = {} + + vim.opt.autoindent = true + table.insert(results, vim.bo.autoindent) + + vim.opt.autoindent = false + table.insert(results, vim.bo.autoindent) + + vim.opt.autoindent = not vim.opt.autoindent:get() + table.insert(results, vim.bo.autoindent) + + return results + ]]) + end) + + it('should change current buffer values and defaults for global local values', function() + local result = exec_lua [[ + local result = {} + + vim.opt.makeprg = "global-local" + table.insert(result, vim.api.nvim_get_option('makeprg')) + table.insert(result, (pcall(vim.api.nvim_buf_get_option, 0, 'makeprg'))) + + vim.opt_local.mp = "only-local" + table.insert(result, vim.api.nvim_get_option('makeprg')) + table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg')) + + vim.opt_global.makeprg = "only-global" + table.insert(result, vim.api.nvim_get_option('makeprg')) + table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg')) + + vim.opt.makeprg = "global-local" + table.insert(result, vim.api.nvim_get_option('makeprg')) + table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg')) + return result + ]] + + -- Set -> global & local + eq("global-local", result[1]) + eq(false, result[2]) + + -- Setlocal -> only local + eq("global-local", result[3]) + eq("only-local", result[4]) + + -- Setglobal -> only global + eq("only-global", result[5]) + eq("only-local", result[6]) + + -- set -> doesn't override previously set value + eq("global-local", result[7]) + eq("only-local", result[8]) + end) + + it('should allow all sorts of string manipulation', function() + eq({'hello', 'hello world', 'start hello world'}, exec_lua [[ + local results = {} + + vim.opt.makeprg = "hello" + table.insert(results, vim.o.makeprg) + + vim.opt.makeprg = vim.opt.makeprg + " world" + table.insert(results, vim.o.makeprg) + + vim.opt.makeprg = vim.opt.makeprg ^ "start " + table.insert(results, vim.o.makeprg) + + return results + ]]) + end) + + describe('option:get()', function() + it('should work for boolean values', function() + eq(false, exec_lua [[ + vim.opt.number = false + return vim.opt.number:get() + ]]) + end) + + it('should work for number values', function() + local tabstop = exec_lua[[ + vim.opt.tabstop = 10 + return vim.opt.tabstop:get() + ]] + + eq(10, tabstop) + end) + + it('should work for string values', function() + eq("hello world", exec_lua [[ + vim.opt.makeprg = "hello world" + return vim.opt.makeprg:get() + ]]) + end) + + it('should work for set type flaglists', function() + local formatoptions = exec_lua [[ + vim.opt.formatoptions = 'tcro' + return vim.opt.formatoptions:get() + ]] + + eq(true, formatoptions.t) + eq(true, not formatoptions.q) + end) + + it('should work for set type flaglists', function() + local formatoptions = exec_lua [[ + vim.opt.formatoptions = { t = true, c = true, r = true, o = true } + return vim.opt.formatoptions:get() + ]] + + eq(true, formatoptions.t) + eq(true, not formatoptions.q) + end) + + it('should work for array list type options', function() + local wildignore = exec_lua [[ + vim.opt.wildignore = "*.c,*.o,__pycache__" + return vim.opt.wildignore:get() + ]] + + eq(3, #wildignore) + eq("*.c", wildignore[1]) + end) + + it('should work for key-value pair options', function() + local listchars = exec_lua [[ + vim.opt.listchars = "tab:>~,space:_" + return vim.opt.listchars:get() + ]] + + eq({ + tab = ">~", + space = "_", + }, listchars) + end) + + it('should allow you to add numeric options', function() + eq(16, exec_lua [[ + vim.opt.tabstop = 12 + vim.opt.tabstop = vim.opt.tabstop + 4 + return vim.bo.tabstop + ]]) + end) + + it('should allow you to subtract numeric options', function() + eq(2, exec_lua [[ + vim.opt.tabstop = 4 + vim.opt.tabstop = vim.opt.tabstop - 2 + return vim.bo.tabstop + ]]) + end) + end) + + describe('key:value style options', function() + it('should handle dictionary style', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + + return vim.o.listchars + ]] + eq("eol:~,space:.", listchars) + end) + + it('should allow adding dictionary style', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + + vim.opt.listchars = vim.opt.listchars + { space = "-" } + + return vim.o.listchars + ]] + + eq("eol:~,space:-", listchars) + end) + + it('should allow adding dictionary style', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + vim.opt.listchars = vim.opt.listchars + { space = "-" } + { space = "_" } + + return vim.o.listchars + ]] + + eq("eol:~,space:_", listchars) + end) + + it('should allow completely new keys', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + vim.opt.listchars = vim.opt.listchars + { tab = ">>>" } + + return vim.o.listchars + ]] + + eq("eol:~,space:.,tab:>>>", listchars) + end) + + it('should allow subtracting dictionary style', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + vim.opt.listchars = vim.opt.listchars - "space" + + return vim.o.listchars + ]] + + eq("eol:~", listchars) + end) + + it('should allow subtracting dictionary style', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + vim.opt.listchars = vim.opt.listchars - "space" - "eol" + + return vim.o.listchars + ]] + + eq("", listchars) + end) + + it('should allow subtracting dictionary style multiple times', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + vim.opt.listchars = vim.opt.listchars - "space" - "space" + + return vim.o.listchars + ]] + + eq("eol:~", listchars) + end) + + it('should allow adding a key:value string to a listchars', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + vim.opt.listchars = vim.opt.listchars + "tab:>~" + + return vim.o.listchars + ]] + + eq("eol:~,space:.,tab:>~", listchars) + end) + + it('should allow prepending a key:value string to a listchars', function() + local listchars = exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + vim.opt.listchars = vim.opt.listchars ^ "tab:>~" + + return vim.o.listchars + ]] + + eq("eol:~,space:.,tab:>~", listchars) + end) + end) + + it('should automatically set when calling remove', function() + eq("foo,baz", exec_lua [[ + vim.opt.wildignore = "foo,bar,baz" + vim.opt.wildignore:remove("bar") + + return vim.o.wildignore + ]]) + end) + + it('should automatically set when calling remove with a table', function() + eq("foo", exec_lua [[ + vim.opt.wildignore = "foo,bar,baz" + vim.opt.wildignore:remove { "bar", "baz" } + + return vim.o.wildignore + ]]) + end) + + it('should automatically set when calling append', function() + eq("foo,bar,baz,bing", exec_lua [[ + vim.opt.wildignore = "foo,bar,baz" + vim.opt.wildignore:append("bing") + + return vim.o.wildignore + ]]) + end) + + it('should automatically set when calling append with a table', function() + eq("foo,bar,baz,bing,zap", exec_lua [[ + vim.opt.wildignore = "foo,bar,baz" + vim.opt.wildignore:append { "bing", "zap" } + + return vim.o.wildignore + ]]) + end) + + it('should allow adding tables', function() + local wildignore = exec_lua [[ + vim.opt.wildignore = 'foo' + return vim.o.wildignore + ]] + eq(wildignore, 'foo') + + wildignore = exec_lua [[ + vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } + return vim.o.wildignore + ]] + eq(wildignore, 'foo,bar,baz') + end) + + it('should handle adding duplicates', function() + local wildignore = exec_lua [[ + vim.opt.wildignore = 'foo' + return vim.o.wildignore + ]] + eq(wildignore, 'foo') + + wildignore = exec_lua [[ + vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } + return vim.o.wildignore + ]] + eq(wildignore, 'foo,bar,baz') + + wildignore = exec_lua [[ + vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } + return vim.o.wildignore + ]] + eq(wildignore, 'foo,bar,baz') + end) + + it('should allow adding multiple times', function() + local wildignore = exec_lua [[ + vim.opt.wildignore = 'foo' + vim.opt.wildignore = vim.opt.wildignore + 'bar' + 'baz' + return vim.o.wildignore + ]] + eq(wildignore, 'foo,bar,baz') + end) + + it('should remove values when you use minus', function() + local wildignore = exec_lua [[ + vim.opt.wildignore = 'foo' + return vim.o.wildignore + ]] + eq(wildignore, 'foo') + + wildignore = exec_lua [[ + vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } + return vim.o.wildignore + ]] + eq(wildignore, 'foo,bar,baz') + + wildignore = exec_lua [[ + vim.opt.wildignore = vim.opt.wildignore - 'bar' + return vim.o.wildignore + ]] + eq(wildignore, 'foo,baz') + end) + + it('should prepend values when using ^', function() + local wildignore = exec_lua [[ + vim.opt.wildignore = 'foo' + vim.opt.wildignore = vim.opt.wildignore ^ 'first' + return vim.o.wildignore + ]] + eq('first,foo', wildignore) + + wildignore = exec_lua [[ + vim.opt.wildignore = vim.opt.wildignore ^ 'super_first' + return vim.o.wildignore + ]] + eq(wildignore, 'super_first,first,foo') + end) + + end) + it('vim.cmd', function() exec_lua [[ vim.cmd "autocmd BufNew * ++once lua BUF = vim.fn.expand('<abuf>')" @@ -1457,6 +1931,34 @@ describe('lua stdlib', function() eq(buf2, val) end) end) + + describe('vim.api.nvim_win_call', function() + it('can access window options', function() + command('vsplit') + local win1 = meths.get_current_win() + command('wincmd w') + local win2 = exec_lua [[ + win2 = vim.api.nvim_get_current_win() + return win2 + ]] + command('wincmd p') + + eq('', meths.win_get_option(win1, 'winhighlight')) + eq('', meths.win_get_option(win2, 'winhighlight')) + + local val = exec_lua [[ + return vim.api.nvim_win_call(win2, function() + vim.cmd "setlocal winhighlight=Normal:Normal" + return vim.api.nvim_get_current_win() + end) + ]] + + eq('', meths.win_get_option(win1, 'winhighlight')) + eq('Normal:Normal', meths.win_get_option(win2, 'winhighlight')) + eq(win1, meths.get_current_win()) + eq(win2, val) + end) + end) end) describe('lua: require("mod") from packages', function() diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index a78ed07876..85c67be8f9 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -118,7 +118,7 @@ describe('health.vim', function() Error = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, Heading = { bold=true, foreground=Screen.colors.Magenta }, Heading2 = { foreground = Screen.colors.SlateBlue }, - Bar = { foreground=Screen.colors.Purple }, + Bar = { foreground = 0x6a0dad }, Bullet = { bold=true, foreground=Screen.colors.Brown }, }) command("checkhealth foo success1") diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua new file mode 100644 index 0000000000..e09d93f7cc --- /dev/null +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -0,0 +1,62 @@ +local helpers = require('test.functional.helpers')(after_each) + +local exec_lua = helpers.exec_lua +local eq = helpers.eq + +describe('vim.lsp.codelens', function() + before_each(function() + helpers.clear() + exec_lua('require("vim.lsp")') + end) + after_each(helpers.clear) + + it('on_codelens_stores_and_displays_lenses', function() + local fake_uri = "file://fake/uri" + local bufnr = exec_lua([[ + fake_uri = ... + local bufnr = vim.uri_to_bufnr(fake_uri) + local lines = {'So', 'many', 'lines'} + vim.fn.bufload(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + return bufnr + ]], fake_uri) + + exec_lua([[ + local bufnr = ... + local lenses = { + { + range = { + start = { line = 0, character = 0, }, + ['end'] = { line = 0, character = 0 } + }, + command = { title = 'Lens1', command = 'Dummy' } + }, + } + vim.lsp.codelens.on_codelens(nil, 'textDocument/codeLens', lenses, 1, bufnr) + ]], bufnr) + + local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr) + local expected = { + { + range = { + start = { line = 0, character = 0 }, + ['end'] = { line = 0, character = 0 } + }, + command = { + title = 'Lens1', + command = 'Dummy', + }, + }, + } + eq(expected, stored_lenses) + + local virtual_text_chunks = exec_lua([[ + local bufnr = ... + local ns = vim.lsp.codelens.__namespaces[1] + local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {}) + return vim.api.nvim_buf_get_extmark_by_id(bufnr, ns, extmarks[1][1], { details = true })[3].virt_text + ]], bufnr) + + eq({[1] = {'Lens1', 'LspCodeLens'}}, virtual_text_chunks) + end) +end) diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua index 8c91c4ab2c..962028e7e1 100644 --- a/test/functional/plugin/lsp/diagnostic_spec.lua +++ b/test/functional/plugin/lsp/diagnostic_spec.lua @@ -86,6 +86,39 @@ describe('vim.lsp.diagnostic', function() eq(2, #result[1]) eq('Diagnostic #1', result[1][1].message) end) + it('Can convert diagnostic to quickfix items format', function() + local bufnr = exec_lua([[ + local fake_uri = ... + return vim.uri_to_bufnr(fake_uri) + ]], fake_uri) + local result = exec_lua([[ + local bufnr = ... + vim.lsp.diagnostic.save( + { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 2, 1, 2, 1), + }, bufnr, 1 + ) + return vim.lsp.util.diagnostics_to_items(vim.lsp.diagnostic.get_all()) + ]], bufnr) + local expected = { + { + bufnr = bufnr, + col = 2, + lnum = 2, + text = 'Diagnostic #1', + type = 'E' + }, + { + bufnr = bufnr, + col = 2, + lnum = 3, + text = 'Diagnostic #2', + type = 'E' + }, + } + eq(expected, result) + end) it('should be able to save and count a single client error', function() eq(1, exec_lua [[ vim.lsp.diagnostic.save( diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 6eda515fb6..663271deab 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -9,6 +9,7 @@ local eq = helpers.eq local pcall_err = helpers.pcall_err local pesc = helpers.pesc local insert = helpers.insert +local funcs = helpers.funcs local retry = helpers.retry local NIL = helpers.NIL local read_file = require('test.helpers').read_file @@ -1392,10 +1393,10 @@ describe('LSP', function() { label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} }, { label='foocar', sortText="f", textEdit={newText='foobar'} }, -- real-world snippet text - { label='foocar', sortText="g", insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} }, - { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} }, + { label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} }, + { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} }, -- nested snippet tokens - { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} }, + { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} }, -- plain text { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} }, } @@ -1407,9 +1408,9 @@ describe('LSP', function() { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="d", insertText='foobar', textEdit={} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="f", textEdit={newText='foobar'} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="g", insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2,typ3 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2,typ3 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } }, } @@ -1820,36 +1821,20 @@ describe('LSP', function() end) describe('lsp.util.jump_to_location', function() - local default_target_bufnr - local default_target_uri = 'file://fake/uri' - - local create_buf = function(uri, lines) - for i, line in ipairs(lines) do - lines[i] = '"' .. line .. '"' - end - lines = table.concat(lines, ", ") - - -- Let's set "hidden" to true in order to avoid errors when switching - -- between buffers in test. - local code = string.format([[ - vim.api.nvim_set_option('hidden', true) + local target_bufnr - local bufnr = vim.uri_to_bufnr("%s") - local lines = {%s} + 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 - ]], uri, lines) - - return exec_lua(code) - end - - before_each(function() - default_target_bufnr = create_buf(default_target_uri, {'1st line of text', 'å å ɧ 汉语 ↥ 🤦 🦄'}) + ]] end) - local location = function(uri, start_line, start_char, end_line, end_char) + local location = function(start_line, start_char, end_line, end_char) return { - uri = uri, + uri = "file://fake/uri", range = { start = { line = start_line, character = start_char }, ["end"] = { line = end_line, character = end_char }, @@ -1857,9 +1842,9 @@ describe('LSP', function() } end - local jump = function(bufnr, msg) + local jump = function(msg) eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg)) - eq(bufnr, exec_lua[[return vim.fn.bufnr('%')]]) + eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]]) return { line = exec_lua[[return vim.fn.line('.')]], col = exec_lua[[return vim.fn.col('.')]], @@ -1867,13 +1852,13 @@ describe('LSP', function() end it('jumps to a Location', function() - local pos = jump(default_target_bufnr, location(default_target_uri, 0, 9, 0, 9)) + local pos = jump(location(0, 9, 0, 9)) eq(1, pos.line) eq(10, pos.col) end) it('jumps to a LocationLink', function() - local pos = jump(default_target_bufnr, { + local pos = jump({ targetUri = "file://fake/uri", targetSelectionRange = { start = { line = 0, character = 4 }, @@ -1889,93 +1874,22 @@ describe('LSP', function() end) it('jumps to the correct multibyte column', function() - local pos = jump(default_target_bufnr, location(default_target_uri, 1, 2, 1, 2)) + local pos = jump(location(1, 2, 1, 2)) eq(2, pos.line) eq(4, pos.col) eq('å', exec_lua[[return vim.fn.expand('<cword>')]]) end) - it('should not push item to tagstack if destination is the same as source', function() - -- Set cursor at the 2nd line, 1st character. This is the source position - -- for the test, and will also be the destination one, making the cursor - -- "motionless", thus not triggering a push to the tagstack. - exec_lua(string.format([[ - vim.api.nvim_win_set_buf(0, %d) - vim.api.nvim_win_set_cursor(0, {2, 0}) - ]], default_target_bufnr)) - - -- Jump to 'f' in 'foobar', at the 2nd line. - jump(default_target_bufnr, location(default_target_uri, 1, 0, 1, 0)) - - local stack = exec_lua[[return vim.fn.gettagstack()]] - eq(0, stack.length) - end) - - it('should not push the same item from same buffer twice to tagstack', function() - -- Set cursor at the 2nd line, 5th character. - exec_lua(string.format([[ - vim.api.nvim_win_set_buf(0, %d) - vim.api.nvim_win_set_cursor(0, {2, 4}) - ]], default_target_bufnr)) - - local stack - - -- Jump to 1st line, 1st column. - jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0)) - - stack = exec_lua[[return vim.fn.gettagstack()]] - eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from) - - -- Go back to 5th character at 2nd line, which is currently at the top of - -- the tagstack. - exec_lua(string.format([[ - vim.api.nvim_win_set_cursor(0, {2, 4}) - ]], default_target_bufnr)) - - -- Jump again to 1st line, 1st column. Since we're jumping from the same - -- position we have just jumped from, this jump shouldn't be pushed to - -- the tagstack. - jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0)) - - stack = exec_lua[[return vim.fn.gettagstack()]] - eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from) - eq(1, stack.length) - end) - - it('should not push the same item from another buffer twice to tagstack', function() - local target_uri = 'file://foo/bar' - local target_bufnr = create_buf(target_uri, {'this is a line', 'foobar'}) - - -- Set cursor at the 1st line, 3rd character of the default test buffer. - exec_lua(string.format([[ - vim.api.nvim_win_set_buf(0, %d) - vim.api.nvim_win_set_cursor(0, {1, 2}) - ]], default_target_bufnr)) - - local stack + it('adds current position to jumplist before jumping', function() + funcs.nvim_win_set_buf(0, target_bufnr) + local mark = funcs.nvim_buf_get_mark(target_bufnr, "'") + eq({ 1, 0 }, mark) - -- Jump to 1st line, 1st column of a different buffer from the source - -- position. - jump(target_bufnr, location(target_uri, 0, 0, 0, 0)) + funcs.nvim_win_set_cursor(0, {2, 3}) + jump(location(0, 9, 0, 9)) - stack = exec_lua[[return vim.fn.gettagstack()]] - eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from) - - -- Go back to 3rd character at 1st line of the default test buffer, which - -- is currently at the top of the tagstack. - exec_lua(string.format([[ - vim.api.nvim_win_set_buf(0, %d) - vim.api.nvim_win_set_cursor(0, {1, 2}) - ]], default_target_bufnr)) - - -- Jump again to 1st line, 1st column of the different buffer. Since - -- we're jumping from the same position we have just jumped from, this - -- jump shouldn't be pushed to the tagstack. - jump(target_bufnr, location(target_uri, 0, 0, 0, 0)) - - stack = exec_lua[[return vim.fn.gettagstack()]] - eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from) - eq(1, stack.length) + mark = funcs.nvim_buf_get_mark(target_bufnr, "'") + eq({ 2, 3 }, mark) end) end) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 72ff6f2fb6..d2f9148e8f 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -235,6 +235,100 @@ void ui_refresh(void) }, res) end) + it('can match special regex characters like \\ * + ( with `vim-match?`', function() + if pending_c_parser(pending) then return end + + insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", '((_) @plus (vim-match? @plus "^\\\\+$"))'.. + '((_) @times (vim-match? @times "^\\\\*$"))'.. + '((_) @paren (vim-match? @paren "^\\\\($"))'.. + '((_) @escape (vim-match? @escape "^\\\\\\\\n$"))'.. + '((_) @string (vim-match? @string "^\\"\\\\\\\\n\\"$"))') + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid,node in pairs(match) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end + table.insert(res, {pattern, mrepr}) + end + return res + ]]) + + eq({ + { 2, { { "times", '*', 0, 4, 0, 5 } } }, + { 5, { { "string", 'string_literal', 0, 16, 0, 20 } } }, + { 4, { { "escape", 'escape_sequence', 0, 17, 0, 19 } } }, + { 3, { { "paren", '(', 0, 22, 0, 23 } } }, + { 1, { { "plus", '+', 0, 25, 0, 26 } } }, + { 2, { { "times", '*', 0, 30, 0, 31 } } }, + }, res) + end) + + it('supports builtin query predicate any-of?', function() + if pending_c_parser(pending) then return end + + insert([[ + #include <stdio.h> + + int main(void) { + int i; + for(i=1; i<=100; i++) { + if(((i%3)||(i%5))== 0) + printf("number= %d FizzBuzz\n", i); + else if((i%3)==0) + printf("number= %d Fizz\n", i); + else if((i%5)==0) + printf("number= %d Buzz\n", i); + else + printf("number= %d\n",i); + } + return 0; + } + ]]) + exec_lua([[ + function get_query_result(query_text) + cquery = vim.treesitter.parse_query("c", query_text) + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for cid, node in cquery:iter_captures(tree:root(), 0) do + -- can't transmit node over RPC. just check the name, range, and text + local text = vim.treesitter.get_node_text(node, 0) + local range = {node:range()} + table.insert(res, {cquery.captures[cid], node:type(), range, text}) + end + return res + end + ]]) + + local res0 = exec_lua([[return get_query_result(...)]], + [[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]]) + eq({ + { "c-keyword", "primitive_type", { 2, 2, 2, 5 }, "int" }, + { "c-keyword", "primitive_type", { 3, 4, 3, 7 }, "int" }, + }, res0) + + local res1 = exec_lua([[return get_query_result(...)]], + [[ + ((string_literal) @fizzbuzz-strings (#any-of? @fizzbuzz-strings + "\"number= %d FizzBuzz\\n\"" + "\"number= %d Fizz\\n\"" + "\"number= %d Buzz\\n\"" + )) + ]]) + eq({ + { "fizzbuzz-strings", "string_literal", { 6, 15, 6, 38 }, "\"number= %d FizzBuzz\\n\""}, + { "fizzbuzz-strings", "string_literal", { 8, 15, 8, 34 }, "\"number= %d Fizz\\n\""}, + { "fizzbuzz-strings", "string_literal", { 10, 15, 10, 34 }, "\"number= %d Buzz\\n\""}, + }, res1) + end) + it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() if pending_c_parser(pending) then return end @@ -308,7 +402,7 @@ void ui_refresh(void) return list ]] - eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) + eq({ 'any-of?', 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) end) @@ -599,6 +693,56 @@ int x = INT_MAX; eq(result, "value") end) + + describe("when setting a key on a capture", function() + it("it should create the nested table", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local query = require("vim.treesitter.query") + local value + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0) do + for _, nested_tbl in pairs(metadata) do + return nested_tbl.key + end + end + ]]) + + eq(result, "value") + end) + + it("it should not overwrite the nested table", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local query = require("vim.treesitter.query") + local result + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value") (#set! @number "key2" "value2"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0) do + for _, nested_tbl in pairs(metadata) do + return nested_tbl + end + end + ]]) + local expected = { + ["key"] = "value", + ["key2"] = "value2", + } + + eq(expected, result) + end) + end) end) end) end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 82d3075be2..98aafd8757 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -66,6 +66,18 @@ describe('decorations providers', function() expect_events(expected, actual, "beam trace") end + it('does not OOM when inserting, rather than appending, to the decoration provider vector', function() + -- Add a dummy decoration provider with a larger ns id than what setup_provider() creates. + -- This forces get_decor_provider() to insert into the providers vector, + -- rather than append, which used to spin in an infinite loop allocating + -- memory until nvim crashed/was killed. + setup_provider([[ + local ns2 = a.nvim_create_namespace "ns2" + a.nvim_set_decoration_provider(ns2, {}) + ]]) + helpers.assert_alive() + end) + it('leave a trace', function() insert(mulholland) @@ -333,6 +345,35 @@ describe('decorations providers', function() ]]} end) + it('can have virtual text of the style: right_align', function() + insert(mulholland) + setup_provider [[ + local hl = a.nvim_get_hl_id_by_name "ErrorMsg" + local test_ns = a.nvim_create_namespace "mulholland" + function on_do(event, ...) + if event == "line" then + local win, buf, line = ... + a.nvim_buf_set_extmark(buf, test_ns, line, 0, { + virt_text = {{'+'}, {string.rep(' ', line+1), 'ErrorMsg'}}; + virt_text_pos='right_align'; + ephemeral = true; + }) + end + end + ]] + + screen:expect{grid=[[ + // just to see if there was an acciden+{2: }| + // on Mulholland Drive +{2: }| + try_start(); +{2: }| + bufref_T save_buf; +{2: }| + switch_buffer(&save_buf, buf); +{2: }| + posp = getmark(mark, false); +{2: }| + restore_buffer(&save_buf);^ +{2: }| + | + ]]} + end) + it('can highlight beyond EOL', function() insert(mulholland) setup_provider [[ @@ -366,7 +407,7 @@ describe('decorations providers', function() end) describe('extmark decorations', function() - local screen + local screen, ns before_each( function() clear() screen = Screen.new(50, 15) @@ -397,6 +438,8 @@ describe('extmark decorations', function() [23] = {foreground = Screen.colors.Magenta1, background = Screen.colors.LightGrey}; [24] = {bold = true}; } + + ns = meths.create_namespace 'test' end) local example_text = [[ @@ -417,7 +460,6 @@ end]] insert(example_text) feed 'gg' - local ns = meths.create_namespace 'test' for i = 1,9 do meths.buf_set_extmark(0, ns, i, 0, { virt_text={{'|', 'LineNr'}}, virt_text_pos='overlay'}) if i == 3 or (i >= 6 and i <= 9) then @@ -484,7 +526,6 @@ end]] it('can have virtual text of overlay position and styling', function() insert(example_text) feed 'gg' - local ns = meths.create_namespace 'test' command 'set ft=lua' command 'syntax on' @@ -572,4 +613,88 @@ end]] {24:-- VISUAL LINE --} | ]]} end) + + it('can have virtual text of fixed win_col position', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 0, { virt_text={{'Very', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 2, 10, { virt_text={{'Much', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 3, 15, { virt_text={{'Error', 'ErrorMsg'}}, virt_text_win_col=31, hl_mode='blend'}) + meths.buf_set_extmark(0, ns, 7, 21, { virt_text={{'-', 'NonText'}}, virt_text_win_col=4, hl_mode='blend'}) + + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if hl_id_cell ~= nil then {4:Much} | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + | + ]]} + + feed '3G12|i<cr><esc>' + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if hl_i {4:Much} | + ^d_cell ~= nil then | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + | + ]]} + + feed 'u:<cr>' + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if hl_i^d_cell ~= nil then {4:Much} | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + {1:~ }| + : | + ]]} + + feed '8|i<cr><esc>' + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(item) | + if | + ^hl_id_cell ~= nil then {4:Much} | + hl_id = hl_id_cell {4:Error} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text | + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + {1:~ }| + | + ]]} + end) end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index ccb13a69d2..9fa0ad08f1 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -62,6 +62,27 @@ describe('float window', function() eq(1000, funcs.win_getid()) end) + it('win_execute() should work' , function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {'the floatwin', 'abc', 'def'}) + local win = meths.open_win(buf, false, {relative='win', width=16, height=1, row=0, col=10}) + local line = funcs.win_execute(win, 'echo getline(1)') + eq('\nthe floatwin', line) + eq('\n1', funcs.win_execute(win, 'echo line(".",'..win.id..')')) + eq('\n3', funcs.win_execute(win, 'echo line("$",'..win.id..')')) + eq('\n0', funcs.win_execute(win, 'echo line("$", 123456)')) + funcs.win_execute(win, 'bwipe!') + end) + + it('win_execute() call commands that not allowed' , function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'}) + local win = meths.open_win(buf, true, {relative='win', width=16, height=1, row=0, col=10}) + eq(pcall_err(funcs.win_execute, win, 'close'), 'Vim(close):E37: No write since last change (add ! to override)') + eq(pcall_err(funcs.win_execute, win, 'bdelete'), 'Vim(bdelete):E89: No write since last change for buffer 2 (add ! to override)') + funcs.win_execute(win, 'bwipe!') + end) + it('closed immediately by autocmd #11383', function() eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately', pcall_err(exec_lua, [[ @@ -690,6 +711,49 @@ describe('float window', function() ]]} end + meths.win_set_config(win, {border="rounded"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {5:╭─────────╮}| + {5:│}{1: halloj! }{5:│}| + {5:│}{1: BORDAA }{5:│}| + {5:╰─────────╯}| + ]], float_pos={ + [5] = { { id = 1002 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╭─────────╮}{0: }| + {0:~ }{5:│}{1: halloj! }{5:│}{0: }| + {0:~ }{5:│}{1: BORDAA }{5:│}{0: }| + {0:~ }{5:╰─────────╯}{0: }| + | + ]]} + end + meths.win_set_config(win, {border="solid"}) if multigrid then screen:expect{grid=[[ @@ -910,6 +974,57 @@ describe('float window', function() end end) + it('terminates border on edge of viewport when window extends past viewport', function() + local buf = meths.create_buf(false, false) + meths.open_win(buf, false, {relative='editor', width=40, height=7, row=0, col=0, border="single"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:┌────────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:└────────────────────────────────────────┘}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 0, 0, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + {5:^┌──────────────────────────────────────┐}| + {5:│}{1: }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:│}{2:~ }{5:│}| + {5:└──────────────────────────────────────┘}| + | + ]]} + end + end) + it('with border show popupmenu', function() screen:try_resize(40,10) local buf = meths.create_buf(false, false) @@ -1008,8 +1123,8 @@ describe('float window', function() {1: abb }| {13: acc }| ]], float_pos={ - [5] = { { id = 1002 }, "NW", 1, 0, 5, true }, - [6] = { { id = -1 }, "NW", 5, 4, 0, false } + [5] = { { id = 1002 }, "NW", 1, 0, 5, true, 50 }, + [6] = { { id = -1 }, "NW", 5, 4, 0, false, 100 } }, win_viewport={ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3}; @@ -2686,8 +2801,8 @@ describe('float window', function() {1: word }| {1: longtext }| ]], float_pos={ - [4] = {{ id = 1001 }, "NW", 1, 2, 5, true}, - [5] = {{ id = -1 }, "NW", 4, 1, 1, false} + [4] = {{ id = 1001 }, "NW", 1, 2, 5, true, 50}, + [5] = {{ id = -1 }, "NW", 4, 1, 1, false, 100} }} else screen:expect([[ @@ -2773,8 +2888,8 @@ describe('float window', function() {1:yy }| {1:zz }| ]], float_pos={ - [4] = {{ id = 1001 }, "NW", 1, 2, 5, true}, - [5] = {{ id = -1 }, "NW", 2, 1, 0, false} + [4] = {{ id = 1001 }, "NW", 1, 2, 5, true, 50}, + [5] = {{ id = -1 }, "NW", 2, 1, 0, false, 100} }} else screen:expect([[ @@ -2829,6 +2944,53 @@ describe('float window', function() end end) + it('command menu rendered above cursor (pum_above)', function() + command('set wildmenu wildmode=longest:full wildoptions=pum') + feed(':sign u<tab>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + :sign un^ | + ## grid 4 + {7: }| + {12:~ }| + {12:~ }| + {12:~ }| + ## grid 5 + {1: undefine }| + {1: unplace }| + ]], float_pos={ + [5] = {{id = -1}, "SW", 1, 6, 5, false, 250}; + [4] = {{id = 1001}, "NW", 1, 2, 5, true, 50}; + }} + else + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }{7: }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{1: undefine }{0: }| + {0:~ }{1: unplace }{0: }| + :sign un^ | + ]]} + end + end) + it('with ext_popupmenu', function() screen:set_option('ext_popupmenu', true) feed('ix ') @@ -3035,7 +3197,7 @@ describe('float window', function() {1:word }| {1:longtext }| ]], float_pos={ - [4] = {{id = -1}, "NW", 2, 1, 0, false}} + [4] = {{id = -1}, "NW", 2, 1, 0, false, 100}} } else screen:expect([[ @@ -3079,8 +3241,8 @@ describe('float window', function() {15:some info }| {15:about item }| ]], float_pos={ - [4] = {{id = -1}, "NW", 2, 1, 0, false}, - [6] = {{id = 1002}, "NW", 2, 1, 12, true}, + [4] = {{id = -1}, "NW", 2, 1, 0, false, 100}, + [6] = {{id = 1002}, "NW", 2, 1, 12, true, 50}, }} else screen:expect([[ @@ -3194,7 +3356,7 @@ describe('float window', function() {1:word }| {1:longtext }| ]], float_pos={ - [4] = {{id = -1}, "NW", 2, 1, 0, false}, + [4] = {{id = -1}, "NW", 2, 1, 0, false, 100}, }} else screen:expect([[ @@ -6227,6 +6389,69 @@ describe('float window', function() ]]} end end) + + it('can use z-index', function() + local buf = meths.create_buf(false,false) + local win1 = meths.open_win(buf, false, {relative='editor', width=20, height=3, row=1, col=5, zindex=30}) + meths.win_set_option(win1, "winhl", "Normal:ErrorMsg,EndOfBuffer:ErrorMsg") + local win2 = meths.open_win(buf, false, {relative='editor', width=20, height=3, row=2, col=6, zindex=50}) + meths.win_set_option(win2, "winhl", "Normal:Search,EndOfBuffer:Search") + local win3 = meths.open_win(buf, false, {relative='editor', width=20, height=3, row=3, col=7, zindex=40}) + meths.win_set_option(win3, "winhl", "Normal:Question,EndOfBuffer:Question") + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {7: }| + {7:~ }| + {7:~ }| + ## grid 5 + {17: }| + {17:~ }| + {17:~ }| + ## grid 6 + {8: }| + {8:~ }| + {8:~ }| + ]], float_pos={ + [4] = {{id = 1001}, "NW", 1, 1, 5, true, 30}; + [5] = {{id = 1002}, "NW", 1, 2, 6, true, 50}; + [6] = {{id = 1003}, "NW", 1, 3, 7, true, 40}; + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }{7: }{0: }| + {0:~ }{7:~}{17: }{0: }| + {0:~ }{7:~}{17:~ }{8: }{0: }| + {0:~ }{17:~ }{8: }{0: }| + {0:~ }{8:~ }{0: }| + | + ]]} + end + end) end describe('with ext_multigrid', function() diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 9d7719a7c0..c238898244 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -811,7 +811,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:^~ }| ]], messages={ - {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Helli"\nType number and <Enter> or click with mouse (empty cancels): ' } }, kind = ""} + {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ' } }, kind = ""} }} feed('1') @@ -822,7 +822,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:^~ }| ]], messages={ - {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Helli"\nType number and <Enter> or click with mouse (empty cancels): ' } }, kind = ""}, + {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ' } }, kind = ""}, { content = { { "1" } }, kind = "" } }} diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index fcf6926433..f73d051857 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -429,6 +429,15 @@ screen:redraw_debug() to show all intermediate screen states. ]]) extstate.win_viewport = nil end + if expected.float_pos then + expected.float_pos = deepcopy(expected.float_pos) + for _, v in pairs(expected.float_pos) do + if not v.external and v[7] == nil then + v[7] = 50 + end + end + end + -- Convert assertion errors into invalid screen state descriptions. for _, k in ipairs(concat_tables(ext_keys, {'mode', 'mouse_enabled'})) do -- Empty states are considered the default and need not be mentioned. @@ -1287,6 +1296,11 @@ function Screen:get_snapshot(attrs, ignore) end local function fmt_ext_state(name, state) + local function remove_all_metatables(item, path) + if path[#path] ~= inspect.METATABLE then + return item + end + end if name == "win_viewport" then local str = "{\n" for k,v in pairs(state) do @@ -1295,13 +1309,18 @@ local function fmt_ext_state(name, state) ..", curcol = "..v.curcol.."};\n") end return str .. "}" - else - -- TODO(bfredl): improve formatting of more states - local function remove_all_metatables(item, path) - if path[#path] ~= inspect.METATABLE then - return item + elseif name == "float_pos" then + local str = "{\n" + for k,v in pairs(state) do + str = str.." ["..k.."] = {{id = "..v[1].id.."}" + for i = 2, #v do + str = str..", "..inspect(v[i]) end + str = str .. "};\n" end + return str .. "}" + else + -- TODO(bfredl): improve formatting of more states return inspect(state,{process=remove_all_metatables}) end end diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua index 2c6e586665..de77100cc0 100644 --- a/test/functional/ui/spell_spec.lua +++ b/test/functional/ui/spell_spec.lua @@ -36,7 +36,7 @@ describe("'spell'", function() feed('ggJJJJJJ0') screen:expect([[ {1:^Lorem} {1:ipsum} dolor sit {1:amet}, {1:consectetur} {1:adipiscing} {1:elit}, {1:sed} do {1:eiusmod} {1:tempor} {1:i}| - {1:ncididunt} {1:ut} {1:labore} {1:et} {1:dolore} {1:magna} {1:aliqua}. {1:Ut} {1:enim} ad minim {1:veniam}, {1:quis} {1:nostru}| + {1:ncididunt} {1:ut} {1:labore} et {1:dolore} {1:magna} {1:aliqua}. {1:Ut} {1:enim} ad minim {1:veniam}, {1:quis} {1:nostru}| {1:d} {1:exercitation} {1:ullamco} {1:laboris} {1:nisi} {1:ut} {1:aliquip} ex ea {1:commodo} {1:consequat}. {1:Duis} {1:aut}| {1:e} {1:irure} dolor in {1:reprehenderit} in {1:voluptate} {1:velit} {1:esse} {1:cillum} {1:dolore} {1:eu} {1:fugiat} {1:n}| {1:ulla} {1:pariatur}. {1:Excepteur} {1:sint} {1:occaecat} {1:cupidatat} non {1:proident}, {1:sunt} in culpa {1:qui}| @@ -44,6 +44,7 @@ describe("'spell'", function() {0:~ }| | ]]) + end) it('has correct highlight at start of line', function() |