aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rw-r--r--test/functional/api/keymap_spec.lua114
-rw-r--r--test/functional/api/server_requests_spec.lua9
-rw-r--r--test/functional/api/vim_spec.lua40
-rw-r--r--test/functional/eval/function_spec.lua29
-rw-r--r--test/functional/eval/hostname_spec.lua2
-rw-r--r--test/functional/eval/msgpack_functions_spec.lua6
-rw-r--r--test/functional/ex_cmds/echo_spec.lua321
-rw-r--r--test/functional/ex_cmds/mksession_spec.lua49
-rw-r--r--test/functional/helpers.lua71
-rw-r--r--test/functional/legacy/assert_spec.lua10
-rw-r--r--test/functional/legacy/search_spec.lua474
-rw-r--r--test/functional/lua/overrides_spec.lua119
-rw-r--r--test/functional/options/defaults_spec.lua52
-rw-r--r--test/functional/shada/errors_spec.lua18
-rw-r--r--test/functional/ui/highlight_spec.lua170
-rw-r--r--test/functional/ui/screen.lua5
-rw-r--r--test/functional/viml/function_spec.lua232
17 files changed, 1626 insertions, 95 deletions
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index 833e0d2f3c..aa556b563d 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -1,5 +1,6 @@
-
local helpers = require('test.functional.helpers')(after_each)
+local global_helpers = require('test.helpers')
+
local clear = helpers.clear
local command = helpers.command
local curbufmeths = helpers.curbufmeths
@@ -8,13 +9,7 @@ local funcs = helpers.funcs
local meths = helpers.meths
local source = helpers.source
-local function local_copy(t)
- local copy = {}
- for k,v in pairs(t) do
- copy[k] = v
- end
- return copy
-end
+local shallowcopy = global_helpers.shallowcopy
describe('get_keymap', function()
before_each(clear)
@@ -22,16 +17,16 @@ describe('get_keymap', function()
-- Basic mapping and table to be used to describe results
local foo_bar_string = 'nnoremap foo bar'
local foo_bar_map_table = {
- lhs='foo',
- silent=0,
- rhs='bar',
- expr=0,
- sid=0,
- buffer=0,
- nowait=0,
- mode='n',
- noremap=1,
- }
+ lhs='foo',
+ silent=0,
+ rhs='bar',
+ expr=0,
+ sid=0,
+ buffer=0,
+ nowait=0,
+ mode='n',
+ noremap=1,
+ }
it('returns empty list when no map', function()
eq({}, meths.get_keymap('n'))
@@ -50,7 +45,7 @@ describe('get_keymap', function()
-- Add another mapping
command('nnoremap foo_longer bar_longer')
- local foolong_bar_map_table = local_copy(foo_bar_map_table)
+ local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
foolong_bar_map_table['lhs'] = 'foo_longer'
foolong_bar_map_table['rhs'] = 'bar_longer'
@@ -72,7 +67,7 @@ describe('get_keymap', function()
command('inoremap foo bar')
-- The table will be the same except for the mode
- local insert_table = local_copy(foo_bar_map_table)
+ local insert_table = shallowcopy(foo_bar_map_table)
insert_table['mode'] = 'i'
eq({insert_table}, meths.get_keymap('i'))
@@ -81,11 +76,11 @@ describe('get_keymap', function()
it('considers scope', function()
-- change the map slightly
command('nnoremap foo_longer bar_longer')
- local foolong_bar_map_table = local_copy(foo_bar_map_table)
+ local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
foolong_bar_map_table['lhs'] = 'foo_longer'
foolong_bar_map_table['rhs'] = 'bar_longer'
- local buffer_table = local_copy(foo_bar_map_table)
+ local buffer_table = shallowcopy(foo_bar_map_table)
buffer_table['buffer'] = 1
command('nnoremap <buffer> foo bar')
@@ -98,7 +93,7 @@ describe('get_keymap', function()
it('considers scope for overlapping maps', function()
command('nnoremap foo bar')
- local buffer_table = local_copy(foo_bar_map_table)
+ local buffer_table = shallowcopy(foo_bar_map_table)
buffer_table['buffer'] = 1
command('nnoremap <buffer> foo bar')
@@ -121,7 +116,7 @@ describe('get_keymap', function()
command('nnoremap <buffer> foo bar')
-- Final buffer will have buffer mappings
- local buffer_table = local_copy(foo_bar_map_table)
+ local buffer_table = shallowcopy(foo_bar_map_table)
buffer_table['buffer'] = final_buffer
eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n'))
eq({buffer_table}, meths.buf_get_keymap(0, 'n'))
@@ -243,4 +238,75 @@ describe('get_keymap', function()
eq('<F12>', meths.get_keymap('n')[1]['lhs'])
eq(':let g:maparg_test_var = 1<CR>', meths.get_keymap('n')[1]['rhs'])
end)
+
+ it('works correctly despite various &cpo settings', function()
+ local cpo_table = {
+ silent=0,
+ expr=0,
+ sid=0,
+ buffer=0,
+ nowait=0,
+ noremap=1,
+ }
+ local function cpomap(lhs, rhs, mode)
+ local ret = shallowcopy(cpo_table)
+ ret.lhs = lhs
+ ret.rhs = rhs
+ ret.mode = mode
+ return ret
+ end
+
+ command('set cpo-=< cpo+=B')
+ command('nnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
+ command('nnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
+
+ command('set cpo+=B<')
+ command('xnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
+ command('xnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
+
+ command('set cpo-=B<')
+ command('snoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
+ command('snoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
+
+ command('set cpo-=B cpo+=<')
+ command('onoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
+ command('onoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
+
+ for _, cmd in ipairs({
+ 'set cpo-=B cpo+=<',
+ 'set cpo-=B<',
+ 'set cpo+=B<',
+ 'set cpo-=< cpo+=B',
+ }) do
+ command(cmd)
+ eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'n'),
+ cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'n')},
+ meths.get_keymap('n'))
+ eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'x'),
+ cpomap('\\<lt>C-a><lt>C-a><lt>LT>C-a>\\', '\\<lt>C-b><lt>C-b><lt>LT>C-b>\\', 'x')},
+ meths.get_keymap('x'))
+ eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 's'),
+ cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 's')},
+ meths.get_keymap('s'))
+ eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 'o'),
+ cpomap('<lt>C-a><lt>C-a><lt>LT>C-a> ', '<lt>C-b><lt>C-b><lt>LT>C-b>', 'o')},
+ meths.get_keymap('o'))
+ end
+ end)
+
+ it('always uses space for space and bar for bar', function()
+ local space_table = {
+ lhs='| |',
+ rhs='| |',
+ mode='n',
+ silent=0,
+ expr=0,
+ sid=0,
+ buffer=0,
+ nowait=0,
+ noremap=1,
+ }
+ command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>')
+ eq({space_table}, meths.get_keymap('n'))
+ end)
end)
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index cf15062325..6a32f979ea 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -282,8 +282,13 @@ describe('server -> client', function()
end)
end)
- describe('when connecting to its own pipe adress', function()
- it('it does not deadlock', function()
+ describe('connecting to its own pipe address', function()
+ it('does not deadlock', function()
+ if not os.getenv("TRAVIS") and helpers.os_name() == "osx" then
+ -- It does, in fact, deadlock on QuickBuild. #6851
+ pending("deadlocks on QuickBuild", function() end)
+ return
+ end
local address = funcs.serverlist()[1]
local first = string.sub(address,1,1)
ok(first == '/' or first == '\\')
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 161682b973..c531d4af46 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -327,11 +327,11 @@ describe('api', function()
{'nvim_get_mode', {}},
{'nvim_eval', {'1'}},
}
- eq({{{mode='n', blocking=false},
- 13,
- {mode='n', blocking=false}, -- TODO: should be blocked=true
- 1},
- NIL}, meths.call_atomic(req))
+ eq({ { {mode='n', blocking=false},
+ 13,
+ {mode='n', blocking=false}, -- TODO: should be blocked=true
+ 1 },
+ NIL}, meths.call_atomic(req))
eq({mode='r', blocking=true}, nvim("get_mode"))
end)
-- TODO: bug #6166
@@ -588,6 +588,36 @@ describe('api', function()
end)
end)
+ describe('list_runtime_paths', function()
+ it('returns nothing with empty &runtimepath', function()
+ meths.set_option('runtimepath', '')
+ eq({}, meths.list_runtime_paths())
+ end)
+ it('returns single runtimepath', function()
+ meths.set_option('runtimepath', 'a')
+ eq({'a'}, meths.list_runtime_paths())
+ end)
+ it('returns two runtimepaths', function()
+ meths.set_option('runtimepath', 'a,b')
+ eq({'a', 'b'}, meths.list_runtime_paths())
+ end)
+ it('returns empty strings when appropriate', function()
+ meths.set_option('runtimepath', 'a,,b')
+ eq({'a', '', 'b'}, meths.list_runtime_paths())
+ meths.set_option('runtimepath', ',a,b')
+ eq({'', 'a', 'b'}, meths.list_runtime_paths())
+ meths.set_option('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)
+ local paths_list = meths.list_runtime_paths()
+ neq({long_path}, paths_list)
+ eq({long_path:sub(1, #(paths_list[1]))}, paths_list)
+ end)
+ end)
+
it('can throw exceptions', function()
local status, err = pcall(nvim, 'get_option', 'invalid-option')
eq(false, status)
diff --git a/test/functional/eval/function_spec.lua b/test/functional/eval/function_spec.lua
new file mode 100644
index 0000000000..776e760aaf
--- /dev/null
+++ b/test/functional/eval/function_spec.lua
@@ -0,0 +1,29 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local eq = helpers.eq
+local exc_exec = helpers.exc_exec
+
+describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
+ local max_func_args = 20 -- from eval.h
+ local range = helpers.funcs.range
+
+ before_each(clear)
+
+ it('printf()', function()
+ local printf = helpers.funcs.printf
+ local rep = helpers.funcs['repeat']
+ local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'
+ eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args))))
+ local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
+ eq('Vim(call):E740: Too many arguments for function printf', ret)
+ end)
+
+ it('rpcnotify()', function()
+ local rpcnotify = helpers.funcs.rpcnotify
+ local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args)))
+ eq(1, ret)
+ ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
+ eq('Vim(call):E740: Too many arguments for function rpcnotify', ret)
+ end)
+end)
diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/eval/hostname_spec.lua
index f1867846c4..6d5b64b929 100644
--- a/test/functional/eval/hostname_spec.lua
+++ b/test/functional/eval/hostname_spec.lua
@@ -8,7 +8,7 @@ describe('hostname()', function()
it('returns hostname string', function()
local actual = call('hostname')
- ok(string.len(actual) > 1)
+ ok(string.len(actual) > 0)
if call('executable', 'hostname') == 1 then
local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '')
helpers.eq(expected, actual)
diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua
index 4a052b4aff..b241635dfe 100644
--- a/test/functional/eval/msgpack_functions_spec.lua
+++ b/test/functional/eval/msgpack_functions_spec.lua
@@ -337,7 +337,8 @@ describe('msgpack*() functions', function()
eq(1, eval('type(parsed[0]) == type(0) ' ..
'|| parsed[0]._TYPE is v:msgpack_types.integer'))
if eval('type(parsed[0]) == type(0)') == 1 then
- eq(1, eval('0xFFFFFFFFFFFFFFFF == parsed[0]'))
+ command('call assert_equal(0xFFFFFFFFFFFFFFFF, parsed[0])')
+ eq({}, eval('v:errors'))
else
eq({_TYPE={}, _VAL={1, 3, 0x7FFFFFFF, 0x7FFFFFFF}}, eval('parsed[0]'))
end
@@ -351,7 +352,8 @@ describe('msgpack*() functions', function()
eq(1, eval('type(parsed[0]) == type(0) ' ..
'|| parsed[0]._TYPE is v:msgpack_types.integer'))
if eval('type(parsed[0]) == type(0)') == 1 then
- eq(1, eval('-0x8000000000000000 == parsed[0]'))
+ command('call assert_equal(-0x7fffffffffffffff - 1, parsed[0])')
+ eq({}, eval('v:errors'))
else
eq({_TYPE={}, _VAL={-1, 2, 0, 0}}, eval('parsed[0]'))
end
diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua
new file mode 100644
index 0000000000..10c7230896
--- /dev/null
+++ b/test/functional/ex_cmds/echo_spec.lua
@@ -0,0 +1,321 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local eq = helpers.eq
+local NIL = helpers.NIL
+local eval = helpers.eval
+local clear = helpers.clear
+local meths = helpers.meths
+local funcs = helpers.funcs
+local source = helpers.source
+local dedent = helpers.dedent
+local command = helpers.command
+local exc_exec = helpers.exc_exec
+local redir_exec = helpers.redir_exec
+
+describe(':echo', function()
+ before_each(function()
+ clear()
+ source([[
+ function String(s)
+ return execute('echo a:s')[1:]
+ endfunction
+ ]])
+ end)
+
+ describe('used to represent floating-point values', function()
+ it('dumps NaN values', function()
+ eq('str2float(\'nan\')', eval('String(str2float(\'nan\'))'))
+ end)
+
+ it('dumps infinite values', function()
+ eq('str2float(\'inf\')', eval('String(str2float(\'inf\'))'))
+ eq('-str2float(\'inf\')', eval('String(str2float(\'-inf\'))'))
+ end)
+
+ it('dumps regular values', function()
+ eq('1.5', funcs.String(1.5))
+ eq('1.56e-20', funcs.String(1.56000e-020))
+ eq('0.0', eval('String(0.0)'))
+ end)
+
+ it('dumps special v: values', function()
+ eq('v:true', eval('String(v:true)'))
+ eq('v:false', eval('String(v:false)'))
+ eq('v:null', eval('String(v:null)'))
+ eq('v:true', funcs.String(true))
+ eq('v:false', funcs.String(false))
+ eq('v:null', funcs.String(NIL))
+ end)
+
+ it('dumps values with at most six digits after the decimal point',
+ function()
+ eq('1.234568e-20', funcs.String(1.23456789123456789123456789e-020))
+ eq('1.234568', funcs.String(1.23456789123456789123456789))
+ end)
+
+ it('dumps values with at most seven digits before the decimal point',
+ function()
+ eq('1234567.891235', funcs.String(1234567.89123456789123456789))
+ eq('1.234568e7', funcs.String(12345678.9123456789123456789))
+ end)
+
+ it('dumps negative values', function()
+ eq('-1.5', funcs.String(-1.5))
+ eq('-1.56e-20', funcs.String(-1.56000e-020))
+ eq('-1.234568e-20', funcs.String(-1.23456789123456789123456789e-020))
+ eq('-1.234568', funcs.String(-1.23456789123456789123456789))
+ eq('-1234567.891235', funcs.String(-1234567.89123456789123456789))
+ eq('-1.234568e7', funcs.String(-12345678.9123456789123456789))
+ end)
+ end)
+
+ describe('used to represent numbers', function()
+ it('dumps regular values', function()
+ eq('0', funcs.String(0))
+ eq('-1', funcs.String(-1))
+ eq('1', funcs.String(1))
+ end)
+
+ it('dumps large values', function()
+ eq('2147483647', funcs.String(2^31-1))
+ eq('-2147483648', funcs.String(-2^31))
+ end)
+ end)
+
+ describe('used to represent strings', function()
+ it('dumps regular strings', function()
+ eq('test', funcs.String('test'))
+ end)
+
+ it('dumps empty strings', function()
+ eq('', funcs.String(''))
+ end)
+
+ it('dumps strings with \' inside', function()
+ eq('\'\'\'', funcs.String('\'\'\''))
+ eq('a\'b\'\'', funcs.String('a\'b\'\''))
+ eq('\'b\'\'d', funcs.String('\'b\'\'d'))
+ eq('a\'b\'c\'d', funcs.String('a\'b\'c\'d'))
+ end)
+
+ it('dumps NULL strings', function()
+ eq('', eval('String($XXX_UNEXISTENT_VAR_XXX)'))
+ end)
+
+ it('dumps NULL lists', function()
+ eq('[]', eval('String(v:_null_list)'))
+ end)
+
+ it('dumps NULL dictionaries', function()
+ eq('{}', eval('String(v:_null_dict)'))
+ end)
+ end)
+
+ describe('used to represent funcrefs', function()
+ before_each(function()
+ source([[
+ function Test1()
+ endfunction
+
+ function s:Test2() dict
+ endfunction
+
+ function g:Test3() dict
+ endfunction
+
+ let g:Test2_f = function('s:Test2')
+ ]])
+ end)
+
+ it('dumps references to built-in functions', function()
+ eq('function', eval('String(function("function"))'))
+ end)
+
+ it('dumps references to user functions', function()
+ eq('Test1', eval('String(function("Test1"))'))
+ eq('g:Test3', eval('String(function("g:Test3"))'))
+ end)
+
+ it('dumps references to script functions', function()
+ eq('<SNR>2_Test2', eval('String(Test2_f)'))
+ end)
+
+ it('dumps partials with self referencing a partial', function()
+ source([[
+ function TestDict() dict
+ endfunction
+ let d = {}
+ let TestDictRef = function('TestDict', d)
+ let d.tdr = TestDictRef
+ ]])
+ eq(dedent([[
+
+ function('TestDict', {'tdr': function('TestDict', {...@1})})
+ function('TestDict', {'tdr': function('TestDict', {...@1})})]]),
+ redir_exec('echo String(d.tdr)'))
+ end)
+
+ it('dumps automatically created partials', function()
+ eq('function(\'<SNR>2_Test2\', {\'f\': function(\'<SNR>2_Test2\')})',
+ eval('String({"f": Test2_f}.f)'))
+ eq('function(\'<SNR>2_Test2\', [1], {\'f\': function(\'<SNR>2_Test2\', [1])})',
+ eval('String({"f": function(Test2_f, [1])}.f)'))
+ end)
+
+ it('dumps manually created partials', function()
+ eq('function(\'Test3\', [1, 2], {})',
+ eval('String(function("Test3", [1, 2], {}))'))
+ eq('function(\'Test3\', {})',
+ eval('String(function("Test3", {}))'))
+ eq('function(\'Test3\', [1, 2])',
+ eval('String(function("Test3", [1, 2]))'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in self',
+ function()
+ meths.set_var('d', {v=true})
+ eq(dedent([[
+
+ {'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}
+ {'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]]),
+ redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
+ end)
+
+ it('does not show errors when dumping partials referencing the same dictionary',
+ function()
+ command('let d = {}')
+ -- Regression for “eval/typval_encode: Dump empty dictionary before
+ -- checking for refcycle”, results in error.
+ eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('String([function("tr", d), function("tr", d)])'))
+ -- Regression for “eval: Work with reference cycles in partials (self)
+ -- properly”, results in crash.
+ eval('extend(d, {"a": 1})')
+ eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('String([function("tr", d), function("tr", d)])'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in arguments',
+ function()
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ -- Regression: the below line used to crash (add returns original list and
+ -- there was error in dumping partials). Tested explicitly in
+ -- test/unit/api/private_helpers_spec.lua.
+ eval('add(l, function("Test1", l))')
+ eq(dedent([=[
+
+ function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])
+ function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])]=]),
+ redir_exec('echo String(function("Test1", l))'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in self and arguments',
+ function()
+ meths.set_var('d', {v=true})
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ eval('add(l, function("Test1", l))')
+ eval('add(l, function("Test1", d))')
+ eq(dedent([=[
+
+ {'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}
+ {'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]=]),
+ redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
+ end)
+ end)
+
+ describe('used to represent lists', function()
+ it('dumps empty list', function()
+ eq('[]', funcs.String({}))
+ end)
+
+ it('dumps nested lists', function()
+ eq('[[[[[]]]]]', funcs.String({{{{{}}}}}))
+ end)
+
+ it('dumps nested non-empty lists', function()
+ eq('[1, [[3, [[5], 4]], 2]]', funcs.String({1, {{3, {{5}, 4}}, 2}}))
+ end)
+
+ it('does not error when dumping recursive lists', function()
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ eq(0, exc_exec('echo String(l)'))
+ end)
+
+ it('dumps recursive lists without error', function()
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ eq('\n[[...@0]]\n[[...@0]]', redir_exec('echo String(l)'))
+ eq('\n[[[...@1]]]\n[[[...@1]]]', redir_exec('echo String([l])'))
+ end)
+ end)
+
+ describe('used to represent dictionaries', function()
+ it('dumps empty dictionary', function()
+ eq('{}', eval('String({})'))
+ end)
+
+ it('dumps list with two same empty dictionaries, also in partials', function()
+ command('let d = {}')
+ eq('[{}, {}]', eval('String([d, d])'))
+ eq('[function(\'tr\', {}), {}]', eval('String([function("tr", d), d])'))
+ eq('[{}, function(\'tr\', {})]', eval('String([d, function("tr", d)])'))
+ end)
+
+ it('dumps non-empty dictionary', function()
+ eq('{\'t\'\'est\': 1}', funcs.String({['t\'est']=1}))
+ end)
+
+ it('does not error when dumping recursive dictionaries', function()
+ meths.set_var('d', {d=1})
+ eval('extend(d, {"d": d})')
+ eq(0, exc_exec('echo String(d)'))
+ end)
+
+ it('dumps recursive dictionaries without the error', function()
+ meths.set_var('d', {d=1})
+ eval('extend(d, {"d": d})')
+ eq('\n{\'d\': {...@0}}\n{\'d\': {...@0}}',
+ redir_exec('echo String(d)'))
+ eq('\n{\'out\': {\'d\': {...@1}}}\n{\'out\': {\'d\': {...@1}}}',
+ redir_exec('echo String({"out": d})'))
+ end)
+ end)
+
+ describe('used to represent special values', function()
+ local function chr(n)
+ return ('%c'):format(n)
+ end
+ local function ctrl(c)
+ return ('%c'):format(c:upper():byte() - 0x40)
+ end
+ it('displays hex as hex', function()
+ -- Regression: due to missing (uint8_t) cast \x80 was represented as
+ -- ~@<80>.
+ eq('<80>', funcs.String(chr(0x80)))
+ eq('<81>', funcs.String(chr(0x81)))
+ eq('<8e>', funcs.String(chr(0x8e)))
+ eq('<c2>', funcs.String(('«'):sub(1, 1)))
+ eq('«', funcs.String(('«'):sub(1, 2)))
+ end)
+ it('displays ASCII control characters using ^X notation', function()
+ eq('^C', funcs.String(ctrl('c')))
+ eq('^A', funcs.String(ctrl('a')))
+ eq('^F', funcs.String(ctrl('f')))
+ end)
+ it('prints CR, NL and tab as-is', function()
+ eq('\n', funcs.String('\n'))
+ eq('\r', funcs.String('\r'))
+ eq('\t', funcs.String('\t'))
+ end)
+ it('prints non-printable UTF-8 in <> notation', function()
+ -- SINGLE SHIFT TWO, unicode control
+ eq('<8e>', funcs.String(funcs.nr2char(0x8E)))
+ -- Surrogate pair: U+1F0A0 PLAYING CARD BACK is represented in UTF-16 as
+ -- 0xD83C 0xDCA0. This is not valid in UTF-8.
+ eq('<d83c>', funcs.String(funcs.nr2char(0xD83C)))
+ eq('<dca0>', funcs.String(funcs.nr2char(0xDCA0)))
+ eq('<d83c><dca0>', funcs.String(funcs.nr2char(0xD83C) .. funcs.nr2char(0xDCA0)))
+ end)
+ end)
+end)
diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua
new file mode 100644
index 0000000000..5d658f10bb
--- /dev/null
+++ b/test/functional/ex_cmds/mksession_spec.lua
@@ -0,0 +1,49 @@
+local lfs = require('lfs')
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local get_pathsep = helpers.get_pathsep
+local eq = helpers.eq
+local funcs = helpers.funcs
+
+local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec'
+
+describe(':mksession', function()
+ local session_file = file_prefix .. '.vim'
+ local tab_dir = file_prefix .. '.d'
+
+ before_each(function()
+ clear()
+ lfs.mkdir(tab_dir)
+ end)
+
+ after_each(function()
+ os.remove(session_file)
+ lfs.rmdir(tab_dir)
+ end)
+
+ it('restores tab-local working directories', function()
+ local tmpfile_base = file_prefix .. '-tmpfile'
+ local cwd_dir = funcs.getcwd()
+
+ -- :mksession does not save empty tabs, so create some buffers.
+ command('edit ' .. tmpfile_base .. '1')
+ command('tabnew')
+ command('edit ' .. tmpfile_base .. '2')
+ command('tcd ' .. tab_dir)
+ command('tabfirst')
+ command('mksession ' .. session_file)
+
+ -- Create a new test instance of Nvim.
+ clear()
+
+ command('source ' .. session_file)
+ -- First tab should have the original working directory.
+ command('tabnext 1')
+ eq(cwd_dir, funcs.getcwd())
+ -- Second tab should have the tab-local working directory.
+ command('tabnext 2')
+ eq(cwd_dir .. get_pathsep() .. tab_dir, funcs.getcwd())
+ end)
+end)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 62b0ce1200..f4b2a8dfdc 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -174,7 +174,7 @@ local os_name = (function()
end)()
local function iswin()
- return os_name() == 'windows'
+ return package.config:sub(1,1) == '\\'
end
-- Executes a VimL function.
@@ -319,7 +319,14 @@ end
-- Dedent the given text and write it to the file name.
local function write_file(name, text, dont_dedent)
local file = io.open(name, 'w')
- if not dont_dedent then
+ if type(text) == 'table' then
+ -- Byte blob
+ local bytes = text
+ text = ''
+ for _, char in ipairs(bytes) do
+ text = ('%s%c'):format(text, char)
+ end
+ elseif not dont_dedent then
text = dedent(text)
end
file:write(text)
@@ -337,11 +344,23 @@ local function read_file(name)
return ret
end
+local sourced_fnames = {}
local function source(code)
local fname = tmpname()
write_file(fname, code)
nvim_command('source '..fname)
- os.remove(fname)
+ -- DO NOT REMOVE FILE HERE.
+ -- do_source() has a habit of checking whether files are “same” by using inode
+ -- and device IDs. If you run two source() calls in quick succession there is
+ -- a good chance that underlying filesystem will reuse the inode, making files
+ -- appear as “symlinks” to do_source when it checks FileIDs. With current
+ -- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse
+ -- inodes, Mac OS machines (again, both QB and travis) do not.
+ --
+ -- Files appearing as “symlinks” mean that both the first and the second
+ -- source() calls will use same SID, which may fail some tests which check for
+ -- exact numbers after `<SNR>` in e.g. function names.
+ sourced_fnames[#sourced_fnames + 1] = fname
return fname
end
@@ -492,17 +511,6 @@ local exc_exec = function(cmd)
return ret
end
-local function redir_exec(cmd)
- nvim_command(([[
- redir => g:__output
- silent! execute "%s"
- redir END
- ]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0')))
- local ret = nvim_eval('get(g:, "__output", 0)')
- nvim_command('unlet! g:__output')
- return ret
-end
-
local function create_callindex(func)
local table = {}
setmetatable(table, {
@@ -562,6 +570,19 @@ local curbufmeths = create_callindex(curbuf)
local curwinmeths = create_callindex(curwin)
local curtabmeths = create_callindex(curtab)
+local function redir_exec(cmd)
+ meths.set_var('__redir_exec_cmd', cmd)
+ nvim_command([[
+ redir => g:__redir_exec_output
+ silent! execute g:__redir_exec_cmd
+ redir END
+ ]])
+ local ret = meths.get_var('__redir_exec_output')
+ meths.del_var('__redir_exec_output')
+ meths.del_var('__redir_exec_cmd')
+ return ret
+end
+
local function get_pathsep()
return funcs.fnamemodify('.', ':p'):sub(-1)
end
@@ -579,6 +600,24 @@ local function missing_provider(provider)
end
end
+local function alter_slashes(obj)
+ if not iswin() then
+ return obj
+ end
+ if type(obj) == 'string' then
+ local ret = obj:gsub('/', '\\')
+ return ret
+ elseif type(obj) == 'table' then
+ local ret = {}
+ for k, v in pairs(obj) do
+ ret[k] = alter_slashes(v)
+ end
+ return ret
+ else
+ assert(false, 'Could only alter slashes for tables of strings and strings')
+ end
+end
+
local module = {
prepend_argv = prepend_argv,
clear = clear,
@@ -647,11 +686,15 @@ local module = {
NIL = mpack.NIL,
get_pathsep = get_pathsep,
missing_provider = missing_provider,
+ alter_slashes = alter_slashes,
}
return function(after_each)
if after_each then
after_each(function()
+ for _, fname in ipairs(sourced_fnames) do
+ os.remove(fname)
+ end
check_logs()
check_cores('build/bin/nvim')
end)
diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua
index d6255d42e7..2f342ec9a3 100644
--- a/test/functional/legacy/assert_spec.lua
+++ b/test/functional/legacy/assert_spec.lua
@@ -255,6 +255,16 @@ describe('assert function:', function()
end)
end)
+ -- assert_report({msg})
+ describe('assert_report()', function()
+ it('should add a message to v:errors', function()
+ command("call assert_report('something is wrong')")
+ command("call assert_match('something is wrong', v:errors[0])")
+ command('call remove(v:errors, 0)')
+ expected_empty()
+ end)
+ end)
+
-- assert_exception({cmd}, [, {error}])
describe('assert_exception()', function()
it('should assert thrown exceptions properly', function()
diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua
new file mode 100644
index 0000000000..5f71861821
--- /dev/null
+++ b/test/functional/legacy/search_spec.lua
@@ -0,0 +1,474 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local eval = helpers.eval
+local feed = helpers.feed
+local funcs = helpers.funcs
+
+describe('search cmdline', function()
+ local screen
+
+ before_each(function()
+ clear()
+ command('set nohlsearch')
+ screen = Screen.new(20, 3)
+ screen:attach()
+ screen:set_default_attr_ids({
+ inc = {reverse = true}
+ })
+ end)
+
+ local function tenlines()
+ funcs.setline(1, {
+ ' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there',
+ ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'
+ })
+ command('1')
+ end
+
+ it('history can be navigated with <C-N>/<C-P>', function()
+ tenlines()
+ command('set noincsearch')
+ feed('/foobar<CR>')
+ feed('/the<CR>')
+ eq('the', eval('@/'))
+ feed('/thes<C-P><C-P><CR>')
+ eq('foobar', eval('@/'))
+ end)
+
+ describe('can traverse matches', function()
+ before_each(tenlines)
+ local function forwarditer(wrapscan)
+ command('set incsearch '..wrapscan)
+ feed('/the')
+ screen:expect([[
+ 1 |
+ 2 {inc:the}se |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 2 these |
+ 3 {inc:the} |
+ /the^ |
+ ]])
+ eq({0, 0, 0, 0}, funcs.getpos('"'))
+ feed('<C-G>')
+ screen:expect([[
+ 3 the |
+ 4 {inc:the}ir |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 4 their |
+ 5 {inc:the}re |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 5 there |
+ 6 {inc:the}ir |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 6 their |
+ 7 {inc:the} |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 7 the |
+ 8 {inc:the}m |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 8 them |
+ 9 {inc:the}se |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ if wrapscan == 'wrapscan' then
+ screen:expect([[
+ 2 {inc:the}se |
+ 3 the |
+ /the^ |
+ ]])
+ else
+ screen:expect([[
+ 8 them |
+ 9 {inc:the}se |
+ /the^ |
+ ]])
+ feed('<CR>')
+ eq({0, 0, 0, 0}, funcs.getpos('"'))
+ end
+ end
+
+ local function backiter(wrapscan)
+ command('set incsearch '..wrapscan)
+ command('$')
+
+ feed('?the')
+ screen:expect([[
+ 9 {inc:the}se |
+ 10 foobar |
+ ?the^ |
+ ]])
+ if wrapscan == 'wrapscan' then
+ feed('<C-G>')
+ screen:expect([[
+ 2 {inc:the}se |
+ 3 the |
+ ?the^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ 2 ^these |
+ 3 the |
+ ?the |
+ ]])
+ else
+ feed('<C-G>')
+ screen:expect([[
+ 9 {inc:the}se |
+ 10 foobar |
+ ?the^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ 9 ^these |
+ 10 foobar |
+ ?the |
+ ]])
+ end
+ command('$')
+ feed('?the')
+ screen:expect([[
+ 9 {inc:the}se |
+ 10 foobar |
+ ?the^ |
+ ]])
+ feed('<C-T>')
+ screen:expect([[
+ 8 {inc:the}m |
+ 9 these |
+ ?the^ |
+ ]])
+ for i = 1, 6 do
+ feed('<C-T>')
+ -- Avoid sleep just before expect, otherwise expect will take the full
+ -- timeout
+ if i ~= 6 then
+ screen:sleep(1)
+ end
+ end
+ screen:expect([[
+ 2 {inc:the}se |
+ 3 the |
+ ?the^ |
+ ]])
+ feed('<C-T>')
+ if wrapscan == 'wrapscan' then
+ screen:expect([[
+ 9 {inc:the}se |
+ 10 foobar |
+ ?the^ |
+ ]])
+ else
+ screen:expect([[
+ 2 {inc:the}se |
+ 3 the |
+ ?the^ |
+ ]])
+ end
+ end
+
+ it("using <C-G> and 'nowrapscan'", function()
+ forwarditer('nowrapscan')
+ end)
+
+ it("using <C-G> and 'wrapscan'", function()
+ forwarditer('wrapscan')
+ end)
+
+ it("using <C-T> and 'nowrapscan'", function()
+ backiter('nowrapscan')
+ end)
+
+ it("using <C-T> and 'wrapscan'", function()
+ backiter('wrapscan')
+ end)
+ end)
+
+ it('expands pattern with <C-L>', function()
+ tenlines()
+ command('set incsearch wrapscan')
+
+ feed('/the')
+ screen:expect([[
+ 1 |
+ 2 {inc:the}se |
+ /the^ |
+ ]])
+ feed('<C-L>')
+ screen:expect([[
+ 1 |
+ 2 {inc:thes}e |
+ /thes^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 9 {inc:thes}e |
+ 10 foobar |
+ /thes^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 2 {inc:thes}e |
+ 3 the |
+ /thes^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ 2 ^these |
+ 3 the |
+ /thes |
+ ]])
+
+ command('1')
+ command('set nowrapscan')
+ feed('/the')
+ screen:expect([[
+ 1 |
+ 2 {inc:the}se |
+ /the^ |
+ ]])
+ feed('<C-L>')
+ screen:expect([[
+ 1 |
+ 2 {inc:thes}e |
+ /thes^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 9 {inc:thes}e |
+ 10 foobar |
+ /thes^ |
+ ]])
+ feed('<C-G><CR>')
+ screen:expect([[
+ 9 ^these |
+ 10 foobar |
+ /thes |
+ ]])
+ end)
+
+ it('reduces pattern with <BS> and keeps cursor position', function()
+ tenlines()
+ command('set incsearch wrapscan')
+
+ -- First match
+ feed('/thei')
+ screen:expect([[
+ 4 {inc:thei}r |
+ 5 there |
+ /thei^ |
+ ]])
+ -- Match from initial cursor position when modifying search
+ feed('<BS>')
+ screen:expect([[
+ 1 |
+ 2 {inc:the}se |
+ /the^ |
+ ]])
+ -- New text advances to next match
+ feed('s')
+ screen:expect([[
+ 1 |
+ 2 {inc:thes}e |
+ /thes^ |
+ ]])
+ -- Stay on this match when deleting a character
+ feed('<BS>')
+ screen:expect([[
+ 1 |
+ 2 {inc:the}se |
+ /the^ |
+ ]])
+ -- Advance to previous match
+ feed('<C-T>')
+ screen:expect([[
+ 9 {inc:the}se |
+ 10 foobar |
+ /the^ |
+ ]])
+ -- Extend search to include next character
+ feed('<C-L>')
+ screen:expect([[
+ 9 {inc:thes}e |
+ 10 foobar |
+ /thes^ |
+ ]])
+ -- Deleting all characters resets the cursor position
+ feed('<BS><BS><BS><BS>')
+ screen:expect([[
+ 1 |
+ 2 these |
+ /^ |
+ ]])
+ feed('the')
+ screen:expect([[
+ 1 |
+ 2 {inc:the}se |
+ /the^ |
+ ]])
+ feed('\\>')
+ screen:expect([[
+ 2 these |
+ 3 {inc:the} |
+ /the\>^ |
+ ]])
+ end)
+
+ it('can traverse matches in the same line with <C-G>/<C-T>', function()
+ funcs.setline(1, { ' 1', ' 2 these', ' 3 the theother' })
+ command('1')
+ command('set incsearch')
+
+ -- First match
+ feed('/the')
+ screen:expect([[
+ 1 |
+ 2 {inc:the}se |
+ /the^ |
+ ]])
+
+ -- Next match, different line
+ feed('<C-G>')
+ screen:expect([[
+ 2 these |
+ 3 {inc:the} theother |
+ /the^ |
+ ]])
+
+ -- Next match, same line
+ feed('<C-G>')
+ screen:expect([[
+ 2 these |
+ 3 the {inc:the}other |
+ /the^ |
+ ]])
+ feed('<C-G>')
+ screen:expect([[
+ 2 these |
+ 3 the theo{inc:the}r |
+ /the^ |
+ ]])
+
+ -- Previous match, same line
+ feed('<C-T>')
+ screen:expect([[
+ 2 these |
+ 3 the {inc:the}other |
+ /the^ |
+ ]])
+ feed('<C-T>')
+ screen:expect([[
+ 2 these |
+ 3 {inc:the} theother |
+ /the^ |
+ ]])
+
+ -- Previous match, different line
+ feed('<C-T>')
+ screen:expect([[
+ 2 {inc:the}se |
+ 3 the theother |
+ /the^ |
+ ]])
+ end)
+
+ it('keeps the view after deleting a char from the search', function()
+ screen:detach()
+ screen = Screen.new(20, 6)
+ screen:attach()
+ screen:set_default_attr_ids({
+ inc = {reverse = true}
+ })
+ screen:set_default_attr_ignore({
+ {bold=true, reverse=true}, {bold=true, foreground=Screen.colors.Blue1}
+ })
+ tenlines()
+
+ feed('/foo')
+ screen:expect([[
+ 6 their |
+ 7 the |
+ 8 them |
+ 9 these |
+ 10 {inc:foo}bar |
+ /foo^ |
+ ]])
+ feed('<BS>')
+ screen:expect([[
+ 6 their |
+ 7 the |
+ 8 them |
+ 9 these |
+ 10 {inc:fo}obar |
+ /fo^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ 6 their |
+ 7 the |
+ 8 them |
+ 9 these |
+ 10 ^foobar |
+ /fo |
+ ]])
+ eq({lnum = 10, leftcol = 0, col = 4, topfill = 0, topline = 6,
+ coladd = 0, skipcol = 0, curswant = 4},
+ funcs.winsaveview())
+ end)
+
+ it('restores original view after failed search', function()
+ screen:detach()
+ screen = Screen.new(40, 3)
+ screen:attach()
+ screen:set_default_attr_ids({
+ inc = {reverse = true},
+ err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
+ more = { bold = true, foreground = Screen.colors.SeaGreen4 },
+ })
+ tenlines()
+ feed('0')
+ feed('/foo')
+ screen:expect([[
+ 9 these |
+ 10 {inc:foo}bar |
+ /foo^ |
+ ]])
+ feed('<C-W>')
+ screen:expect([[
+ 1 |
+ 2 these |
+ /^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ / |
+ {err:E35: No previous regular expression} |
+ {more:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<CR>')
+ eq({lnum = 1, leftcol = 0, col = 0, topfill = 0, topline = 1,
+ coladd = 0, skipcol = 0, curswant = 0},
+ funcs.winsaveview())
+ end)
+end)
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index c8aee130a7..6e1d50071d 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -3,14 +3,17 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
+local neq = helpers.neq
local NIL = helpers.NIL
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 redir_exec = helpers.redir_exec
+local alter_slashes = helpers.alter_slashes
local screen
@@ -173,3 +176,119 @@ describe('debug.debug', function()
]])
end)
end)
+
+describe('package.path/package.cpath', function()
+ local sl = alter_slashes
+
+ local function get_new_paths(sufs, runtimepaths)
+ runtimepaths = runtimepaths or meths.list_runtime_paths()
+ local new_paths = {}
+ local sep = package.config:sub(1, 1)
+ for _, v in ipairs(runtimepaths) do
+ for _, suf in ipairs(sufs) do
+ new_paths[#new_paths + 1] = v .. sep .. 'lua' .. suf
+ end
+ end
+ return new_paths
+ end
+ local function execute_lua(cmd, ...)
+ return meths.execute_lua(cmd, {...})
+ end
+ local function eval_lua(expr, ...)
+ return meths.execute_lua('return ' .. expr, {...})
+ end
+ local function set_path(which, value)
+ return execute_lua('package[select(1, ...)] = select(2, ...)', which, value)
+ end
+
+ it('contains directories from &runtimepath on first invocation', function()
+ local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'})
+ local new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
+
+ local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'})
+ local new_cpaths_str = table.concat(new_cpaths, ';')
+ eq(new_cpaths_str, eval_lua('package.cpath'):sub(1, #new_cpaths_str))
+ end)
+ it('puts directories from &runtimepath always at the start', function()
+ meths.set_option('runtimepath', 'a,b')
+ local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b'})
+ local new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
+
+ set_path('path', sl'foo/?.lua;foo/?/init.lua;' .. new_paths_str)
+
+ neq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
+
+ command('set runtimepath+=c')
+ new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b', 'c'})
+ new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
+ end)
+ it('understands uncommon suffixes', function()
+ set_path('cpath', './?/foo/bar/baz/x.nlua')
+ meths.set_option('runtimepath', 'a')
+ local new_paths = get_new_paths({'/?/foo/bar/baz/x.nlua'}, {'a'})
+ local new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
+
+ set_path('cpath', './yyy?zzz/x')
+ meths.set_option('runtimepath', 'b')
+ new_paths = get_new_paths({'/yyy?zzz/x'}, {'b'})
+ new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
+
+ set_path('cpath', './yyy?zzz/123?ghi/x')
+ meths.set_option('runtimepath', 'b')
+ new_paths = get_new_paths({'/yyy?zzz/123?ghi/x'}, {'b'})
+ new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
+ end)
+ it('preserves empty items', function()
+ local many_empty_path = ';;;;;;'
+ local many_empty_cpath = ';;;;;;./?.luaso'
+ set_path('path', many_empty_path)
+ set_path('cpath', many_empty_cpath)
+ meths.set_option('runtimepath', 'a')
+ local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'})
+ local new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str .. ';' .. many_empty_path, eval_lua('package.path'))
+ local new_cpaths = get_new_paths({'/?.luaso'}, {'a'})
+ local new_cpaths_str = table.concat(new_cpaths, ';')
+ eq(new_cpaths_str .. ';' .. many_empty_cpath, eval_lua('package.cpath'))
+ end)
+ it('preserves empty value', function()
+ set_path('path', '')
+ meths.set_option('runtimepath', 'a')
+ local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'})
+ local new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str .. ';', eval_lua('package.path'))
+ end)
+ it('purges out all additions if runtimepath is set to empty', function()
+ local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'})
+ local new_paths_str = table.concat(new_paths, ';')
+ local path = eval_lua('package.path')
+ eq(new_paths_str, path:sub(1, #new_paths_str))
+
+ local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'})
+ local new_cpaths_str = table.concat(new_cpaths, ';')
+ local cpath = eval_lua('package.cpath')
+ eq(new_cpaths_str, cpath:sub(1, #new_cpaths_str))
+
+ meths.set_option('runtimepath', '')
+ eq(path:sub(#new_paths_str + 2, -1), eval_lua('package.path'))
+ eq(cpath:sub(#new_cpaths_str + 2, -1), eval_lua('package.cpath'))
+ end)
+ it('works with paths with escaped commas', function()
+ meths.set_option('runtimepath', '\\,')
+ local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','})
+ local new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
+ end)
+ it('ignores paths with semicolons', function()
+ meths.set_option('runtimepath', 'foo;bar,\\,')
+ local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','})
+ local new_paths_str = table.concat(new_paths, ';')
+ eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
+ end)
+end)
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index dc73679bb4..b83b7b8eee 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -8,6 +8,8 @@ local clear = helpers.clear
local eval = helpers.eval
local eq = helpers.eq
local neq = helpers.neq
+local mkdir = helpers.mkdir
+local rmdir = helpers.rmdir
local function init_session(...)
local args = { helpers.nvim_prog, '-i', 'NONE', '--embed',
@@ -121,6 +123,56 @@ describe('startup defaults', function()
it('v:progpath is set to the absolute path', function()
eq(eval("fnamemodify(v:progpath, ':p')"), eval('v:progpath'))
end)
+
+ describe('$NVIM_LOG_FILE', function()
+ -- TODO(jkeyes): use stdpath('data') instead.
+ local datasubdir = helpers.iswin() and 'nvim-data' or 'nvim'
+ local xdgdir = 'Xtest-startup-xdg-logpath'
+ local xdgdatadir = xdgdir..'/'..datasubdir
+ after_each(function()
+ os.remove('Xtest-logpath')
+ rmdir(xdgdir)
+ end)
+
+ it('is used if expansion succeeds', function()
+ clear({env={
+ NVIM_LOG_FILE='Xtest-logpath',
+ }})
+ eq('Xtest-logpath', eval('$NVIM_LOG_FILE'))
+ end)
+ it('defaults to stdpath("data")/log if empty', function()
+ eq(true, mkdir(xdgdir) and mkdir(xdgdatadir))
+ clear({env={
+ XDG_DATA_HOME=xdgdir,
+ NVIM_LOG_FILE='', -- Empty is invalid.
+ }})
+ -- server_start() calls ELOG, which tickles log_path_init().
+ pcall(command, 'call serverstart(serverlist()[0])')
+
+ eq(xdgdir..'/'..datasubdir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
+ end)
+ it('defaults to stdpath("data")/log if invalid', function()
+ eq(true, mkdir(xdgdir) and mkdir(xdgdatadir))
+ clear({env={
+ XDG_DATA_HOME=xdgdir,
+ NVIM_LOG_FILE='.', -- Any directory is invalid.
+ }})
+ -- server_start() calls ELOG, which tickles log_path_init().
+ pcall(command, 'call serverstart(serverlist()[0])')
+
+ eq(xdgdir..'/'..datasubdir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
+ end)
+ it('defaults to .nvimlog if stdpath("data") is invalid', function()
+ clear({env={
+ XDG_DATA_HOME='Xtest-missing-xdg-dir',
+ NVIM_LOG_FILE='.', -- Any directory is invalid.
+ }})
+ -- server_start() calls ELOG, which tickles log_path_init().
+ pcall(command, 'call serverstart(serverlist()[0])')
+
+ eq('.nvimlog', eval('$NVIM_LOG_FILE'))
+ end)
+ end)
end)
describe('XDG-based defaults', function()
diff --git a/test/functional/shada/errors_spec.lua b/test/functional/shada/errors_spec.lua
index 2b6b26b433..66c8c4ad2f 100644
--- a/test/functional/shada/errors_spec.lua
+++ b/test/functional/shada/errors_spec.lua
@@ -510,4 +510,22 @@ $
.. '\nE574: Failed to write variable L',
redir_exec('wshada'))
end)
+
+ it('errors with too large items', function()
+ wshada({
+ 1, 206, 70, 90, 31, 179, 86, 133, 169, 103, 101, 110, 101, 114, 97,
+ 116, 111, 114, 196, 4, 145, 145, 145, 145, 145, 145, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 145, 145, 145, 145, 111, 110, 196, 25, 78, 86, 73, 77, 32,
+ 118, 1, 46, 50, 46, 48, 45, 51, 48, 51, 45, 103, 98, 54, 55,
+ 52, 102, 100, 50, 99, 169, 109, 97, 120, 95, 107, 98, 121, 116, 101,
+ 10, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 207,
+ 207, 207, 207, 207, 207, 207, 207, 207, 16, 8, 206, 89, 90, 30, 253,
+ 35, 129, 161, 102, 196, 30, 47, 100, 101, 118, 47, 115, 104, 109, 47,
+ 102, 117, 122, 122, 105, 110, 103, 45, 110, 118, 105, 109, 45, 115, 104,
+ 97, 100, 97, 47, 108, 115, 2, 206, 89, 90, 30, 251, 13, 130, 162,
+ 115, 112, 196, 3, 102, 111, 111, 162, 115, 99, 195, 3, 146, 10, 0,
+ })
+ eq('Vim(rshada):E576: Error while reading ShaDa file: there is an item at position 93 that is stated to be too long', exc_exec(sdrcmd()))
+ end)
end)
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 4fd00cc320..d1357ea525 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -5,6 +5,7 @@ local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local command = helpers.command
local eval, exc_exec = helpers.eval, helpers.exc_exec
local feed_command, request, eq = helpers.feed_command, helpers.request, helpers.eq
+local curbufmeths = helpers.curbufmeths
describe('colorscheme compatibility', function()
before_each(function()
@@ -670,12 +671,26 @@ describe("'winhighlight' highlight", function()
[8] = {background = Screen.colors.DarkMagenta, bold = true, foreground = Screen.colors.Blue1},
[9] = {foreground = Screen.colors.Brown},
[10] = {foreground = Screen.colors.Brown, background = Screen.colors.DarkBlue},
+ [11] = {background = Screen.colors.DarkBlue, bold = true, reverse = true},
+ [12] = {background = Screen.colors.DarkGreen, reverse = true},
+ [13] = {background = Screen.colors.Magenta4, reverse = true},
+ [14] = {background = Screen.colors.DarkBlue, reverse = true},
+ [15] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [16] = {foreground = Screen.colors.Blue1},
+ [17] = {background = Screen.colors.LightRed},
+ [18] = {background = Screen.colors.Gray90},
+ [19] = {foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray},
+ [20] = {background = Screen.colors.LightGrey, underline = true},
+ [21] = {bold = true},
+ [22] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [23] = {background = Screen.colors.LightMagenta},
+ [24] = {background = Screen.colors.WebGray},
})
command("hi Background1 guibg=DarkBlue")
command("hi Background2 guibg=DarkGreen")
end)
- it('works', function()
+ it('works for background color', function()
insert("aa")
command("split")
command("set winhl=Normal:Background1")
@@ -683,7 +698,7 @@ describe("'winhighlight' highlight", function()
{1:a^a }|
{2:~ }|
{2:~ }|
- {3:[No Name] [+] }|
+ {11:[No Name] [+] }|
aa |
{0:~ }|
{4:[No Name] [+] }|
@@ -695,7 +710,7 @@ describe("'winhighlight' highlight", function()
{1:^ }|
{2:~ }|
{2:~ }|
- {3:[No Name] }|
+ {11:[No Name] }|
aa |
{0:~ }|
{4:[No Name] [+] }|
@@ -740,7 +755,7 @@ describe("'winhighlight' highlight", function()
{1:a^a }|
{2:~ }|
{2:~ }|
- {3:[No Name] [+] }|
+ {11:[No Name] [+] }|
aa |
{0:~ }|
{4:[No Name] [+] }|
@@ -764,16 +779,15 @@ describe("'winhighlight' highlight", function()
{1:^aa }|
{2:~ }|
{2:~ }|
- {3:[No Name] [+] }|
+ {11:[No Name] [+] }|
aa |
{0:~ }|
{4:[No Name] [+] }|
<f 1 --100%-- col 1 |
]])
-
end)
- it('for inactive window works', function()
+ it('for inactive window background works', function()
command("set winhl=Normal:Background1,NormalNC:Background2")
-- tests global value is copied across split
command("split")
@@ -781,10 +795,10 @@ describe("'winhighlight' highlight", function()
{1:^ }|
{2:~ }|
{2:~ }|
- {3:[No Name] }|
+ {11:[No Name] }|
{5: }|
{6:~ }|
- {4:[No Name] }|
+ {12:[No Name] }|
|
]])
@@ -793,10 +807,10 @@ describe("'winhighlight' highlight", function()
{5: }|
{6:~ }|
{6:~ }|
- {4:[No Name] }|
+ {12:[No Name] }|
{1:^ }|
{2:~ }|
- {3:[No Name] }|
+ {11:[No Name] }|
|
]])
@@ -805,13 +819,12 @@ describe("'winhighlight' highlight", function()
{1:^ }|
{2:~ }|
{2:~ }|
- {3:[No Name] }|
+ {11:[No Name] }|
{5: }|
{6:~ }|
- {4:[No Name] }|
+ {12:[No Name] }|
|
]])
-
end)
it('works with NormalNC', function()
@@ -825,7 +838,7 @@ describe("'winhighlight' highlight", function()
{3:[No Name] }|
{7: }|
{8:~ }|
- {4:[No Name] }|
+ {13:[No Name] }|
|
]])
@@ -834,7 +847,7 @@ describe("'winhighlight' highlight", function()
{7: }|
{8:~ }|
{8:~ }|
- {4:[No Name] }|
+ {13:[No Name] }|
^ |
{0:~ }|
{3:[No Name] }|
@@ -848,10 +861,10 @@ describe("'winhighlight' highlight", function()
{7: }|
{8:~ }|
{8:~ }|
- {4:[No Name] }|
+ {13:[No Name] }|
{1:^ }|
{2:~ }|
- {3:[No Name] }|
+ {11:[No Name] }|
|
]])
@@ -863,7 +876,7 @@ describe("'winhighlight' highlight", function()
{3:[No Name] }|
{1: }|
{2:~ }|
- {4:[No Name] }|
+ {14:[No Name] }|
|
]])
@@ -873,10 +886,10 @@ describe("'winhighlight' highlight", function()
{7: }|
{8:~ }|
{8:~ }|
- {4:[No Name] }|
+ {13:[No Name] }|
{1:^ }|
{2:~ }|
- {3:[No Name] }|
+ {11:[No Name] }|
|
]])
@@ -888,13 +901,12 @@ describe("'winhighlight' highlight", function()
{3:[No Name] }|
{5: }|
{6:~ }|
- {4:[No Name] }|
+ {12:[No Name] }|
|
]])
-
end)
- it('applies also to non-text', function()
+ it('background applies also to non-text', function()
insert('Lorem ipsum dolor sit amet ')
command('set shiftwidth=2')
feed('>>')
@@ -940,6 +952,114 @@ describe("'winhighlight' highlight", function()
|
]])
end)
-end)
+ it('can override NonText, Conceal and EndOfBuffer', function()
+ curbufmeths.set_lines(0,-1,true, {"raa\000"})
+ command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})')
+ command('set cole=2 cocu=nvic')
+ command('split')
+ command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})')
+ command('set winhl=SpecialKey:ErrorMsg,EndOfBuffer:Background1,'
+ ..'Conceal:Background2')
+
+ screen:expect([[
+ ^r{5:#}a{15:^@} |
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] [+] }|
+ r{19:#}a{16:^@} |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('can override LineNr, CursorColumn and ColorColumn', function()
+ insert('very text\nmore text')
+ command('set number')
+ command('set colorcolumn=2')
+ command('set cursorcolumn')
+
+ command('split')
+ command('set winhl=LineNr:Background1,CursorColumn:Background2,'
+ ..'ColorColumn:ErrorMsg')
+ screen:expect([[
+ {1: 1 }v{15:e}ry tex{5:t} |
+ {1: 2 }m{15:o}re tex^t |
+ {0:~ }|
+ {3:[No Name] [+] }|
+ {9: 1 }v{17:e}ry tex{18:t} |
+ {9: 2 }m{17:o}re text |
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('can override Tabline', function()
+ command('tabnew')
+ command('set winhl=TabLine:Background1,TabLineSel:ErrorMsg')
+
+ screen:expect([[
+ {20: No Name] }{15: No Name]}{20:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command("tabnext")
+ screen:expect([[
+ {21: No Name] }{1: No Name]}{20:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('can override popupmenu', function()
+ insert('word wording wordy')
+ command('split')
+ command('set winhl=Pmenu:Background1,PmenuSel:Background2,'
+ ..'PmenuSbar:ErrorMsg,PmenuThumb:Normal')
+ screen:expect([[
+ word wording word^y |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] [+] }|
+ word wording wordy |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ feed('oword<c-x><c-p>')
+ screen:expect([[
+ word wording wordy |
+ wordy^ |
+ {1:word }{0: }|
+ {1:wording }{3: }|
+ {5:wordy }rdy |
+ wordy |
+ {4:[No Name] [+] }|
+ {21:-- }{22:match 1 of 3} |
+ ]])
+
+ feed('<esc>u<c-w><c-w>oword<c-x><c-p>')
+ screen:expect([[
+ word wording wordy |
+ wordy |
+ {23:word }{0: }|
+ {23:wording }{4: }|
+ {24:wordy }rdy |
+ wordy^ |
+ {3:[No Name] [+] }|
+ {21:-- }{22:match 1 of 3} |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 7d9cd6c026..5408e1e195 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -198,8 +198,9 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any)
condition = expected
expected = nil
else
- -- Remove the last line and dedent.
- expected = dedent(expected:gsub('\n[ ]+$', ''))
+ -- Remove the last line and dedent. Note that gsub returns more then one
+ -- value.
+ expected = dedent(expected:gsub('\n[ ]+$', ''), 0)
for row in expected:gmatch('[^\n]+') do
row = row:sub(1, #row - 1) -- Last char must be the screen delimiter.
table.insert(expected_rows, row)
diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua
index 776e760aaf..0cf92f7d40 100644
--- a/test/functional/viml/function_spec.lua
+++ b/test/functional/viml/function_spec.lua
@@ -1,29 +1,221 @@
local helpers = require('test.functional.helpers')(after_each)
-local clear = helpers.clear
local eq = helpers.eq
-local exc_exec = helpers.exc_exec
+local clear = helpers.clear
+local funcs = helpers.funcs
+local dedent = helpers.dedent
+local redir_exec = helpers.redir_exec
-describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
- local max_func_args = 20 -- from eval.h
- local range = helpers.funcs.range
+before_each(clear)
- before_each(clear)
+local function check_nofunc(fname)
+ eq(0, funcs.exists('*' .. fname))
+end
- it('printf()', function()
- local printf = helpers.funcs.printf
- local rep = helpers.funcs['repeat']
- local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'
- eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args))))
- local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
- eq('Vim(call):E740: Too many arguments for function printf', ret)
- end)
+local function check_func(fname, body, indent)
+ if type(body) == 'number' then
+ body = ('return %i'):format(body)
+ end
+ eq(dedent(([[
- it('rpcnotify()', function()
- local rpcnotify = helpers.funcs.rpcnotify
- local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args)))
- eq(1, ret)
- ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
- eq('Vim(call):E740: Too many arguments for function rpcnotify', ret)
+ function %s()%s
+ endfunction]]
+ ), 3):format(
+ fname,
+ body and ('\n1' .. (' '):rep(2 + (indent or 8)) .. body) or ''),
+ redir_exec('function ' .. fname))
+end
+
+describe(':endfunction', function()
+ it('accepts bang', function()
+ eq('', redir_exec([[
+ function F()
+ endfunction!
+ ]]))
+ check_func('F')
+ eq('', redir_exec([[
+ function! F()
+ return 1
+ endfunction!
+ ]]))
+ check_func('F', 1)
+ end)
+ it('accepts comments', function()
+ eq('', redir_exec([[
+ function F1()
+ endfunction " Comment
+ ]]))
+ check_func('F1')
+ eq('', redir_exec([[
+ function F2()
+ endfunction " }}}
+ ]]))
+ check_func('F2')
+ eq('', redir_exec([[
+ function F3()
+ endfunction " F3
+ ]]))
+ check_func('F3')
+ eq('', redir_exec([[
+ function F4()
+ endfunction! " F4
+ ]]))
+ check_func('F4')
+ eq('', redir_exec([[
+ function! F4()
+ return 2
+ endfunction! " F4
+ ]]))
+ check_func('F4', 2)
+ end)
+ it('accepts function name', function()
+ eq('', redir_exec([[
+ function F0()
+ endfunction F0
+ ]]))
+ check_func('F0')
+ eq('', redir_exec([[
+ function F1()
+ endfunction! F1
+ ]]))
+ check_func('F1')
+ eq('', redir_exec([[
+ function! F2()
+ endfunction! F2
+ ]]))
+ check_func('F2')
+ eq('', redir_exec([[
+ function! F2()
+ return 3
+ endfunction! F2
+ ]]))
+ check_func('F2', 3)
+ end)
+ it('accepts weird characters', function()
+ eq('', redir_exec([[
+ function F1()
+ endfunction: }}}
+ ]]))
+ check_func('F1')
+ -- From accurev
+ eq('', redir_exec([[
+ function F2()
+ endfunction :}}}
+ ]]))
+ check_func('F2')
+ -- From cream-vimabbrev
+ eq('', redir_exec([[
+ function F3()
+ endfunction 1}}}
+ ]]))
+ check_func('F3')
+ -- From pyunit
+ eq('', redir_exec([[
+ function F4()
+ endfunction # }}}
+ ]]))
+ check_func('F4')
+ -- From vim-lldb
+ eq('', redir_exec([[
+ function F5()
+ endfunction()
+ ]]))
+ check_func('F5')
+ -- From vim-mail
+ eq('', redir_exec([[
+ function F6()
+ endfunction;
+ ]]))
+ check_func('F6')
+ end)
+ it('accepts commented bar', function()
+ eq('', redir_exec([[
+ function F1()
+ endfunction " F1 | echo 42
+ ]]))
+ check_func('F1')
+ eq('', redir_exec([[
+ function! F1()
+ return 42
+ endfunction! " F1 | echo 42
+ ]]))
+ check_func('F1', 42)
+ end)
+ it('errors out on an uncommented bar', function()
+ eq('\nE488: Trailing characters: | echo 42', redir_exec([[
+ function F1()
+ endfunction | echo 42
+ ]]))
+ check_nofunc('F1')
+ end)
+ it('allows running multiple commands', function()
+ eq('\n2', redir_exec([[
+ function F1()
+ echo 2
+ endfunction
+ call F1()
+ ]]))
+ check_func('F1', 'echo 2')
+ eq('\n2\n3\n4', redir_exec([[
+ function F2()
+ echo 2
+ endfunction F2
+ function F3()
+ echo 3
+ endfunction " F3
+ function! F4()
+ echo 4
+ endfunction!
+ call F2()
+ call F3()
+ call F4()
+ ]]))
+ check_func('F2', 'echo 2')
+ check_func('F3', 'echo 3')
+ check_func('F4', 'echo 4')
+ end)
+ it('allows running multiple commands with only one character in between',
+ function()
+ eq('\n3', redir_exec(dedent([[
+ function! F1()
+ echo 3
+ endfunction!
+ call F1()]])))
+ check_func('F1', 'echo 3', 2)
+ eq('\n4', redir_exec(dedent([[
+ function F5()
+ echo 4
+ endfunction
+ call F5()]])))
+ check_func('F5', 'echo 4', 2)
+ eq('\n5', redir_exec(dedent([[
+ function F6()
+ echo 5
+ endfunction " TEST
+ call F6()]])))
+ check_func('F6', 'echo 5', 2)
+ eq('\n6', redir_exec(dedent([[
+ function F7()
+ echo 6
+ endfunction F7
+ call F7()]])))
+ check_func('F7', 'echo 6', 2)
+ eq('\n2\n3\n4', redir_exec(dedent([[
+ function F2()
+ echo 2
+ endfunction F2
+ function F3()
+ echo 3
+ endfunction " F3
+ function! F4()
+ echo 4
+ endfunction!
+ call F2()
+ call F3()
+ call F4()]])))
+ check_func('F2', 'echo 2', 2)
+ check_func('F3', 'echo 3', 2)
+ check_func('F4', 'echo 4', 2)
end)
end)
+-- vim: foldmarker=▶,▲