aboutsummaryrefslogtreecommitdiff
path: root/test/functional/api/vim_spec.lua
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-30 20:35:25 +0000
commit1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch)
treecd08258054db80bb9a11b1061bb091c70b76926a /test/functional/api/vim_spec.lua
parenteaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.gz
rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.bz2
rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.zip
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'test/functional/api/vim_spec.lua')
-rw-r--r--test/functional/api/vim_spec.lua876
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}^ |
+ ]]}
+ end)
+ end)
+
+ describe('nvim_err_writeln', 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] = {foreground = Screen.colors.White, background = Screen.colors.Red},
+ [2] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [3] = {bold = true, reverse = true},
+ })
+ end)
+
+ it('shows only one return prompt after all lines are shown', function()
+ nvim_async('err_writeln', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK')
+ screen:expect([[
+ |
+ {0:~ }|
+ {3: }|
+ {1:FAILURE} |
+ {1:ERROR} |
+ {1:EXCEPTION} |
+ {1:TRACEBACK} |
+ {2:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
end)
describe('nvim_list_chans, nvim_get_chan_info', function()
@@ -2115,7 +2371,7 @@ describe('API', function()
it('stream=job :terminal channel', function()
command(':terminal')
eq({id=1}, meths.get_current_buf())
- eq(3, meths.buf_get_option(1, 'channel'))
+ eq(3, meths.get_option_value('channel', {buf=1}))
local info = {
stream='job',
@@ -2223,15 +2479,14 @@ describe('API', function()
eq(5, meths.get_var('avar'))
end)
- it('throws error on malformed arguments', function()
+ it('validation', function()
local req = {
{'nvim_set_var', {'avar', 1}},
{'nvim_set_var'},
{'nvim_set_var', {'avar', 2}},
}
- local status, err = pcall(meths.call_atomic, req)
- eq(false, status)
- ok(err:match('Items in calls array must be arrays of size 2') ~= nil)
+ eq("Invalid 'calls' item: expected 2-item Array",
+ pcall_err(meths.call_atomic, req))
-- call before was done, but not after
eq(1, meths.get_var('avar'))
@@ -2239,18 +2494,16 @@ describe('API', function()
{ 'nvim_set_var', { 'bvar', { 2, 3 } } },
12,
}
- status, err = pcall(meths.call_atomic, req)
- eq(false, status)
- ok(err:match('Items in calls array must be arrays') ~= nil)
+ eq("Invalid 'calls' item: expected Array, got Integer",
+ pcall_err(meths.call_atomic, req))
eq({2,3}, meths.get_var('bvar'))
req = {
{'nvim_set_current_line', 'little line'},
{'nvim_set_var', {'avar', 3}},
}
- status, err = pcall(meths.call_atomic, req)
- eq(false, status)
- ok(err:match('Args must be Array') ~= nil)
+ eq("Invalid call args: expected Array, got String",
+ pcall_err(meths.call_atomic, req))
-- call before was done, but not after
eq(1, meths.get_var('avar'))
eq({''}, meths.buf_get_lines(0, 0, -1, true))
@@ -2271,45 +2524,45 @@ describe('API', function()
end)
it('returns nothing with empty &runtimepath', function()
- meths.set_option('runtimepath', '')
+ meths.set_option_value('runtimepath', '', {})
eq({}, meths.list_runtime_paths())
end)
it('returns single runtimepath', function()
- meths.set_option('runtimepath', 'a')
+ meths.set_option_value('runtimepath', 'a', {})
eq({'a'}, meths.list_runtime_paths())
end)
it('returns two runtimepaths', function()
- meths.set_option('runtimepath', 'a,b')
+ meths.set_option_value('runtimepath', 'a,b', {})
eq({'a', 'b'}, meths.list_runtime_paths())
end)
it('returns empty strings when appropriate', function()
- meths.set_option('runtimepath', 'a,,b')
+ meths.set_option_value('runtimepath', 'a,,b', {})
eq({'a', '', 'b'}, meths.list_runtime_paths())
- meths.set_option('runtimepath', ',a,b')
+ meths.set_option_value('runtimepath', ',a,b', {})
eq({'', 'a', 'b'}, meths.list_runtime_paths())
-- Trailing "," is ignored. Use ",," if you really really want CWD.
- meths.set_option('runtimepath', 'a,b,')
+ meths.set_option_value('runtimepath', 'a,b,', {})
eq({'a', 'b'}, meths.list_runtime_paths())
- meths.set_option('runtimepath', 'a,b,,')
+ meths.set_option_value('runtimepath', 'a,b,,', {})
eq({'a', 'b', ''}, meths.list_runtime_paths())
end)
it('truncates too long paths', function()
local long_path = ('/a'):rep(8192)
- meths.set_option('runtimepath', long_path)
+ meths.set_option_value('runtimepath', long_path, {})
local paths_list = meths.list_runtime_paths()
eq({}, paths_list)
end)
end)
it('can throw exceptions', function()
- local status, err = pcall(nvim, 'get_option', 'invalid-option')
+ local status, err = pcall(nvim, 'get_option_value', 'invalid-option', {})
eq(false, status)
- ok(err:match('Invalid option name') ~= nil)
+ ok(err:match("Unknown option 'invalid%-option'") ~= nil)
end)
it('does not truncate error message <1 MB #5984', function()
local very_long_name = 'A'..('x'):rep(10000)..'Z'
- local status, err = pcall(nvim, 'get_option', very_long_name)
+ local status, err = pcall(nvim, 'get_option_value', very_long_name, {})
eq(false, status)
eq(very_long_name, err:match('Ax+Z?'))
end)
@@ -2322,7 +2575,7 @@ describe('API', function()
describe('nvim_parse_expression', function()
before_each(function()
- meths.set_option('isident', '')
+ meths.set_option_value('isident', '', {})
end)
local function simplify_east_api_node(line, east_api_node)
@@ -2536,20 +2789,26 @@ describe('API', function()
{
chan = 1,
ext_cmdline = false,
- ext_popupmenu = false,
- ext_tabline = false,
- ext_wildmenu = false,
+ ext_hlstate = false,
ext_linegrid = screen._options.ext_linegrid or false,
+ ext_messages = false,
ext_multigrid = false,
- ext_hlstate = false,
+ ext_popupmenu = false,
+ ext_tabline = false,
ext_termcolors = false,
- ext_messages = false,
+ ext_wildmenu = false,
height = 4,
- rgb = true,
override = true,
+ rgb = true,
+ stdin_tty = false,
+ stdout_tty = false,
+ term_background = '',
+ term_colors = 0,
+ term_name = '',
width = 20,
}
}
+
eq(expected, nvim("list_uis"))
screen:detach()
@@ -2601,9 +2860,9 @@ describe('API', function()
end)
it('can change buftype before visiting', function()
- meths.set_option("hidden", false)
+ meths.set_option_value("hidden", false, {})
eq({id=2}, meths.create_buf(true, false))
- meths.buf_set_option(2, "buftype", "nofile")
+ meths.set_option_value("buftype", "nofile", {buf=2})
meths.buf_set_lines(2, 0, -1, true, {"test text"})
command("split | buffer 2")
eq({id=2}, meths.get_current_buf())
@@ -2622,6 +2881,18 @@ describe('API', function()
eq(false, eval('g:fired'))
end)
+ it('TextChanged and TextChangedI do not trigger without changes', function()
+ local buf = meths.create_buf(true, false)
+ command([[let g:changed = '']])
+ meths.create_autocmd({'TextChanged', 'TextChangedI'}, {
+ buffer = buf,
+ command = 'let g:changed ..= mode()',
+ })
+ meths.set_current_buf(buf)
+ feed('i')
+ eq('', meths.get_var('changed'))
+ end)
+
it('scratch-buffer', function()
eq({id=2}, meths.create_buf(false, true))
eq({id=3}, meths.create_buf(true, true))
@@ -2630,7 +2901,7 @@ describe('API', function()
eq(' 1 %a "[No Name]" line 1\n'..
' 3 h "[Scratch]" line 0\n'..
' 4 h "[Scratch]" line 0',
- meths.exec('ls', true))
+ exec_capture('ls'))
-- current buffer didn't change
eq({id=1}, meths.get_current_buf())
@@ -2646,10 +2917,10 @@ describe('API', function()
local edited_buf = 2
meths.buf_set_lines(edited_buf, 0, -1, true, {"some text"})
for _,b in ipairs(scratch_bufs) do
- eq('nofile', meths.buf_get_option(b, 'buftype'))
- eq('hide', meths.buf_get_option(b, 'bufhidden'))
- eq(false, meths.buf_get_option(b, 'swapfile'))
- eq(false, meths.buf_get_option(b, 'modeline'))
+ eq('nofile', meths.get_option_value('buftype', {buf=b}))
+ eq('hide', meths.get_option_value('bufhidden', {buf=b}))
+ eq(false, meths.get_option_value('swapfile', {buf=b}))
+ eq(false, meths.get_option_value('modeline', {buf=b}))
end
--
@@ -2662,10 +2933,10 @@ describe('API', function()
{1:~ }|
|
]])
- eq('nofile', meths.buf_get_option(edited_buf, 'buftype'))
- eq('hide', meths.buf_get_option(edited_buf, 'bufhidden'))
- eq(false, meths.buf_get_option(edited_buf, 'swapfile'))
- eq(false, meths.buf_get_option(edited_buf, 'modeline'))
+ eq('nofile', meths.get_option_value('buftype', {buf=edited_buf}))
+ eq('hide', meths.get_option_value('bufhidden', {buf=edited_buf}))
+ eq(false, meths.get_option_value('swapfile', {buf=edited_buf}))
+ eq(false, meths.get_option_value('modeline', {buf=edited_buf}))
-- Scratch buffer can be wiped without error.
command('bwipe')
@@ -2679,7 +2950,9 @@ describe('API', function()
it('does not cause heap-use-after-free on exit while setting options', function()
command('au OptionSet * q')
- expect_exit(command, 'silent! call nvim_create_buf(0, 1)')
+ command('silent! call nvim_create_buf(0, 1)')
+ -- nowadays this works because we don't execute any spurious autocmds at all #24824
+ assert_alive()
end)
end)
@@ -2744,13 +3017,13 @@ describe('API', function()
end)
it('should not crash when echoed', function()
- meths.exec("echo nvim_get_all_options_info()", true)
+ meths.exec2("echo nvim_get_all_options_info()", { output = true })
end)
end)
describe('nvim_get_option_info', function()
it('should error for unknown options', function()
- eq("no such option: 'bogus'", pcall_err(meths.get_option_info, 'bogus'))
+ eq("Invalid option (not found): 'bogus'", pcall_err(meths.get_option_info, 'bogus'))
end)
it('should return the same options for short and long name', function()
@@ -2796,7 +3069,7 @@ describe('API', function()
it('should have information about global options', function()
-- precondition: the option was changed from its default
-- in test setup.
- eq(false, meths.get_option'showcmd')
+ eq(false, meths.get_option_value('showcmd', {}))
eq({
allows_duplicates = true,
@@ -2813,6 +3086,117 @@ describe('API', function()
type = "boolean",
was_set = true
}, meths.get_option_info'showcmd')
+
+ meths.set_option_value('showcmd', true, {})
+
+ eq({
+ allows_duplicates = true,
+ commalist = false,
+ default = true,
+ flaglist = false,
+ global_local = false,
+ last_set_chan = 1,
+ last_set_linenr = 0,
+ last_set_sid = -9,
+ name = "showcmd",
+ scope = "global",
+ shortname = "sc",
+ type = "boolean",
+ was_set = true
+ }, meths.get_option_info'showcmd')
+ end)
+ end)
+
+ describe('nvim_get_option_info2', function()
+ local fname
+ local bufs
+ local wins
+
+ before_each(function()
+ fname = tmpname()
+ write_file(fname, [[
+ setglobal dictionary=mydict " 1, global-local (buffer)
+ setlocal formatprg=myprg " 2, global-local (buffer)
+ setglobal equalprg=prg1 " 3, global-local (buffer)
+ setlocal equalprg=prg2 " 4, global-local (buffer)
+ setglobal fillchars=stl:x " 5, global-local (window)
+ setlocal listchars=eol:c " 6, global-local (window)
+ setglobal showbreak=aaa " 7, global-local (window)
+ setlocal showbreak=bbb " 8, global-local (window)
+ setglobal completeopt=menu " 9, global
+ ]])
+
+ exec_lua 'vim.cmd.vsplit()'
+ meths.create_buf(false, false)
+
+ bufs = meths.list_bufs()
+ wins = meths.list_wins()
+
+ meths.win_set_buf(wins[1].id, bufs[1].id)
+ meths.win_set_buf(wins[2].id, bufs[2].id)
+
+ meths.set_current_win(wins[2].id)
+ meths.exec('source ' .. fname, false)
+
+ meths.set_current_win(wins[1].id)
+ end)
+
+ after_each(function()
+ os.remove(fname)
+ end)
+
+ it('should return option information', function()
+ eq(meths.get_option_info('dictionary'), meths.get_option_info2('dictionary', {})) -- buffer
+ eq(meths.get_option_info('fillchars'), meths.get_option_info2('fillchars', {})) -- window
+ eq(meths.get_option_info('completeopt'), meths.get_option_info2('completeopt', {})) -- global
+ end)
+
+ describe('last set', function()
+ local tests = {
+ {desc="(buf option, global requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {scope='global'}}},
+ {desc="(buf option, global requested, local set) is not set", linenr=0, sid=0, args={'formatprg', {scope='global'}}},
+ {desc="(buf option, global requested, both set) points to global", linenr=3, sid=1, args={'equalprg', {scope='global'}}},
+ {desc="(buf option, local requested, global set) is not set", linenr=0, sid=0, args={'dictionary', {scope='local'}}},
+ {desc="(buf option, local requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {scope='local'}}},
+ {desc="(buf option, local requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {scope='local'}}},
+ {desc="(buf option, fallback requested, global set) points to global", linenr=1, sid=1, args={'dictionary', {}}},
+ {desc="(buf option, fallback requested, local set) points to local", linenr=2, sid=1, args={'formatprg', {}}},
+ {desc="(buf option, fallback requested, both set) points to local", linenr=4, sid=1, args={'equalprg', {}}},
+ {desc="(win option, global requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {scope='global'}}},
+ {desc="(win option, global requested, local set) is not set", linenr=0, sid=0, args={'listchars', {scope='global'}}},
+ {desc="(win option, global requested, both set) points to global", linenr=7, sid=1, args={'showbreak', {scope='global'}}},
+ {desc="(win option, local requested, global set) is not set", linenr=0, sid=0, args={'fillchars', {scope='local'}}},
+ {desc="(win option, local requested, local set) points to local", linenr=6, sid=1, args={'listchars', {scope='local'}}},
+ {desc="(win option, local requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {scope='local'}}},
+ {desc="(win option, fallback requested, global set) points to global", linenr=5, sid=1, args={'fillchars', {}}},
+ {desc="(win option, fallback requested, local set) points to local", linenr=6, sid=1, args={'listchars', {}}},
+ {desc="(win option, fallback requested, both set) points to local", linenr=8, sid=1, args={'showbreak', {}}},
+ {desc="(global option, global requested) points to global", linenr=9, sid=1, args={'completeopt', {scope='global'}}},
+ {desc="(global option, local requested) is not set", linenr=0, sid=0, args={'completeopt', {scope='local'}}},
+ {desc="(global option, fallback requested) points to global", linenr=9, sid=1, args={'completeopt', {}}},
+ }
+
+ for _, t in pairs(tests) do
+ it(t.desc, function()
+ -- Switch to the target buffer/window so that curbuf/curwin are used.
+ meths.set_current_win(wins[2].id)
+ local info = meths.get_option_info2(unpack(t.args))
+ eq(t.linenr, info.last_set_linenr)
+ eq(t.sid, info.last_set_sid)
+ end)
+ end
+
+ it('is provided for cross-buffer requests', function()
+ local info = meths.get_option_info2('formatprg', {buf=bufs[2].id})
+ eq(2, info.last_set_linenr)
+ eq(1, info.last_set_sid)
+ end)
+
+ it('is provided for cross-window requests', function()
+ local info = meths.get_option_info2('listchars', {win=wins[2].id})
+ eq(6, info.last_set_linenr)
+ eq(1, info.last_set_sid)
+ end)
end)
end)
@@ -2820,11 +3204,10 @@ describe('API', function()
local screen
before_each(function()
- clear()
screen = Screen.new(40, 8)
screen:attach()
screen:set_default_attr_ids({
- [0] = {bold=true, foreground=Screen.colors.Blue},
+ [0] = {bold = true, foreground = Screen.colors.Blue},
[1] = {bold = true, foreground = Screen.colors.SeaGreen},
[2] = {bold = true, reverse = true},
[3] = {foreground = Screen.colors.Brown, bold = true}, -- Statement
@@ -2879,13 +3262,13 @@ describe('API', function()
it('can save message history', function()
nvim('command', 'set cmdheight=2') -- suppress Press ENTER
nvim("echo", {{"msg\nmsg"}, {"msg"}}, true, {})
- eq("msg\nmsgmsg", meths.exec('messages', true))
+ eq("msg\nmsgmsg", exec_capture('messages'))
end)
it('can disable saving message history', function()
nvim('command', 'set cmdheight=2') -- suppress Press ENTER
nvim_async("echo", {{"msg\nmsg"}, {"msg"}}, false, {})
- eq("", meths.exec("messages", true))
+ eq("", exec_capture('messages'))
end)
end)
@@ -2894,7 +3277,6 @@ describe('API', function()
local screen
before_each(function()
- clear()
screen = Screen.new(100, 35)
screen:attach()
screen:set_default_attr_ids({
@@ -3031,10 +3413,10 @@ describe('API', function()
eq(true, meths.del_mark('F'))
eq({0, 0}, meths.buf_get_mark(buf, 'F'))
end)
- it('fails when invalid marks are used', function()
- eq(false, pcall(meths.del_mark, 'f'))
- eq(false, pcall(meths.del_mark, '!'))
- eq(false, pcall(meths.del_mark, 'fail'))
+ it('validation', function()
+ eq("Invalid mark name (must be file/uppercase): 'f'", pcall_err(meths.del_mark, 'f'))
+ eq("Invalid mark name (must be file/uppercase): '!'", pcall_err(meths.del_mark, '!'))
+ eq("Invalid mark name (must be a single char): 'fail'", pcall_err(meths.del_mark, 'fail'))
end)
end)
describe('nvim_get_mark', function()
@@ -3048,10 +3430,10 @@ describe('API', function()
assert(string.find(mark[4], "mybuf$"))
eq({2, 2, buf.id, mark[4]}, mark)
end)
- it('fails when invalid marks are used', function()
- eq(false, pcall(meths.del_mark, 'f'))
- eq(false, pcall(meths.del_mark, '!'))
- eq(false, pcall(meths.del_mark, 'fail'))
+ it('validation', function()
+ eq("Invalid mark name (must be file/uppercase): 'f'", pcall_err(meths.get_mark, 'f', {}))
+ eq("Invalid mark name (must be file/uppercase): '!'", pcall_err(meths.get_mark, '!', {}))
+ eq("Invalid mark name (must be a single char): 'fail'", pcall_err(meths.get_mark, 'fail', {}))
end)
it('returns the expected when mark is not set', function()
eq(true, meths.del_mark('A'))
@@ -3113,15 +3495,15 @@ describe('API', function()
meths.eval_statusline('a%=b', { fillchar = '\031', maxwidth = 5 }))
end)
it('rejects multiple-character fillchar', function()
- eq('fillchar must be a single character',
+ eq("Invalid 'fillchar': expected single character",
pcall_err(meths.eval_statusline, '', { fillchar = 'aa' }))
end)
it('rejects empty string fillchar', function()
- eq('fillchar must be a single character',
+ eq("Invalid 'fillchar': expected single character",
pcall_err(meths.eval_statusline, '', { fillchar = '' }))
end)
it('rejects non-string fillchar', function()
- eq('fillchar must be a single character',
+ eq("Invalid 'fillchar': expected String, got Integer",
pcall_err(meths.eval_statusline, '', { fillchar = 1 }))
end)
it('rejects invalid string', function()
@@ -3213,6 +3595,38 @@ describe('API', function()
'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
{ use_winbar = true, highlights = true }))
end)
+ it('works with statuscolumn', function()
+ exec([[
+ let &stc='%C%s%=%l '
+ set cul nu nuw=3 scl=yes:2 fdc=2
+ call setline(1, repeat(['aaaaa'], 5))
+ let g:ns = nvim_create_namespace('')
+ call sign_define('a', {'text':'aa', 'texthl':'IncSearch', 'numhl':'Normal'})
+ call sign_place(2, 1, 'a', bufnr(), {'lnum':4})
+ call nvim_buf_set_extmark(0, g:ns, 3, 1, { 'sign_text':'bb', 'sign_hl_group':'ErrorMsg' })
+ 1,5fold | 1,5 fold | foldopen!
+ norm 4G
+ ]])
+ eq({
+ str = '││aabb 4 ',
+ width = 9,
+ highlights = {
+ { group = 'CursorLineFold', start = 0 },
+ { group = 'Normal', start = 6 },
+ { group = 'IncSearch', start = 6 },
+ { group = 'ErrorMsg', start = 8 },
+ { group = 'Normal', start = 10 }
+ }
+ }, meths.eval_statusline('%C%s%=%l ', { use_statuscol_lnum = 4, highlights = true }))
+ eq({
+ str = '3 ' ,
+ width = 2,
+ highlights = {
+ { group = 'LineNr', start = 0 },
+ { group = 'ErrorMsg', start = 1 }
+ }
+ }, meths.eval_statusline('%l%#ErrorMsg# ', { use_statuscol_lnum = 3, highlights = true }))
+ end)
it('no memory leak with click functions', function()
meths.eval_statusline('%@ClickFunc@StatusLineStringWithClickFunc%T', {})
eq({
@@ -3226,6 +3640,7 @@ describe('API', function()
end)
end)
end)
+
describe('nvim_parse_cmd', function()
it('works', function()
eq({
@@ -3708,7 +4123,7 @@ describe('API', function()
}
}, meths.parse_cmd('MyCommand test it', {}))
end)
- it('errors for invalid command', function()
+ it('validates command', function()
eq('Error while parsing command line', pcall_err(meths.parse_cmd, '', {}))
eq('Error while parsing command line', pcall_err(meths.parse_cmd, '" foo', {}))
eq('Error while parsing command line: E492: Not an editor command: Fubar',
@@ -3807,20 +4222,87 @@ describe('API', function()
meths.cmd(meths.parse_cmd("set cursorline", {}), {})
eq(true, meths.get_option_value("cursorline", {}))
end)
+ it('no side-effects (error messages) in pcall() #20339', function()
+ eq({ false, 'Error while parsing command line: E16: Invalid range' },
+ exec_lua([=[return {pcall(vim.api.nvim_parse_cmd, "'<,'>n", {})}]=]))
+ eq('', eval('v:errmsg'))
+ end)
end)
+
describe('nvim_cmd', function()
it('works', function ()
meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
eq(true, meths.get_option_value("cursorline", {}))
end)
+
+ it('validation', function()
+ eq("Invalid 'cmd': expected non-empty String",
+ pcall_err(meths.cmd, { cmd = ""}, {}))
+ eq("Invalid 'cmd': expected String, got Array",
+ pcall_err(meths.cmd, { cmd = {}}, {}))
+ eq("Invalid 'args': expected Array, got Boolean",
+ pcall_err(meths.cmd, { cmd = "set", args = true }, {}))
+ eq("Invalid command arg: expected non-whitespace",
+ pcall_err(meths.cmd, { cmd = "set", args = {' '}, }, {}))
+ eq("Invalid command arg: expected valid type, got Array",
+ pcall_err(meths.cmd, { cmd = "set", args = {{}}, }, {}))
+ eq("Wrong number of arguments",
+ pcall_err(meths.cmd, { cmd = "aboveleft", args = {}, }, {}))
+ eq("Command cannot accept bang: print",
+ pcall_err(meths.cmd, { cmd = "print", args = {}, bang = true }, {}))
+
+ eq("Command cannot accept range: set",
+ pcall_err(meths.cmd, { cmd = "set", args = {}, range = {1} }, {}))
+ eq("Invalid 'range': expected Array, got Boolean",
+ pcall_err(meths.cmd, { cmd = "print", args = {}, range = true }, {}))
+ eq("Invalid 'range': expected <=2 elements",
+ pcall_err(meths.cmd, { cmd = "print", args = {}, range = {1,2,3,4} }, {}))
+ eq("Invalid range element: expected non-negative Integer",
+ pcall_err(meths.cmd, { cmd = "print", args = {}, range = {-1} }, {}))
+
+ eq("Command cannot accept count: set",
+ pcall_err(meths.cmd, { cmd = "set", args = {}, count = 1 }, {}))
+ eq("Invalid 'count': expected Integer, got Boolean",
+ pcall_err(meths.cmd, { cmd = "print", args = {}, count = true }, {}))
+ eq("Invalid 'count': expected non-negative Integer",
+ pcall_err(meths.cmd, { cmd = "print", args = {}, count = -1 }, {}))
+
+ eq("Command cannot accept register: set",
+ pcall_err(meths.cmd, { cmd = "set", args = {}, reg = 'x' }, {}))
+ eq('Cannot use register "=',
+ pcall_err(meths.cmd, { cmd = "put", args = {}, reg = '=' }, {}))
+ eq("Invalid 'reg': expected single character, got xx",
+ pcall_err(meths.cmd, { cmd = "put", args = {}, reg = 'xx' }, {}))
+
+ -- #20681
+ eq('Invalid command: "win_getid"', pcall_err(meths.cmd, { cmd = 'win_getid'}, {}))
+ eq('Invalid command: "echo "hi""', pcall_err(meths.cmd, { cmd = 'echo "hi"'}, {}))
+ eq('Invalid command: "win_getid"', pcall_err(exec_lua, [[return vim.cmd.win_getid{}]]))
+
+ -- Lua call allows empty {} for dict item.
+ eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, magic = {} }]]))
+ eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, mods = {} }]]))
+ eq('', meths.cmd({ cmd = "set", args = {}, magic = {} }, {}))
+
+ -- Lua call does not allow non-empty list-like {} for dict item.
+ eq("Invalid 'magic': Expected Dict-like Lua table",
+ pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { 'a' } }]]))
+ eq("Invalid key: 'bogus'",
+ pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { bogus = true } }]]))
+ eq("Invalid key: 'bogus'",
+ pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, mods = { bogus = true } }]]))
+ end)
+
it('captures output', function()
eq("foo", meths.cmd({ cmd = "echo", args = { '"foo"' } }, { output = true }))
end)
+
it('sets correct script context', function()
meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
- local str = meths.exec([[verbose set cursorline?]], true)
+ local str = exec_capture([[verbose set cursorline?]])
neq(nil, str:find("cursorline\n\tLast set from API client %(channel id %d+%)"))
end)
+
it('works with range', function()
insert [[
line1
@@ -3867,7 +4349,7 @@ describe('API', function()
line6
]]
meths.cmd({ cmd = "del", range = { 2, 4 }, reg = 'a' }, {})
- meths.exec("1put a", false)
+ command('1put a')
expect [[
line1
line2
@@ -3914,7 +4396,7 @@ describe('API', function()
meths.cmd({ cmd = "buffers", mods = { filter = { pattern = "foo", force = true } } },
{ output = true }))
- -- with emsg_silent = true error is suppresed
+ -- with emsg_silent = true error is suppressed
feed([[:lua vim.api.nvim_cmd({ cmd = 'call', mods = { emsg_silent = true } }, {})<CR>]])
eq('', meths.cmd({ cmd = 'messages' }, { output = true }))
-- error from the next command typed is not suppressed #21420
@@ -3927,16 +4409,16 @@ describe('API', function()
vim.api.nvim_echo({{ opts.fargs[1] }}, false, {})
end, { nargs = 1 })
]])
- eq(lfs.currentdir(),
+ eq(luv.cwd(),
meths.cmd({ cmd = "Foo", args = { '%:p:h' }, magic = { file = true } },
{ output = true }))
end)
it('splits arguments correctly', function()
- meths.exec([[
+ exec([[
function! FooFunc(...)
echo a:000
endfunction
- ]], false)
+ ]])
meths.create_user_command("Foo", "call FooFunc(<f-args>)", { nargs = '+' })
eq([=[['a quick', 'brown fox', 'jumps over the', 'lazy dog']]=],
meths.cmd({ cmd = "Foo", args = { "a quick", "brown fox", "jumps over the", "lazy dog"}},
@@ -3948,7 +4430,7 @@ describe('API', function()
it('splits arguments correctly for Lua callback', function()
meths.exec_lua([[
local function FooFunc(opts)
- vim.pretty_print(opts.fargs)
+ vim.print(opts.fargs)
end
vim.api.nvim_create_user_command("Foo", FooFunc, { nargs = '+' })
@@ -4054,8 +4536,6 @@ describe('API', function()
eq('1', meths.cmd({cmd = 'echo', args = {true}}, {output = true}))
end)
describe('first argument as count', function()
- before_each(clear)
-
it('works', function()
command('vsplit | enew')
meths.cmd({cmd = 'bdelete', args = {meths.get_current_buf()}}, {})