diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
commit | 9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch) | |
tree | 607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /test/functional/lua | |
parent | 9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff) | |
parent | 3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff) | |
download | rneovim-usermarks.tar.gz rneovim-usermarks.tar.bz2 rneovim-usermarks.zip |
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'test/functional/lua')
-rw-r--r-- | test/functional/lua/api_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/lua/buffer_updates_spec.lua | 20 | ||||
-rw-r--r-- | test/functional/lua/diagnostic_spec.lua | 199 | ||||
-rw-r--r-- | test/functional/lua/ffi_spec.lua | 19 | ||||
-rw-r--r-- | test/functional/lua/filetype_spec.lua | 8 | ||||
-rw-r--r-- | test/functional/lua/fs_spec.lua | 181 | ||||
-rw-r--r-- | test/functional/lua/help_spec.lua | 50 | ||||
-rw-r--r-- | test/functional/lua/inspector_spec.lua | 56 | ||||
-rw-r--r-- | test/functional/lua/overrides_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/lua/runtime_spec.lua | 56 | ||||
-rw-r--r-- | test/functional/lua/secure_spec.lua | 284 | ||||
-rw-r--r-- | test/functional/lua/spell_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/lua/thread_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/lua/ui_event_spec.lua | 147 | ||||
-rw-r--r-- | test/functional/lua/ui_spec.lua | 46 | ||||
-rw-r--r-- | test/functional/lua/uri_spec.lua | 18 | ||||
-rw-r--r-- | test/functional/lua/vim_spec.lua | 264 | ||||
-rw-r--r-- | test/functional/lua/xdiff_spec.lua | 18 |
18 files changed, 1271 insertions, 105 deletions
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..d364986ad7 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -16,7 +16,7 @@ describe('vim.diagnostic', function() exec_lua [[ require('vim.diagnostic') - function make_diagnostic(msg, x1, y1, x2, y2, severity, source) + function make_diagnostic(msg, x1, y1, x2, y2, severity, source, code) return { lnum = x1, col = y1, @@ -25,23 +25,24 @@ describe('vim.diagnostic', function() message = msg, severity = severity, source = source, + code = code, } end - function make_error(msg, x1, y1, x2, y2, source) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source) + function make_error(msg, x1, y1, x2, y2, source, code) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source, code) end - function make_warning(msg, x1, y1, x2, y2, source) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source) + function make_warning(msg, x1, y1, x2, y2, source, code) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source, code) end - function make_info(msg, x1, y1, x2, y2, source) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source) + function make_info(msg, x1, y1, x2, y2, source, code) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source, code) end - function make_hint(msg, x1, y1, x2, y2, source) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source) + function make_hint(msg, x1, y1, x2, y2, source, code) + return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source, code) end function count_diagnostics(bufnr, severity, namespace) @@ -89,20 +90,25 @@ describe('vim.diagnostic', function() 'DiagnosticFloatingError', 'DiagnosticFloatingHint', 'DiagnosticFloatingInfo', + 'DiagnosticFloatingOk', 'DiagnosticFloatingWarn', 'DiagnosticHint', 'DiagnosticInfo', + 'DiagnosticOk', 'DiagnosticSignError', 'DiagnosticSignHint', 'DiagnosticSignInfo', + 'DiagnosticSignOk', 'DiagnosticSignWarn', 'DiagnosticUnderlineError', 'DiagnosticUnderlineHint', 'DiagnosticUnderlineInfo', + 'DiagnosticUnderlineOk', 'DiagnosticUnderlineWarn', 'DiagnosticVirtualTextError', 'DiagnosticVirtualTextHint', 'DiagnosticVirtualTextInfo', + 'DiagnosticVirtualTextOk', 'DiagnosticVirtualTextWarn', 'DiagnosticWarn', }, exec_lua([[return vim.fn.getcompletion('Diagnostic', 'highlight')]])) @@ -128,6 +134,55 @@ 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('removes diagnostic from stale cache on reset', function() + local diagnostics = exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + make_error('Diagnostic #2', 2, 1, 2, 1), + }) + local other_bufnr = vim.fn.bufadd('test | test') + vim.cmd('noautocmd bwipeout! ' .. diagnostic_bufnr) + return vim.diagnostic.get(diagnostic_bufnr) + ]] + eq(2, #diagnostics) + diagnostics = exec_lua [[ + vim.diagnostic.reset() + return vim.diagnostic.get() + ]] + eq(0, #diagnostics) + end) + it('resolves buffer number 0 to the current buffer', function() eq(2, exec_lua [[ vim.api.nvim_set_current_buf(diagnostic_bufnr) @@ -1129,6 +1184,44 @@ end) eq(" some_linter: 👀 Warning", result[1][2][1]) eq(" another_linter: 🔥 Error", result[2][2][1]) end) + + it('can add a suffix to virtual text', function() + eq(' Some error ✘', exec_lua [[ + local diagnostics = { + make_error('Some error', 0, 0, 0, 0), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { + underline = false, + virtual_text = { + prefix = '', + suffix = ' ✘', + } + }) + + local extmarks = get_virt_text_extmarks(diagnostic_ns) + local virt_text = extmarks[1][4].virt_text[2][1] + return virt_text + ]]) + + eq(' Some error [err-code]', exec_lua [[ + local diagnostics = { + make_error('Some error', 0, 0, 0, 0, nil, 'err-code'), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, { + underline = false, + virtual_text = { + prefix = '', + suffix = function(diag) return string.format(' [%s]', diag.code) end, + } + }) + + local extmarks = get_virt_text_extmarks(diagnostic_ns) + local virt_text = extmarks[1][4].virt_text[2][1] + return virt_text + ]]) + end) end) describe('set()', function() @@ -1745,10 +1838,55 @@ end) return lines ]]) - eq("Error executing lua: .../diagnostic.lua:0: prefix: expected 'string' or 'table' or 'function', got 42", + eq(".../diagnostic.lua:0: prefix: expected string|table|function, got number", pcall_err(exec_lua, [[ vim.diagnostic.open_float({ prefix = 42 }) ]])) end) + it('can add a suffix to diagnostics', function() + -- Default is to render the diagnostic error code + eq({'1. Syntax error [code-x]', '2. Some warning [code-y]'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3, nil, "code-x"), + make_warning("Some warning", 1, 1, 1, 3, nil, "code-y"), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"}) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + + eq({'1. Syntax error', '2. Some warning'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3, nil, "code-x"), + make_warning("Some warning", 1, 1, 1, 3, nil, "code-y"), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer", suffix = ""}) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + + -- Suffix is rendered on the last line of a multiline diagnostic + eq({'1. Syntax error', ' More context [code-x]'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error\nMore context", 0, 1, 0, 3, nil, "code-x"), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"}) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) + + eq(".../diagnostic.lua:0: suffix: expected string|table|function, got number", + pcall_err(exec_lua, [[ vim.diagnostic.open_float({ suffix = 42 }) ]])) + end) + it('works with the old signature', function() eq({'1. Syntax error'}, exec_lua [[ local diagnostics = { @@ -1952,19 +2090,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) @@ -1979,5 +2124,31 @@ end) return vim.g.diagnostic_autocmd_triggered == diagnostic_bufnr ]]) end) + + it("checks if diagnostics are disabled in a buffer", function() + eq({true, true, true , true}, exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + }) + vim.api.nvim_set_current_buf(diagnostic_bufnr) + vim.diagnostic.disable() + return { + vim.diagnostic.is_disabled(), + vim.diagnostic.is_disabled(diagnostic_bufnr), + vim.diagnostic.is_disabled(diagnostic_bufnr, diagnostic_ns), + vim.diagnostic.is_disabled(_, diagnostic_ns), + } + ]]) + + eq({false, false, false , false}, exec_lua [[ + vim.diagnostic.enable() + return { + vim.diagnostic.is_disabled(), + vim.diagnostic.is_disabled(diagnostic_bufnr), + vim.diagnostic.is_disabled(diagnostic_bufnr, diagnostic_ns), + vim.diagnostic.is_disabled(_, diagnostic_ns), + } + ]]) + end) end) end) diff --git a/test/functional/lua/ffi_spec.lua b/test/functional/lua/ffi_spec.lua index 80c01a2b8c..18b13a8959 100644 --- a/test/functional/lua/ffi_spec.lua +++ b/test/functional/lua/ffi_spec.lua @@ -29,32 +29,37 @@ describe('ffi.cdef', function() typedef struct window_S win_T; typedef struct {} stl_hlrec_t; typedef struct {} StlClickRecord; + typedef struct {} statuscol_T; typedef struct {} Error; win_T *find_window_by_handle(int Window, Error *err); int build_stl_str_hl( win_T *wp, - char_u *out, + char *out, size_t outlen, - char_u *fmt, - int use_sandbox, - char_u fillchar, + char *fmt, + char *opt_name, + int opt_scope, + int fillchar, int maxwidth, stl_hlrec_t **hltab, - StlClickRecord **tabtab + StlClickRecord **tabtab, + statuscol_T *scp ); ]] return ffi.C.build_stl_str_hl( ffi.C.find_window_by_handle(0, ffi.new('Error')), - ffi.new('char_u[1024]'), + ffi.new('char[1024]'), 1024, - ffi.cast('char_u*', 'StatusLineOfLength20'), + ffi.cast('char*', 'StatusLineOfLength20'), + nil, 0, 0, 0, nil, + nil, nil ) ]=]) 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..642d36f63a 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') local clear = helpers.clear local exec_lua = helpers.exec_lua @@ -7,10 +8,43 @@ local mkdir_p = helpers.mkdir_p local rmdir = helpers.rmdir local nvim_dir = helpers.nvim_dir local test_build_dir = helpers.test_build_dir -local iswin = helpers.iswin local nvim_prog = helpers.nvim_prog +local is_os = helpers.is_os -local nvim_prog_basename = iswin() and 'nvim.exe' or 'nvim' +local nvim_prog_basename = is_os('win') and 'nvim.exe' or 'nvim' + +local test_basename_dirname_eq = { + '~/foo/', + '~/foo', + '~/foo/bar.lua', + 'foo.lua', + ' ', + '', + '.', + '..', + '../', + '~', + '/usr/bin', + '/usr/bin/gcc', + '/', + '/usr/', + '/usr', + 'c:/usr', + 'c:/', + 'c:', + 'c:/users/foo', + 'c:/users/foo/bar.lua', + 'c:/users/foo/bar/../', +} + +local tests_windows_paths = { + 'c:\\usr', + 'c:\\', + 'c:', + 'c:\\users\\foo', + 'c:\\users\\foo\\bar.lua', + 'c:\\users\\foo\\bar\\..\\', +} before_each(clear) @@ -41,19 +75,73 @@ describe('vim.fs', function() local nvim_dir = ... return vim.fs.dirname(nvim_dir) ]], nvim_dir)) + + local function test_paths(paths) + for _, path in ipairs(paths) do + eq( + exec_lua([[ + local path = ... + return vim.fn.fnamemodify(path,':h'):gsub('\\', '/') + ]], path), + exec_lua([[ + local path = ... + return vim.fs.dirname(path) + ]], path), + path + ) + end + end + + test_paths(test_basename_dirname_eq) + if is_os('win') then + test_paths(tests_windows_paths) + end end) end) describe('basename()', function() it('works', function() + eq(nvim_prog_basename, exec_lua([[ local nvim_prog = ... return vim.fs.basename(nvim_prog) ]], nvim_prog)) + + local function test_paths(paths) + for _, path in ipairs(paths) do + eq( + exec_lua([[ + local path = ... + return vim.fn.fnamemodify(path,':t'):gsub('\\', '/') + ]], path), + exec_lua([[ + local path = ... + return vim.fs.basename(path) + ]], path), + path + ) + end + end + + test_paths(test_basename_dirname_eq) + if is_os('win') then + test_paths(tests_windows_paths) + end end) end) describe('dir()', function() + before_each(function() + lfs.mkdir('testd') + lfs.mkdir('testd/a') + lfs.mkdir('testd/a/b') + lfs.mkdir('testd/a/b/c') + end) + + after_each(function() + rmdir('testd') + end) + it('works', function() eq(true, exec_lua([[ local dir, nvim = ... @@ -65,6 +153,71 @@ describe('vim.fs', function() return false ]], nvim_dir, nvim_prog_basename)) end) + + it('works with opts.depth and opts.skip', function() + io.open('testd/a1', 'w'):close() + io.open('testd/b1', 'w'):close() + io.open('testd/c1', 'w'):close() + io.open('testd/a/a2', 'w'):close() + io.open('testd/a/b2', 'w'):close() + io.open('testd/a/c2', 'w'):close() + io.open('testd/a/b/a3', 'w'):close() + io.open('testd/a/b/b3', 'w'):close() + io.open('testd/a/b/c3', 'w'):close() + io.open('testd/a/b/c/a4', 'w'):close() + io.open('testd/a/b/c/b4', 'w'):close() + io.open('testd/a/b/c/c4', 'w'):close() + + local function run(dir, depth, skip) + local r = exec_lua([[ + local dir, depth, skip = ... + local r = {} + local skip_f + if skip then + skip_f = function(n) + if vim.tbl_contains(skip or {}, n) then + return false + end + end + end + for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f }) do + r[name] = type_ + end + return r + ]], dir, depth, skip) + return r + end + + local exp = {} + + exp['a1'] = 'file' + exp['b1'] = 'file' + exp['c1'] = 'file' + exp['a'] = 'directory' + + eq(exp, run('testd', 1)) + + exp['a/a2'] = 'file' + exp['a/b2'] = 'file' + exp['a/c2'] = 'file' + exp['a/b'] = 'directory' + + eq(exp, run('testd', 2)) + + exp['a/b/a3'] = 'file' + exp['a/b/b3'] = 'file' + exp['a/b/c3'] = 'file' + exp['a/b/c'] = 'directory' + + eq(exp, run('testd', 3)) + eq(exp, run('testd', 999, {'a/b/c'})) + + exp['a/b/c/a4'] = 'file' + exp['a/b/c/b4'] = 'file' + exp['a/b/c/c4'] = 'file' + + eq(exp, run('testd', 999)) + end) end) describe('find()', function() @@ -77,6 +230,28 @@ describe('vim.fs', function() local dir, nvim = ... return vim.fs.find(nvim, { path = dir, type = 'file' }) ]], test_build_dir, nvim_prog_basename)) + eq({nvim_dir}, exec_lua([[ + local dir = ... + local parent, name = dir:match('^(.*/)([^/]+)$') + return vim.fs.find(name, { path = parent, upward = true, type = 'directory' }) + ]], nvim_dir)) + 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) @@ -85,7 +260,7 @@ describe('vim.fs', function() eq('C:/Users/jdoe', exec_lua [[ return vim.fs.normalize('C:\\Users\\jdoe') ]]) end) it('works with ~', function() - if iswin() then + if is_os('win') then pending([[$HOME does not exist on Windows ¯\_(ツ)_/¯]]) end eq(os.getenv('HOME') .. '/src/foo', exec_lua [[ return vim.fs.normalize('~/src/foo') ]]) diff --git a/test/functional/lua/help_spec.lua b/test/functional/lua/help_spec.lua new file mode 100644 index 0000000000..b396e2ba30 --- /dev/null +++ b/test/functional/lua/help_spec.lua @@ -0,0 +1,50 @@ +-- 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) + eq({}, rv.invalid_links, 'invalid tags in :help docs') + eq({}, rv.invalid_urls, 'invalid URLs in :help docs') + -- Check that parse errors did not increase wildly. + -- TODO: Fix all parse errors in :help files. + ok(rv.err_count < 250, '<250 parse errors', rv.err_count) + 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) + eq(0, rv.err_count, 'parse errors in :help docs') + eq({}, rv.invalid_links, 'invalid tags in :help docs') + end) +end) diff --git a/test/functional/lua/inspector_spec.lua b/test/functional/lua/inspector_spec.lua new file mode 100644 index 0000000000..5e488bb082 --- /dev/null +++ b/test/functional/lua/inspector_spec.lua @@ -0,0 +1,56 @@ +local helpers = require('test.functional.helpers')(after_each) +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear + +describe('vim.inspect_pos', function() + before_each(function() + clear() + end) + + it('it returns items', function() + local ret = exec_lua([[ + local buf = vim.api.nvim_create_buf(true, false) + vim.api.nvim_set_current_buf(buf) + vim.api.nvim_buf_set_lines(0, 0, -1, false, {"local a = 123"}) + vim.api.nvim_buf_set_option(buf, "filetype", "lua") + vim.cmd("syntax on") + return {buf, vim.inspect_pos(0, 0, 10)} + ]]) + local buf, items = unpack(ret) + eq('', eval('v:errmsg')) + eq({ + buffer = buf, + col = 10, + row = 0, + extmarks = {}, + treesitter = {}, + semantic_tokens = {}, + syntax = { + { + hl_group = 'luaNumber', + hl_group_link = 'Constant', + }, + }, + }, items) + end) +end) + +describe('vim.show_pos', function() + before_each(function() + clear() + end) + + it('it does not error', function() + exec_lua([[ + local buf = vim.api.nvim_create_buf(true, false) + vim.api.nvim_set_current_buf(buf) + vim.api.nvim_buf_set_lines(0, 0, -1, false, {"local a = 123"}) + vim.api.nvim_buf_set_option(buf, "filetype", "lua") + vim.cmd("syntax on") + return {buf, vim.show_pos(0, 0, 10)} + ]]) + eq('', eval('v:errmsg')) + end) +end) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index 32c1615a45..3f107811ae 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -8,12 +8,12 @@ local feed = helpers.feed local clear = helpers.clear local funcs = helpers.funcs local meths = helpers.meths -local iswin = helpers.iswin local command = helpers.command local write_file = helpers.write_file local exec_capture = helpers.exec_capture local exec_lua = helpers.exec_lua local pcall_err = helpers.pcall_err +local is_os = helpers.is_os local screen @@ -135,7 +135,7 @@ describe('print', function() print("very slow") vim.api.nvim_command("sleep 1m") -- force deferred event processing end - ]], (iswin() and "timeout 1") or "sleep 0.1") + ]], (is_os('win') and "timeout 1") or "sleep 0.1") eq('very slow\nvery fast', exec_capture('lua test()')) end) end) diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua index e9c34c9228..b659f2eacb 100644 --- a/test/functional/lua/runtime_spec.lua +++ b/test/functional/lua/runtime_spec.lua @@ -4,6 +4,7 @@ local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval local exec = helpers.exec +local funcs = helpers.funcs local mkdir_p = helpers.mkdir_p local rmdir = helpers.rmdir local write_file = helpers.write_file @@ -29,6 +30,7 @@ describe('runtime:', function() after_each(function() rmdir(plug_dir) + exec('bwipe!') end) describe('colors', function() @@ -39,6 +41,8 @@ describe('runtime:', function() mkdir_p(colorscheme_folder) write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]]) + eq({'new_colorscheme'}, funcs.getcompletion('new_c', 'color')) + exec('colorscheme new_colorscheme') eq(1, eval('g:lua_colorscheme')) @@ -64,23 +68,25 @@ describe('runtime:', function() 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]]) + write_file(compiler_file, [[vim.b.lua_compiler = 1]]) + + eq({'new_compiler'}, funcs.getcompletion('new_c', 'compiler')) exec('compiler new_compiler') - eq(1, eval('g:lua_compiler')) + eq(1, eval('b: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']]) + write_file(compiler_file..'.vim', [[let b:compiler = 'vim']]) + write_file(compiler_file..'.lua', [[vim.b.compiler = 'lua']]) exec('compiler new_compiler') - eq('vim', eval('g:compiler')) + eq('vim', eval('b:compiler')) rmdir(compiler_folder) end) end) @@ -91,10 +97,12 @@ describe('runtime:', function() 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]]) + write_file(ftplugin_file , [[vim.b.lua_ftplugin = 1]]) + + eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype')) exec [[set filetype=new-ft]] - eq(1, eval('g:lua_ftplugin')) + eq(1, eval('b:lua_ftplugin')) rmdir(ftplugin_folder) end) end) @@ -105,10 +113,12 @@ describe('runtime:', function() 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]]) + write_file(indent_file , [[vim.b.lua_indent = 1]]) + + eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype')) exec [[set filetype=new-ft]] - eq(1, eval('g:lua_indent')) + eq(1, eval('b:lua_indent')) rmdir(indent_folder) end) end) @@ -116,24 +126,32 @@ describe('runtime:', function() describe('syntax', function() local syntax_folder = table.concat({plug_dir, 'syntax'}, sep) - it('loads lua syntaxes on filetype change', function() + before_each(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]]) + write_file(syntax_file , [[vim.b.current_syntax = 'my-lang']]) + exec([[let b:current_syntax = '']]) + end) + it('loads lua syntaxes on filetype change', function() exec('set filetype=my-lang') - eq(1, eval('g:lua_syntax')) - rmdir(syntax_folder) + eq('my-lang', eval('b:current_syntax')) 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) + eq('my-lang', eval('b:current_syntax')) + end) + + it('loads lua syntaxes for :ownsyntax', function() + exec('ownsyntax my-lang') + eq('my-lang', eval('w:current_syntax')) + eq('', eval('b:current_syntax')) + end) + + it('lua syntaxes are included in cmdline completion', function() + eq({'my-lang'}, funcs.getcompletion('my-l', 'filetype')) + eq({'my-lang'}, funcs.getcompletion('my-l', 'syntax')) end) end) diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua new file mode 100644 index 0000000000..2647b2be46 --- /dev/null +++ b/test/functional/lua/secure_spec.lua @@ -0,0 +1,284 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +local eq = helpers.eq +local clear = helpers.clear +local command = helpers.command +local pathsep = helpers.get_pathsep() +local is_os = helpers.is_os +local curbufmeths = helpers.curbufmeths +local exec_lua = helpers.exec_lua +local feed_command = helpers.feed_command +local feed = helpers.feed +local funcs = helpers.funcs +local pcall_err = helpers.pcall_err +local matches = helpers.matches + +describe('vim.secure', function() + describe('read()', function() + local xstate = 'Xstate' + + setup(function() + helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) + end) + + teardown(function() + helpers.rmdir(xstate) + end) + + before_each(function() + helpers.write_file('Xfile', [[ + let g:foobar = 42 + ]]) + clear{env={XDG_STATE_HOME=xstate}} + end) + + after_each(function() + os.remove('Xfile') + helpers.rmdir(xstate) + end) + + it('works', function() + local screen = Screen.new(80, 8) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {bold = true, reverse = true}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen}, + [4] = {reverse = true}, + }) + + local cwd = funcs.getcwd() + + -- Need to use feed_command instead of exec_lua because of the confirmation prompt + feed_command([[lua vim.secure.read('Xfile')]]) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {2: }| + :lua vim.secure.read('Xfile') | + {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}| + {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ | + ]]} + feed('d') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('! %s', cwd .. pathsep .. 'Xfile'), vim.trim(trust)) + eq(helpers.NIL, exec_lua([[return vim.secure.read('Xfile')]])) + + os.remove(funcs.stdpath('state') .. pathsep .. 'trust') + + feed_command([[lua vim.secure.read('Xfile')]]) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {2: }| + :lua vim.secure.read('Xfile') | + {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}| + {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ | + ]]} + feed('a') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + local hash = funcs.sha256(helpers.read_file('Xfile')) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('%s %s', hash, cwd .. pathsep .. 'Xfile'), vim.trim(trust)) + eq(helpers.NIL, exec_lua([[vim.secure.read('Xfile')]])) + + os.remove(funcs.stdpath('state') .. pathsep .. 'trust') + + feed_command([[lua vim.secure.read('Xfile')]]) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {2: }| + :lua vim.secure.read('Xfile') | + {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}| + {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ | + ]]} + feed('i') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + -- Trust database is not updated + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(nil, trust) + + feed_command([[lua vim.secure.read('Xfile')]]) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {2: }| + :lua vim.secure.read('Xfile') | + {3:]] .. cwd .. pathsep .. [[Xfile is untrusted}{MATCH:%s+}| + {3:[i]gnore, (v)iew, (d)eny, (a)llow: }^ | + ]]} + feed('v') + screen:expect{grid=[[ + ^let g:foobar = 42 | + {1:~ }| + {1:~ }| + {2:]] .. funcs.fnamemodify(cwd, ':~') .. pathsep .. [[Xfile [RO]{MATCH:%s+}| + | + {1:~ }| + {4:[No Name] }| + | + ]]} + + -- Trust database is not updated + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(nil, trust) + + -- Cannot write file + pcall_err(command, 'write') + eq(true, curbufmeths.get_option('readonly')) + end) + end) + + describe('trust()', function() + local xstate = 'Xstate' + + setup(function() + helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) + end) + + teardown(function() + helpers.rmdir(xstate) + end) + + before_each(function() + helpers.write_file('test_file', 'test') + end) + + after_each(function() + os.remove('test_file') + end) + + it('returns error when passing both path and bufnr', function() + matches('"path" and "bufnr" are mutually exclusive', + pcall_err(exec_lua, [[vim.secure.trust({action='deny', bufnr=0, path='test_file'})]])) + end) + + it('returns error when passing neither path or bufnr', function() + matches('one of "path" or "bufnr" is required', + pcall_err(exec_lua, [[vim.secure.trust({action='deny'})]])) + end) + + it('trust then deny then remove a file using bufnr', function() + local cwd = funcs.getcwd() + local hash = funcs.sha256(helpers.read_file('test_file')) + local full_path = cwd .. pathsep .. 'test_file' + + command('edit test_file') + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) + local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('%s %s', hash, full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('! %s', full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq('', vim.trim(trust)) + end) + + it('deny then trust then remove a file using bufnr', function() + local cwd = funcs.getcwd() + local hash = funcs.sha256(helpers.read_file('test_file')) + local full_path = cwd .. pathsep .. 'test_file' + + command('edit test_file') + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]])) + local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('! %s', full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('%s %s', hash, full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq('', vim.trim(trust)) + end) + + it('trust using bufnr then deny then remove a file using path', function() + local cwd = funcs.getcwd() + local hash = funcs.sha256(helpers.read_file('test_file')) + local full_path = cwd .. pathsep .. 'test_file' + + command('edit test_file') + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) + local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('%s %s', hash, full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('! %s', full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq('', vim.trim(trust)) + end) + + it('deny then trust then remove a file using bufnr', function() + local cwd = funcs.getcwd() + local hash = funcs.sha256(helpers.read_file('test_file')) + local full_path = cwd .. pathsep .. 'test_file' + + command('edit test_file') + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]])) + local trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('! %s', full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq(string.format('%s %s', hash, full_path), vim.trim(trust)) + + eq({true, full_path}, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]])) + trust = helpers.read_file(funcs.stdpath('state') .. pathsep .. 'trust') + eq('', vim.trim(trust)) + end) + + it('trust returns error when buffer not associated to file', function() + command('new') + eq({false, 'buffer is not associated with a file'}, + exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) + end) + end) +end) diff --git a/test/functional/lua/spell_spec.lua b/test/functional/lua/spell_spec.lua index 7e831f16a7..b3de6a0912 100644 --- a/test/functional/lua/spell_spec.lua +++ b/test/functional/lua/spell_spec.lua @@ -15,7 +15,7 @@ describe('vim.spell', function() end it('can handle nil', function() - eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'check' (expected string)]], + eq([[bad argument #1 to 'check' (expected string)]], pcall_err(exec_lua, [[vim.spell.check(nil)]])) end) diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua index e183ce3a57..c7f2783cf3 100644 --- a/test/functional/lua/thread_spec.lua +++ b/test/functional/lua/thread_spec.lua @@ -272,7 +272,7 @@ describe('threadpool', function() work:queue({}) ]]) - eq([[Error executing lua: [string "<nvim>"]:0: Error: thread arg not support type 'function' at 1]], + eq([[Error: thread arg not support type 'function' at 1]], status) 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..6481da900e --- /dev/null +++ b/test/functional/lua/ui_event_spec.lua @@ -0,0 +1,147 @@ +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) + + it('can receive accurate message kinds even if they are history', function() + exec_lua([[ + vim.cmd.echomsg("'message1'") + print('message2') + vim.ui_attach(ns, { ext_messages = true }, on_event) + vim.cmd.echomsg("'message3'") + ]]) + feed(':messages<cr>') + feed('<cr>') + + local actual = exec_lua([[ + return vim.tbl_filter(function (event) + return event[1] == "msg_history_show" + end, events) + ]]) + eq({ + { + 'msg_history_show', + { + { 'echomsg', { { 0, 'message1' } } }, + { '', { { 0, 'message2' } } }, + { 'echomsg', { { 0, 'message3' } } }, + }, + }, + }, actual, inspect(actual)) + end) +end) diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua index 3fcb2dec8d..9ee99b4905 100644 --- a/test/functional/lua/ui_spec.lua +++ b/test/functional/lua/ui_spec.lua @@ -4,6 +4,7 @@ local exec_lua = helpers.exec_lua local clear = helpers.clear local feed = helpers.feed local eval = helpers.eval +local poke_eventloop = helpers.poke_eventloop describe('vim.ui', function() before_each(function() @@ -83,5 +84,50 @@ describe('vim.ui', function() feed('abcdefg<cr>') eq('abcdefg', exec_lua('return result')) end) + + it('can input empty text #18144', function() + feed(':lua vim.ui.input({}, function(input) result = input end)<cr>') + feed('<cr>') + eq('', exec_lua('return result')) + end) + + it('can input empty text with cancelreturn opt #18144', function() + feed(':lua vim.ui.input({ cancelreturn = "CANCEL" }, function(input) result = input end)<cr>') + feed('<cr>') + eq('', exec_lua('return result')) + end) + + it('can return nil when aborted with ESC #18144', function() + feed(':lua result = "on_confirm not called"<cr>') + feed(':lua vim.ui.input({}, function(input) result = input end)<cr>') + feed('Inputted Text<esc>') + -- Note: When `result == nil`, exec_lua('returns result') returns vim.NIL + eq(true, exec_lua('return (nil == result)')) + end) + + it('can return opts.cacelreturn when aborted with ESC with cancelreturn opt #18144', function() + feed(':lua result = "on_confirm not called"<cr>') + feed(':lua vim.ui.input({ cancelreturn = "CANCEL" }, function(input) result = input end)<cr>') + feed('Inputted Text<esc>') + eq('CANCEL', exec_lua('return result')) + end) + + it('can return nil when interrupted with Ctrl-C #18144', function() + feed(':lua result = "on_confirm not called"<cr>') + feed(':lua vim.ui.input({}, function(input) result = input end)<cr>') + poke_eventloop() -- This is needed because Ctrl-C flushes input + feed('Inputted Text<c-c>') + eq(true, exec_lua('return (nil == result)')) + end) + + it('can return the identical object when an arbitrary opts.cancelreturn object is given', function() + feed(':lua fn = function() return 42 end<CR>') + eq(42, exec_lua('return fn()')) + feed(':lua vim.ui.input({ cancelreturn = fn }, function(input) result = input end)<cr>') + feed('cancel<esc>') + eq(true, exec_lua('return (result == fn)')) + eq(42, exec_lua('return result()')) + end) + end) end) diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index 4635f17557..416e9e1f02 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local exec_lua = helpers.exec_lua local eq = helpers.eq +local is_os = helpers.is_os +local skip = helpers.skip local write_file = require('test.helpers').write_file describe('URI methods', function() @@ -11,7 +13,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 +25,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 +34,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 +46,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 +74,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 +85,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 +121,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) @@ -167,7 +169,7 @@ describe('URI methods', function() describe('uri from bufnr', function() it('Windows paths should not be treated as uris', function() - if not helpers.iswin() then return end + skip(not is_os('win'), "Not applicable on non-Windows") local file = helpers.tmpname() write_file(file, 'Test content') diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 2b249b7a69..867f366d06 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local nvim_prog = helpers.nvim_prog local funcs = helpers.funcs local meths = helpers.meths local command = helpers.command @@ -22,8 +23,8 @@ local remove_trace = helpers.remove_trace local mkdir_p = helpers.mkdir_p 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,28 +159,32 @@ 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} - for i,k in pairs(indicies32) do + exec_lua([[_G.test_text = "xy åäö ɧ 汉语 ↥ 🤦x🦄 å بِيَّ\000ъ"]]) + local indices32 = {[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 indices16 = {[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(indices32) do eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ...)", i), i) end - for i,k in pairs(indicies16) do + for i,k in pairs(indices16) do eq(k, exec_lua("return vim.str_byteindex(_G.test_text, ..., true)", i), i) end + eq("index out of range", pcall_err(exec_lua, "return vim.str_byteindex(_G.test_text, ...)", #indices32 + 1)) + eq("index out of range", pcall_err(exec_lua, "return vim.str_byteindex(_G.test_text, ..., true)", #indices16 + 1)) local i32, i16 = 0, 0 - for k = 0,48 do - if indicies32[i32] < k then + local len = 51 + for k = 0,len do + if indices32[i32] < k then i32 = i32 + 1 end - if indicies16[i16] < k then + if indices16[i16] < k then i16 = i16 + 1 - if indicies16[i16+1] == indicies16[i16] then + if indices16[i16+1] == indices16[i16] then i16 = i16 + 1 end end eq({i32, i16}, exec_lua("return {vim.str_utfindex(_G.test_text, ...)}", k), k) end + eq("index out of range", pcall_err(exec_lua, "return vim.str_utfindex(_G.test_text, ...)", len + 1)) end) it("vim.str_utf_start", function() @@ -414,6 +419,12 @@ describe('lua stdlib', function() return getmetatable(t2) == mt ]])) + ok(exec_lua([[ + local t1 = {a = vim.NIL} + local t2 = vim.deepcopy(t1) + return t2.a == vim.NIL + ]])) + matches('Cannot deepcopy object of type thread', pcall_err(exec_lua, [[ local thread = coroutine.create(function () return 0 end) @@ -425,6 +436,8 @@ describe('lua stdlib', function() it('vim.pesc', function() eq('foo%-bar', exec_lua([[return vim.pesc('foo-bar')]])) eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]])) + -- pesc() returns one result. #20751 + eq({'x'}, exec_lua([[return {vim.pesc('x')}]])) -- Validates args. matches('s: expected string, got number', @@ -499,6 +512,8 @@ describe('lua stdlib', function() eq(NIL, exec_lua("return vim.tbl_get({ unindexable = function () end }, 'unindexable', 'missing_key')")) eq(NIL, exec_lua("return vim.tbl_get({}, 'missing_key')")) eq(NIL, exec_lua("return vim.tbl_get({})")) + eq(1, exec_lua("return select('#', vim.tbl_get({}))")) + eq(1, exec_lua("return select('#', vim.tbl_get({ nested = {} }, 'nested', 'missing_key'))")) end) it('vim.tbl_extend', function() @@ -748,6 +763,20 @@ describe('lua stdlib', function() pcall_err(exec_lua, code)) end) + it('vim.spairs', function() + local res = '' + local table = { + ccc=1, + bbb=2, + ddd=3, + aaa=4 + } + for key, _ in vim.spairs(table) do + res = res .. key + end + matches('aaabbbcccddd', res) + end) + it('vim.call, vim.fn', function() eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]])) eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]])) @@ -788,6 +817,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 +930,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() @@ -1006,11 +1040,11 @@ describe('lua stdlib', function() 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.nonexistent") 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} + return {vim.g.nonexistent == vim.NIL, vim.g.nullvar == vim.NIL} ]]) eq({hello="world"}, funcs.luaeval "vim.g.to_delete") @@ -1029,6 +1063,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 +1079,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 +1088,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 +1104,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() @@ -1109,12 +1147,12 @@ describe('lua stdlib', function() eq('bye', funcs.luaeval "vim.b[BUF].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[BUF].nonexistant") + eq(NIL, funcs.luaeval "vim.b.nonexistent") + eq(NIL, funcs.luaeval "vim.b[BUF].nonexistent") 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} + return {vim.b.nonexistent == vim.NIL, vim.b.nullvar == vim.NIL} ]]) matches([[attempt to index .* nil value]], @@ -1133,6 +1171,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 +1187,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 +1196,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 +1212,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() @@ -1189,7 +1231,7 @@ describe('lua stdlib', function() eq(NIL, funcs.luaeval "vim.b.testing") eq(NIL, funcs.luaeval "vim.b.other") - eq(NIL, funcs.luaeval "vim.b.nonexistant") + eq(NIL, funcs.luaeval "vim.b.nonexistent") end) it('vim.w', function() @@ -1208,8 +1250,8 @@ describe('lua stdlib', function() eq('hi', funcs.luaeval "vim.w.testing") eq('bye', funcs.luaeval "vim.w[WIN].testing") eq(123, funcs.luaeval "vim.w.other") - eq(NIL, funcs.luaeval "vim.w.nonexistant") - eq(NIL, funcs.luaeval "vim.w[WIN].nonexistant") + eq(NIL, funcs.luaeval "vim.w.nonexistent") + eq(NIL, funcs.luaeval "vim.w[WIN].nonexistent") matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.w[WIN][0].testing')) @@ -1227,6 +1269,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 +1285,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 +1294,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 +1310,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() @@ -1283,7 +1329,7 @@ describe('lua stdlib', function() eq(NIL, funcs.luaeval "vim.w.testing") eq(NIL, funcs.luaeval "vim.w.other") - eq(NIL, funcs.luaeval "vim.w.nonexistant") + eq(NIL, funcs.luaeval "vim.w.nonexistent") end) it('vim.t', function() @@ -1295,10 +1341,10 @@ describe('lua stdlib', function() eq('hi', funcs.luaeval "vim.t.testing") eq(123, funcs.luaeval "vim.t.other") - eq(NIL, funcs.luaeval "vim.t.nonexistant") + eq(NIL, funcs.luaeval "vim.t.nonexistent") eq('hi', funcs.luaeval "vim.t[0].testing") eq(123, funcs.luaeval "vim.t[0].other") - eq(NIL, funcs.luaeval "vim.t[0].nonexistant") + eq(NIL, funcs.luaeval "vim.t[0].nonexistent") matches([[attempt to index .* nil value]], pcall_err(exec_lua, 'return vim.t[0][0].testing')) @@ -1316,6 +1362,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 +1378,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 +1387,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 +1403,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" @@ -1361,15 +1411,27 @@ describe('lua stdlib', function() eq(NIL, funcs.luaeval "vim.t.testing") eq(NIL, funcs.luaeval "vim.t.other") - eq(NIL, funcs.luaeval "vim.t.nonexistant") + eq(NIL, funcs.luaeval "vim.t.nonexistent") 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 +1458,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 +1479,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')) @@ -2141,6 +2203,22 @@ describe('lua stdlib', function() end) end) -- vim.opt + describe('opt_local', function() + it('should be able to append to an array list type option', function() + eq({ "foo,bar,baz,qux" }, exec_lua [[ + local result = {} + + vim.opt.tags = "foo,bar" + vim.opt_local.tags:append("baz") + vim.opt_local.tags:append("qux") + + table.insert(result, vim.bo.tags) + + return result + ]]) + end) + end) + it('vim.cmd', function() exec_lua [[ vim.cmd "autocmd BufNew * ++once lua BUF = vim.fn.expand('<abuf>')" @@ -2173,6 +2251,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() @@ -2185,13 +2267,19 @@ describe('lua stdlib', function() eq(true, exec_lua[[return vim.g.test]]) end) - it('vim.region', function() - insert(helpers.dedent( [[ - text tααt tααt text - text tαxt txtα tex - text tαxt tαxt - ]])) - eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]]) + describe('vim.region', function() + it('charwise', function() + insert(helpers.dedent( [[ + text tααt tααt text + text tαxt txtα tex + text tαxt tαxt + ]])) + eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]]) + end) + it('blockwise', function() + insert([[αα]]) + eq({0,5}, exec_lua[[ return vim.region(0,{0,0},{0,4},'3',true)[0] ]]) + end) end) describe('vim.on_key', function() @@ -2623,6 +2711,46 @@ describe('lua stdlib', function() a.nvim_buf_call(a.nvim_create_buf(false, true), function() vim.cmd "redraw" end) ]] end) + + it('can be nested crazily with hidden buffers', function() + eq(true, exec_lua([[ + local function scratch_buf_call(fn) + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_option(buf, 'cindent', true) + return vim.api.nvim_buf_call(buf, function() + return vim.api.nvim_get_current_buf() == buf + and vim.api.nvim_buf_get_option(buf, 'cindent') + and fn() + end) and vim.api.nvim_buf_delete(buf, {}) == nil + end + + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return scratch_buf_call(function() + return true + end) + end) + end) + end) + end) + end) + end) + end) + end) + end) + end) + end) + ]])) + end) end) describe('vim.api.nvim_win_call', function() @@ -2721,6 +2849,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() @@ -2745,9 +2924,14 @@ describe('lua: builtin modules', function() end) - it('does not work when disabled without runtime', function() - clear{args={'--luamod-dev'}, env={VIMRUNTIME='fixtures/a'}} - expect_exit(exec_lua, [[return vim.tbl_count {x=1,y=2}]]) + it('fails when disabled without runtime', function() + clear() + command("let $VIMRUNTIME='fixtures/a'") + -- Use system([nvim,…]) instead of clear() to avoid stderr noise. #21844 + local out = funcs.system({nvim_prog, '--clean', '--luamod-dev', + [[+call nvim_exec_lua('return vim.tbl_count {x=1,y=2}')]], '+qa!'}):gsub('\r\n', '\n') + eq(1, eval('v:shell_error')) + matches("'vim%.shared' not found", out) end) end) diff --git a/test/functional/lua/xdiff_spec.lua b/test/functional/lua/xdiff_spec.lua index d55268fc78..3121ac051f 100644 --- a/test/functional/lua/xdiff_spec.lua +++ b/test/functional/lua/xdiff_spec.lua @@ -74,11 +74,13 @@ describe('xdiff bindings', function() end) it('with error callback', function() - exec_lua([[on_hunk = function(sa, ca, sb, cb) + exec_lua[[ + on_hunk = function(sa, ca, sb, cb) error('ERROR1') - end]]) + end + ]] - eq([[Error executing lua: [string "<nvim>"]:0: error running function on_hunk: [string "<nvim>"]:0: ERROR1]], + eq([[error running function on_hunk: [string "<nvim>"]:0: ERROR1]], pcall_err(exec_lua, [[vim.diff(a1, b1, {on_hunk = on_hunk})]])) end) @@ -135,19 +137,19 @@ describe('xdiff bindings', function() end) it('can handle bad args', function() - eq([[Error executing lua: [string "<nvim>"]:0: Expected at least 2 arguments]], + eq([[Expected at least 2 arguments]], pcall_err(exec_lua, [[vim.diff('a')]])) - eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'diff' (expected string)]], + eq([[bad argument #1 to 'diff' (expected string)]], pcall_err(exec_lua, [[vim.diff(1, 2)]])) - eq([[Error executing lua: [string "<nvim>"]:0: bad argument #3 to 'diff' (expected table)]], + eq([[bad argument #3 to 'diff' (expected table)]], pcall_err(exec_lua, [[vim.diff('a', 'b', true)]])) - eq([[Error executing lua: [string "<nvim>"]:0: unexpected key: bad_key]], + eq([[unexpected key: bad_key]], pcall_err(exec_lua, [[vim.diff('a', 'b', { bad_key = true })]])) - eq([[Error executing lua: [string "<nvim>"]:0: on_hunk is not a function]], + eq([[on_hunk is not a function]], pcall_err(exec_lua, [[vim.diff('a', 'b', { on_hunk = true })]])) end) |