diff options
Diffstat (limited to 'test/functional/api/vim_spec.lua')
| -rw-r--r-- | test/functional/api/vim_spec.lua | 876 |
1 files changed, 678 insertions, 198 deletions
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 8fcdd9620b..8bbadda9b0 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,6 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local lfs = require('lfs') local luv = require('luv') local fmt = string.format @@ -9,6 +8,7 @@ local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local command = helpers.command local exec = helpers.exec +local exec_capture = helpers.exec_capture local eval = helpers.eval local expect = helpers.expect local funcs = helpers.funcs @@ -28,7 +28,6 @@ local write_file = helpers.write_file local exec_lua = helpers.exec_lua local exc_exec = helpers.exc_exec local insert = helpers.insert -local expect_exit = helpers.expect_exit local skip = helpers.skip local pcall_err = helpers.pcall_err @@ -59,7 +58,7 @@ describe('API', function() -- XXX: This must be the last one, else next one will fail: -- "Packer instance already working. Use another Packer ..." - matches("can't serialize object$", + matches("can't serialize object of type .$", pcall_err(request, nil)) end) @@ -89,132 +88,145 @@ describe('API', function() eq({mode='i', blocking=false}, nvim("get_mode")) end) - describe('nvim_exec', function() + describe('nvim_exec2', function() + it('always returns table', function() + -- In built version this results into `vim.empty_dict()` + eq({}, nvim('exec2', 'echo "Hello"', {})) + eq({}, nvim('exec2', 'echo "Hello"', { output = false })) + eq({ output = 'Hello' }, nvim('exec2', 'echo "Hello"', { output = true })) + end) + + it('default options', function() + -- Should be equivalent to { output = false } + nvim('exec2', "let x0 = 'a'", {}) + eq('a', nvim('get_var', 'x0')) + end) + it('one-line input', function() - nvim('exec', "let x1 = 'a'", false) + nvim('exec2', "let x1 = 'a'", { output = false }) eq('a', nvim('get_var', 'x1')) end) it(':verbose set {option}?', function() - nvim('exec', 'set nowrap', false) - eq('nowrap\n\tLast set from anonymous :source', - nvim('exec', 'verbose set wrap?', true)) + nvim('exec2', 'set nowrap', { output = false }) + eq({ output = 'nowrap\n\tLast set from anonymous :source' }, + nvim('exec2', 'verbose set wrap?', { output = true })) -- Using script var to force creation of a script item - nvim('exec', [[ + nvim('exec2', [[ let s:a = 1 set nowrap - ]], false) - eq('nowrap\n\tLast set from anonymous :source (script id 1)', - nvim('exec', 'verbose set wrap?', true)) + ]], { output = false }) + eq({ output = 'nowrap\n\tLast set from anonymous :source (script id 1)' }, + nvim('exec2', 'verbose set wrap?', { output = true })) end) it('multiline input', function() -- Heredoc + empty lines. - nvim('exec', "let x2 = 'a'\n", false) + nvim('exec2', "let x2 = 'a'\n", { output = false }) eq('a', nvim('get_var', 'x2')) - nvim('exec','lua <<EOF\n\n\n\ny=3\n\n\nEOF', false) + nvim('exec2','lua <<EOF\n\n\n\ny=3\n\n\nEOF', { output = false }) eq(3, nvim('eval', "luaeval('y')")) - eq('', nvim('exec', 'lua <<EOF\ny=3\nEOF', false)) + eq({}, nvim('exec2', 'lua <<EOF\ny=3\nEOF', { output = false })) eq(3, nvim('eval', "luaeval('y')")) -- Multiple statements - nvim('exec', 'let x1=1\nlet x2=2\nlet x3=3\n', false) + nvim('exec2', 'let x1=1\nlet x2=2\nlet x3=3\n', { output = false }) eq(1, nvim('eval', 'x1')) eq(2, nvim('eval', 'x2')) eq(3, nvim('eval', 'x3')) -- Functions - nvim('exec', 'function Foo()\ncall setline(1,["xxx"])\nendfunction', false) + nvim('exec2', 'function Foo()\ncall setline(1,["xxx"])\nendfunction', { output = false }) eq('', nvim('get_current_line')) - nvim('exec', 'call Foo()', false) + nvim('exec2', 'call Foo()', { output = false }) eq('xxx', nvim('get_current_line')) -- Autocmds - nvim('exec','autocmd BufAdd * :let x1 = "Hello"', false) + nvim('exec2','autocmd BufAdd * :let x1 = "Hello"', { output = false }) nvim('command', 'new foo') eq('Hello', request('nvim_eval', 'g:x1')) -- Line continuations - nvim('exec', [[ + nvim('exec2', [[ let abc = #{ \ a: 1, "\ b: 2, \ c: 3 - \ }]], false) + \ }]], { output = false }) eq({a = 1, c = 3}, request('nvim_eval', 'g:abc')) -- try no spaces before continuations to catch off-by-one error - nvim('exec', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', false) + nvim('exec2', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', { output = false }) eq({a = 98}, request('nvim_eval', 'g:ab')) -- Script scope (s:) - eq('ahoy! script-scoped varrrrr', nvim('exec', [[ + eq({ output = 'ahoy! script-scoped varrrrr' }, nvim('exec2', [[ let s:pirate = 'script-scoped varrrrr' function! s:avast_ye_hades(s) abort return a:s .. ' ' .. s:pirate endfunction echo <sid>avast_ye_hades('ahoy!') - ]], true)) + ]], { output = true })) - eq('ahoy! script-scoped varrrrr', nvim('exec', [[ + eq({ output = "{'output': 'ahoy! script-scoped varrrrr'}" }, nvim('exec2', [[ let s:pirate = 'script-scoped varrrrr' function! Avast_ye_hades(s) abort return a:s .. ' ' .. s:pirate endfunction - echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1) - ]], true)) + echo nvim_exec2('echo Avast_ye_hades(''ahoy!'')', {'output': v:true}) + ]], { output = true })) matches('Vim%(echo%):E121: Undefined variable: s:pirate$', - pcall_err(request, 'nvim_exec', [[ + pcall_err(request, 'nvim_exec2', [[ let s:pirate = 'script-scoped varrrrr' - call nvim_exec('echo s:pirate', 1) - ]], false)) + call nvim_exec2('echo s:pirate', {'output': v:true}) + ]], { output = false })) -- Script items are created only on script var access - eq('1\n0', nvim('exec', [[ + eq({ output = '1\n0' }, nvim('exec2', [[ echo expand("<SID>")->empty() let s:a = 123 echo expand("<SID>")->empty() - ]], true)) + ]], { output = true })) - eq('1\n0', nvim('exec', [[ + eq({ output = '1\n0' }, nvim('exec2', [[ echo expand("<SID>")->empty() function s:a() abort endfunction echo expand("<SID>")->empty() - ]], true)) + ]], { output = true })) end) it('non-ASCII input', function() - nvim('exec', [=[ + nvim('exec2', [=[ new exe "normal! i ax \n Ax " :%s/ax/--a1234--/g | :%s/Ax/--A1234--/g - ]=], false) + ]=], { output = false }) nvim('command', '1') eq(' --a1234-- ', nvim('get_current_line')) nvim('command', '2') eq(' --A1234-- ', nvim('get_current_line')) - nvim('exec', [[ + nvim('exec2', [[ new call setline(1,['xxx']) call feedkeys('r') call feedkeys('ñ', 'xt') - ]], false) + ]], { output = false }) eq('ñxx', nvim('get_current_line')) end) it('execution error', function() - eq('nvim_exec(): Vim:E492: Not an editor command: bogus_command', - pcall_err(request, 'nvim_exec', 'bogus_command', false)) + eq('nvim_exec2(): Vim:E492: Not an editor command: bogus_command', + pcall_err(request, 'nvim_exec2', 'bogus_command', {})) eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:exception')) - eq('nvim_exec(): Vim(buffer):E86: Buffer 23487 does not exist', - pcall_err(request, 'nvim_exec', 'buffer 23487', false)) + eq('nvim_exec2(): Vim(buffer):E86: Buffer 23487 does not exist', + pcall_err(request, 'nvim_exec2', 'buffer 23487', {})) eq('', eval('v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:exception')) end) @@ -222,17 +234,17 @@ describe('API', function() it('recursion', function() local fname = tmpname() write_file(fname, 'let x1 = "set from :source file"\n') - -- nvim_exec + -- nvim_exec2 -- :source - -- nvim_exec - request('nvim_exec', [[ + -- nvim_exec2 + request('nvim_exec2', [[ let x2 = substitute('foo','o','X','g') let x4 = 'should be overwritten' - call nvim_exec("source ]]..fname..[[\nlet x3 = substitute('foo','foo','set by recursive nvim_exec','g')\nlet x5='overwritten'\nlet x4=x5\n", v:false) - ]], false) + call nvim_exec2("source ]]..fname..[[\nlet x3 = substitute('foo','foo','set by recursive nvim_exec2','g')\nlet x5='overwritten'\nlet x4=x5\n", {'output': v:false}) + ]], { output = false }) eq('set from :source file', request('nvim_get_var', 'x1')) eq('fXX', request('nvim_get_var', 'x2')) - eq('set by recursive nvim_exec', request('nvim_get_var', 'x3')) + eq('set by recursive nvim_exec2', request('nvim_get_var', 'x3')) eq('overwritten', request('nvim_get_var', 'x4')) eq('overwritten', request('nvim_get_var', 'x5')) os.remove(fname) @@ -242,35 +254,35 @@ describe('API', function() local fname = tmpname() write_file(fname, 'echo "hello"\n') local sourcing_fname = tmpname() - write_file(sourcing_fname, 'call nvim_exec("source '..fname..'", v:false)\n') - meths.exec('set verbose=2', false) + write_file(sourcing_fname, 'call nvim_exec2("source '..fname..'", {"output": v:false})\n') + meths.exec2('set verbose=2', { output = false }) local traceback_output = 'line 0: sourcing "'..sourcing_fname..'"\n'.. 'line 0: sourcing "'..fname..'"\n'.. 'hello\n'.. 'finished sourcing '..fname..'\n'.. - 'continuing in nvim_exec() called at '..sourcing_fname..':1\n'.. + 'continuing in nvim_exec2() called at '..sourcing_fname..':1\n'.. 'finished sourcing '..sourcing_fname..'\n'.. - 'continuing in nvim_exec() called at nvim_exec():0' - eq(traceback_output, - meths.exec('call nvim_exec("source '..sourcing_fname..'", v:false)', true)) + 'continuing in nvim_exec2() called at nvim_exec2():0' + eq({ output = traceback_output }, + meths.exec2('call nvim_exec2("source '..sourcing_fname..'", {"output": v:false})', { output = true })) os.remove(fname) os.remove(sourcing_fname) end) it('returns output', function() - eq('this is spinal tap', - nvim('exec', 'lua <<EOF\n\n\nprint("this is spinal tap")\n\n\nEOF', true)) - eq('', nvim('exec', 'echo', true)) - eq('foo 42', nvim('exec', 'echo "foo" 42', true)) + eq({ output = 'this is spinal tap' }, + nvim('exec2', 'lua <<EOF\n\n\nprint("this is spinal tap")\n\n\nEOF', { output = true })) + eq({ output = '' }, nvim('exec2', 'echo', { output = true })) + eq({ output = 'foo 42' }, nvim('exec2', 'echo "foo" 42', { output = true })) end) - it('displays messages when output=false', function() + it('displays messages when opts.output=false', function() local screen = Screen.new(40, 8) screen:attach() screen:set_default_attr_ids({ [0] = {bold=true, foreground=Screen.colors.Blue}, }) - meths.exec("echo 'hello'", false) + meths.exec2("echo 'hello'", { output = false }) screen:expect{grid=[[ ^ | {0:~ }| @@ -289,7 +301,7 @@ describe('API', function() screen:set_default_attr_ids({ [0] = {bold=true, foreground=Screen.colors.Blue}, }) - meths.exec("echo 'hello'", true) + meths.exec2("echo 'hello'", { output = true }) screen:expect{grid=[[ ^ | {0:~ }| @@ -300,7 +312,7 @@ describe('API', function() ]]} exec([[ func Print() - call nvim_exec('echo "hello"', v:true) + call nvim_exec2('echo "hello"', { 'output': v:true }) endfunc ]]) feed([[:echon 1 | call Print() | echon 5<CR>]]) @@ -322,8 +334,7 @@ describe('API', function() nvim('command', 'edit '..fname) nvim('command', 'normal itesting\napi') nvim('command', 'w') - local f = io.open(fname) - ok(f ~= nil) + local f = assert(io.open(fname)) if is_os('win') then eq('testing\r\napi\r\n', f:read('*a')) else @@ -333,21 +344,27 @@ describe('API', function() os.remove(fname) end) - it('VimL validation error: fails with specific error', function() + it('Vimscript validation error: fails with specific error', function() local status, rv = pcall(nvim, "command", "bogus_command") eq(false, status) -- nvim_command() failed. - eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned. + eq("E492:", string.match(rv, "E%d*:")) -- Vimscript error was returned. eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:exception')) end) - it('VimL execution error: fails with specific error', function() + it('Vimscript execution error: fails with specific error', function() local status, rv = pcall(nvim, "command", "buffer 23487") eq(false, status) -- nvim_command() failed. eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*")) eq('', eval('v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:exception')) end) + + it('gives E493 instead of prompting on backwards range', function() + command('split') + eq('Vim(windo):E493: Backwards range given: 2,1windo echo', + pcall_err(command, '2,1windo echo')) + end) end) describe('nvim_command_output', function() @@ -403,7 +420,7 @@ describe('API', function() eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]])) end) - it('VimL validation error: fails with specific error', function() + it('Vimscript validation error: fails with specific error', function() local status, rv = pcall(nvim, "command_output", "bogus commannnd") eq(false, status) -- nvim_command_output() failed. eq("E492: Not an editor command: bogus commannnd", @@ -413,7 +430,7 @@ describe('API', function() eq({mode='n', blocking=false}, nvim("get_mode")) end) - it('VimL execution error: fails with specific error', function() + it('Vimscript execution error: fails with specific error', function() local status, rv = pcall(nvim, "command_output", "buffer 42") eq(false, status) -- nvim_command_output() failed. eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*")) @@ -422,7 +439,7 @@ describe('API', function() eq({mode='n', blocking=false}, nvim("get_mode")) end) - it('Does not cause heap buffer overflow with large output', function() + it('does not cause heap buffer overflow with large output', function() eq(eval('string(range(1000000))'), nvim('command_output', 'echo range(1000000)')) end) @@ -444,7 +461,7 @@ describe('API', function() eq(2, request("vim_eval", "1+1")) end) - it("VimL error: returns error details, does NOT update v:errmsg", function() + it("Vimscript error: returns error details, does NOT update v:errmsg", function() eq('Vim:E121: Undefined variable: bogus', pcall_err(request, 'nvim_eval', 'bogus expression')) eq('', eval('v:errmsg')) -- v:errmsg was not updated. @@ -459,7 +476,7 @@ describe('API', function() eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'})) end) - it("VimL validation error: returns specific error, does NOT update v:errmsg", function() + it("Vimscript validation error: returns specific error, does NOT update v:errmsg", function() eq('Vim:E117: Unknown function: bogus function', pcall_err(request, 'nvim_call_function', 'bogus function', {'arg1'})) eq('Vim:E119: Not enough arguments for function: atan', @@ -468,7 +485,7 @@ describe('API', function() eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) - it("VimL error: returns error details, does NOT update v:errmsg", function() + it("Vimscript error: returns error details, does NOT update v:errmsg", function() eq('Vim:E808: Number or Float required', pcall_err(request, 'nvim_call_function', 'atan', {'foo'})) eq('Vim:Invalid channel stream "xxx"', @@ -479,7 +496,7 @@ describe('API', function() eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) - it("VimL exception: returns exception details, does NOT update v:errmsg", function() + it("Vimscript exception: returns exception details, does NOT update v:errmsg", function() source([[ function! Foo() abort throw 'wtf' @@ -490,7 +507,7 @@ describe('API', function() eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) - it('validates args', function() + it('validation', function() local too_many_args = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' } source([[ function! Foo(...) abort @@ -504,7 +521,7 @@ describe('API', function() end) describe('nvim_call_dict_function', function() - it('invokes VimL dict function', function() + it('invokes Vimscript dict function', function() source([[ function! F(name) dict return self.greeting.', '.a:name.'!' @@ -532,7 +549,7 @@ describe('API', function() eq('@it works@', nvim('call_dict_function', { result = 'it works', G = 'G'}, 'G', {})) end) - it('validates args', function() + it('validation', function() command('let g:d={"baz":"zub","meep":[]}') eq('Not found: bogus', pcall_err(request, 'nvim_call_dict_function', 'g:d', 'bogus', {1,2})) @@ -559,7 +576,6 @@ describe('API', function() local start_dir before_each(function() - clear() funcs.mkdir("Xtestdir") start_dir = funcs.getcwd() end) @@ -575,7 +591,7 @@ describe('API', function() it('sets previous directory', function() meths.set_current_dir("Xtestdir") - meths.exec('cd -', false) + command('cd -') eq(funcs.getcwd(), start_dir) end) end) @@ -634,7 +650,7 @@ describe('API', function() end) describe('nvim_input', function() - it("VimL error: does NOT fail, updates v:errmsg", function() + it("Vimscript error: does NOT fail, updates v:errmsg", function() local status, _ = pcall(nvim, "input", ":call bogus_fn()<CR>") local v_errnum = string.match(nvim("eval", "v:errmsg"), "E%d*:") eq(true, status) -- nvim_input() did not fail. @@ -648,10 +664,10 @@ describe('API', function() end) describe('nvim_paste', function() - it('validates args', function() - eq('Invalid phase: -2', + it('validation', function() + eq("Invalid 'phase': -2", pcall_err(request, 'nvim_paste', 'foo', true, -2)) - eq('Invalid phase: 4', + eq("Invalid 'phase': 4", pcall_err(request, 'nvim_paste', 'foo', true, 4)) end) local function run_streamed_paste_tests() @@ -1032,7 +1048,7 @@ describe('API', function() line 3 ]]) eq({0,4,1,0}, funcs.getpos('.')) -- Cursor follows the paste. - eq(false, nvim('get_option', 'paste')) + eq(false, nvim('get_option_value', 'paste', {})) command('%delete _') -- Without final "\n". nvim('paste', 'line 1\nline 2\nline 3', true, -1) @@ -1072,7 +1088,7 @@ describe('API', function() nvim('paste', 'line 1\r\n\r\rline 2\nline 3\rline 4\r', true, -1) expect('line 1\n\n\nline 2\nline 3\nline 4\n') eq({0,7,1,0}, funcs.getpos('.')) - eq(false, nvim('get_option', 'paste')) + eq(false, nvim('get_option_value', 'paste', {})) end) it('Replace-mode', function() -- Within single line @@ -1154,10 +1170,10 @@ describe('API', function() end) describe('nvim_put', function() - it('validates args', function() - eq('Invalid lines (expected array of strings)', + it('validation', function() + eq("Invalid 'line': expected String, got Integer", pcall_err(request, 'nvim_put', {42}, 'l', false, false)) - eq("Invalid type: 'x'", + eq("Invalid 'type': 'x'", pcall_err(request, 'nvim_put', {'foo'}, 'x', false, false)) end) it("fails if 'nomodifiable'", function() @@ -1259,9 +1275,9 @@ describe('API', function() yyybc line 2 line 3 ]]) - eq("Invalid type: 'bx'", + eq("Invalid 'type': 'bx'", pcall_err(meths.put, {'xxx', 'yyy'}, 'bx', false, true)) - eq("Invalid type: 'b3x'", + eq("Invalid 'type': 'b3x'", pcall_err(meths.put, {'xxx', 'yyy'}, 'b3x', false, true)) end) end) @@ -1288,6 +1304,11 @@ describe('API', function() end) describe('set/get/del variables', function() + it('validation', function() + eq('Key not found: bogus', pcall_err(meths.get_var, 'bogus')) + eq('Key not found: bogus', pcall_err(meths.del_var, 'bogus')) + end) + it('nvim_get_var, nvim_set_var, nvim_del_var', function() nvim('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua')) @@ -1329,12 +1350,59 @@ describe('API', function() end) it('nvim_get_vvar, nvim_set_vvar', function() - -- Set readonly v: var. - eq('Key is read-only: count', - pcall_err(request, 'nvim_set_vvar', 'count', 42)) - -- Set writable v: var. + eq('Key is read-only: count', pcall_err(request, 'nvim_set_vvar', 'count', 42)) + eq('Dictionary is locked', pcall_err(request, 'nvim_set_vvar', 'nosuchvar', 42)) meths.set_vvar('errmsg', 'set by API') eq('set by API', meths.get_vvar('errmsg')) + meths.set_vvar('errmsg', 42) + eq('42', eval('v:errmsg')) + meths.set_vvar('oldfiles', { 'one', 'two' }) + eq({ 'one', 'two' }, eval('v:oldfiles')) + meths.set_vvar('oldfiles', {}) + eq({}, eval('v:oldfiles')) + eq('Setting v:oldfiles to value with wrong type', pcall_err(meths.set_vvar, 'oldfiles', 'a')) + eq({}, eval('v:oldfiles')) + + feed('i foo foo foo<Esc>0/foo<CR>') + eq({1, 1}, meths.win_get_cursor(0)) + eq(1, eval('v:searchforward')) + feed('n') + eq({1, 5}, meths.win_get_cursor(0)) + meths.set_vvar('searchforward', 0) + eq(0, eval('v:searchforward')) + feed('n') + eq({1, 1}, meths.win_get_cursor(0)) + meths.set_vvar('searchforward', 1) + eq(1, eval('v:searchforward')) + feed('n') + eq({1, 5}, meths.win_get_cursor(0)) + + local screen = Screen.new(60, 3) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, + [1] = {background = Screen.colors.Yellow}, + }) + screen:attach() + eq(1, eval('v:hlsearch')) + screen:expect{grid=[[ + {1:foo} {1:^foo} {1:foo} | + {0:~ }| + | + ]]} + meths.set_vvar('hlsearch', 0) + eq(0, eval('v:hlsearch')) + screen:expect{grid=[[ + foo ^foo foo | + {0:~ }| + | + ]]} + meths.set_vvar('hlsearch', 1) + eq(1, eval('v:hlsearch')) + screen:expect{grid=[[ + {1:foo} {1:^foo} {1:foo} | + {0:~ }| + | + ]]} end) it('vim_set_var returns the old value', function() @@ -1358,56 +1426,71 @@ describe('API', function() end) end) - describe('nvim_get_option, nvim_set_option', function() + describe('nvim_get_option_value, nvim_set_option_value', function() it('works', function() - ok(nvim('get_option', 'equalalways')) - nvim('set_option', 'equalalways', false) - ok(not nvim('get_option', 'equalalways')) + ok(nvim('get_option_value', 'equalalways', {})) + nvim('set_option_value', 'equalalways', false, {}) + ok(not nvim('get_option_value', 'equalalways', {})) end) it('works to get global value of local options', function() - eq(false, nvim('get_option', 'lisp')) - eq(8, nvim('get_option', 'shiftwidth')) + eq(false, nvim('get_option_value', 'lisp', {})) + eq(8, nvim('get_option_value', 'shiftwidth', {})) end) it('works to set global value of local options', function() - nvim('set_option', 'lisp', true) - eq(true, nvim('get_option', 'lisp')) - eq(false, helpers.curbuf('get_option', 'lisp')) + nvim('set_option_value', 'lisp', true, {scope='global'}) + eq(true, nvim('get_option_value', 'lisp', {scope='global'})) + eq(false, nvim('get_option_value', 'lisp', {})) eq(nil, nvim('command_output', 'setglobal lisp?'):match('nolisp')) eq('nolisp', nvim('command_output', 'setlocal lisp?'):match('nolisp')) - nvim('set_option', 'shiftwidth', 20) + nvim('set_option_value', 'shiftwidth', 20, {scope='global'}) eq('20', nvim('command_output', 'setglobal shiftwidth?'):match('%d+')) eq('8', nvim('command_output', 'setlocal shiftwidth?'):match('%d+')) end) - it('most window-local options have no global value', function() - local status, err = pcall(nvim, 'get_option', 'foldcolumn') - eq(false, status) - ok(err:match('Invalid option name') ~= nil) - end) - it('updates where the option was last set from', function() - nvim('set_option', 'equalalways', false) + nvim('set_option_value', 'equalalways', false, {}) local status, rv = pcall(nvim, 'command_output', 'verbose set equalalways?') eq(true, status) ok(nil ~= string.find(rv, 'noequalalways\n'.. '\tLast set from API client %(channel id %d+%)')) - nvim('exec_lua', 'vim.api.nvim_set_option("equalalways", true)', {}) + nvim('exec_lua', 'vim.api.nvim_set_option_value("equalalways", true, {})', {}) status, rv = pcall(nvim, 'command_output', 'verbose set equalalways?') eq(true, status) eq(' equalalways\n\tLast set from Lua', rv) end) - end) - describe('nvim_get_option_value, nvim_set_option_value', function() - it('works', function() - ok(nvim('get_option_value', 'equalalways', {})) - nvim('set_option_value', 'equalalways', false, {}) - ok(not nvim('get_option_value', 'equalalways', {})) + it('updates whether the option has ever been set #25025', function() + eq(false, nvim('get_option_info2', 'autochdir', {}).was_set) + nvim('set_option_value', 'autochdir', true, {}) + eq(true, nvim('get_option_info2', 'autochdir', {}).was_set) + + eq(false, nvim('get_option_info2', 'cmdwinheight', {}).was_set) + nvim('set_option_value', 'cmdwinheight', 10, {}) + eq(true, nvim('get_option_info2', 'cmdwinheight', {}).was_set) + + eq(false, nvim('get_option_info2', 'debug', {}).was_set) + nvim('set_option_value', 'debug', 'beep', {}) + eq(true, nvim('get_option_info2', 'debug', {}).was_set) + end) + + it('validation', function() + eq("Invalid 'scope': expected 'local' or 'global'", + pcall_err(nvim, 'get_option_value', 'scrolloff', {scope = 'bogus'})) + eq("Invalid 'scope': expected 'local' or 'global'", + pcall_err(nvim, 'set_option_value', 'scrolloff', 1, {scope = 'bogus'})) + eq("Invalid 'scope': expected String, got Integer", + pcall_err(nvim, 'get_option_value', 'scrolloff', {scope = 42})) + eq("Invalid 'value': expected valid option type, got Array", + pcall_err(nvim, 'set_option_value', 'scrolloff', {}, {})) + eq("Invalid value for option 'scrolloff': expected Number, got Boolean true", + pcall_err(nvim, 'set_option_value', 'scrolloff', true, {})) + eq("Invalid value for option 'scrolloff': expected Number, got String \"wrong\"", + pcall_err(nvim, 'set_option_value', 'scrolloff', 'wrong', {})) end) it('can get local values when global value is set', function() @@ -1453,19 +1536,22 @@ describe('API', function() -- Now try with options with a special "local is unset" value (e.g. 'undolevels') nvim('set_option_value', 'undolevels', 1000, {}) - eq(1000, nvim('get_option_value', 'undolevels', {scope = 'local'})) + nvim('set_option_value', 'undolevels', 1200, {scope = 'local'}) + eq(1200, nvim('get_option_value', 'undolevels', {scope = 'local'})) nvim('set_option_value', 'undolevels', NIL, {scope = 'local'}) eq(-123456, nvim('get_option_value', 'undolevels', {scope = 'local'})) + eq(1000, nvim('get_option_value', 'undolevels', {})) nvim('set_option_value', 'autoread', true, {}) - eq(true, nvim('get_option_value', 'autoread', {scope = 'local'})) + nvim('set_option_value', 'autoread', false, {scope = 'local'}) + eq(false, nvim('get_option_value', 'autoread', {scope = 'local'})) nvim('set_option_value', 'autoread', NIL, {scope = 'local'}) eq(NIL, nvim('get_option_value', 'autoread', {scope = 'local'})) + eq(true, nvim('get_option_value', 'autoread', {})) end) it('set window options', function() - -- Same as to nvim_win_set_option - nvim('set_option_value', 'colorcolumn', '4,3', {win=0}) + nvim('set_option_value', 'colorcolumn', '4,3', {}) eq('4,3', nvim('get_option_value', 'colorcolumn', {scope = 'local'})) command("set modified hidden") command("enew") -- edit new buffer, window option is preserved @@ -1473,7 +1559,6 @@ describe('API', function() end) it('set local window options', function() - -- Different to nvim_win_set_option nvim('set_option_value', 'colorcolumn', '4,3', {win=0, scope='local'}) eq('4,3', nvim('get_option_value', 'colorcolumn', {win = 0, scope = 'local'})) command("set modified hidden") @@ -1484,11 +1569,11 @@ describe('API', function() it('get buffer or window-local options', function() nvim('command', 'new') local buf = nvim('get_current_buf').id - nvim('buf_set_option', buf, 'tagfunc', 'foobar') + nvim('set_option_value', 'tagfunc', 'foobar', {buf=buf}) eq('foobar', nvim('get_option_value', 'tagfunc', {buf = buf})) local win = nvim('get_current_win').id - nvim('win_set_option', win, 'number', true) + nvim('set_option_value', 'number', true, {win=win}) eq(true, nvim('get_option_value', 'number', {win = win})) end) @@ -1502,6 +1587,40 @@ describe('API', function() nvim('get_option_value', 'filetype', {buf = buf}) eq({1, 9}, nvim('win_get_cursor', win)) end) + + it('can get default option values for filetypes', function() + command('filetype plugin on') + for ft, opts in pairs { + lua = { commentstring = '-- %s' }, + vim = { commentstring = '"%s' }, + man = { tagfunc = 'v:lua.require\'man\'.goto_tag' }, + xml = { formatexpr = 'xmlformat#Format()' } + } do + for option, value in pairs(opts) do + eq(value, nvim('get_option_value', option, { filetype = ft })) + end + end + + command'au FileType lua setlocal commentstring=NEW\\ %s' + + eq('NEW %s', nvim('get_option_value', 'commentstring', { filetype = 'lua' })) + end) + + it('errors for bad FileType autocmds', function() + command'au FileType lua setlocal commentstring=BAD' + eq([[FileType Autocommands for "lua": Vim(setlocal):E537: 'commentstring' must be empty or contain %s: commentstring=BAD]], + pcall_err(nvim, 'get_option_value', 'commentstring', { filetype = 'lua' })) + end) + + it("value of 'modified' is always false for scratch buffers", function() + nvim('set_current_buf', nvim('create_buf', true, true)) + insert([[ + foo + bar + baz + ]]) + eq(false, nvim('get_option_value', 'modified', {})) + end) end) describe('nvim_{get,set}_current_buf, nvim_list_bufs', function() @@ -1774,15 +1893,32 @@ describe('API', function() feed('<C-D>') expect('a') -- recognized i_0_CTRL-D end) + + it("does not interrupt with 'digraph'", function() + command('set digraph') + feed('i,') + eq(2, eval('1+1')) -- causes K_EVENT key + feed('<BS>') + eq(2, eval('1+1')) -- causes K_EVENT key + feed('.') + expect('…') -- digraph ",." worked + feed('<Esc>') + feed(':,') + eq(2, eval('1+1')) -- causes K_EVENT key + feed('<BS>') + eq(2, eval('1+1')) -- causes K_EVENT key + feed('.') + eq('…', funcs.getcmdline()) -- digraph ",." worked + end) end) describe('nvim_get_context', function() - it('validates args', function() + it('validation', function() eq("Invalid key: 'blah'", pcall_err(nvim, 'get_context', {blah={}})) - eq('invalid value for key: types', + eq("Invalid 'types': expected Array, got Integer", pcall_err(nvim, 'get_context', {types=42})) - eq('unexpected type: zub', + eq("Invalid 'type': 'zub'", pcall_err(nvim, 'get_context', {types={'jumps', 'zub', 'zam',}})) end) it('returns map of current editor state', function() @@ -1843,6 +1979,13 @@ describe('API', function() nvim('load_context', ctx) eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]')) end) + + it('errors when context dictionary is invalid', function() + eq('E474: Failed to convert list to msgpack string buffer', + pcall_err(nvim, 'load_context', { regs = { {} }, jumps = { {} } })) + eq("Empty dictionary keys aren't allowed", + pcall_err(nvim, 'load_context', { regs = { { [''] = '' } } })) + end) end) describe('nvim_replace_termcodes', function() @@ -1886,6 +2029,12 @@ describe('API', function() -- value. eq('', meths.replace_termcodes('', true, true, true)) end) + + -- Not exactly the case, as nvim_replace_termcodes() escapes K_SPECIAL in Unicode + it('translates the result of keytrans() on string with 0x80 byte back', function() + local s = 'ff\128\253\097tt' + eq(s, meths.replace_termcodes(funcs.keytrans(s), true, true, true)) + end) end) describe('nvim_feedkeys', function() @@ -1916,6 +2065,19 @@ describe('API', function() end) describe('nvim_out_write', function() + local screen + + before_each(function() + screen = Screen.new(40, 8) + screen:attach() + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, + [1] = {bold = true, foreground = Screen.colors.SeaGreen}, + [2] = {bold = true, reverse = true}, + [3] = {foreground = Screen.colors.Blue}, + }) + end) + it('prints long messages correctly #20534', function() exec([[ set more @@ -1935,6 +2097,46 @@ describe('API', function() ]]) eq('\naaa\n' .. ('a'):rep(5002) .. '\naaa', meths.get_var('out')) end) + + it('blank line in message', function() + feed([[:call nvim_out_write("\na\n")<CR>]]) + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {2: }| + | + a | + {1:Press ENTER or type command to continue}^ | + ]]} + feed('<CR>') + feed([[:call nvim_out_write("b\n\nc\n")<CR>]]) + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + {2: }| + b | + | + c | + {1:Press ENTER or type command to continue}^ | + ]]} + end) + + it('NUL bytes in message', function() + feed([[:lua vim.api.nvim_out_write('aaa\0bbb\0\0ccc\nddd\0\0\0eee\n')<CR>]]) + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {2: }| + aaa{3:^@}bbb{3:^@^@}ccc | + ddd{3:^@^@^@}eee | + {1:Press ENTER or type command to continue}^ | + ]]} + end) end) describe('nvim_err_write', function() @@ -2023,6 +2225,60 @@ describe('API', function() ]]) feed('<cr>') -- exit the press ENTER screen end) + + it('NUL bytes in message', function() + nvim_async('err_write', 'aaa\0bbb\0\0ccc\nddd\0\0\0eee\n') + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + {0:~ }| + {3: }| + {1:aaa^@bbb^@^@ccc} | + {1:ddd^@^@^@eee} | + {2:Press ENTER or type command to continue}^ | + ]]} |