aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rw-r--r--test/functional/api/vim_spec.lua46
-rw-r--r--test/functional/eval/ctx_functions_spec.lua2
-rw-r--r--test/functional/ex_cmds/echo_spec.lua167
-rw-r--r--test/functional/ex_cmds/ls_spec.lua12
-rw-r--r--test/functional/ex_cmds/mksession_spec.lua26
-rw-r--r--test/functional/ex_cmds/profile_spec.lua76
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua (renamed from test/functional/fixtures/lsp-test-rpc-server.lua)37
-rw-r--r--test/functional/helpers.lua6
-rw-r--r--test/functional/legacy/021_control_wi_spec.lua2
-rw-r--r--test/functional/legacy/097_glob_path_spec.lua2
-rw-r--r--test/functional/legacy/eval_spec.lua4
-rw-r--r--test/functional/legacy/expand_spec.lua39
-rw-r--r--test/functional/legacy/prompt_buffer_spec.lua153
-rw-r--r--test/functional/lua/buffer_updates_spec.lua17
-rw-r--r--test/functional/lua/treesitter_spec.lua126
-rw-r--r--test/functional/lua/uri_spec.lua20
-rw-r--r--test/functional/lua/vim_spec.lua261
-rw-r--r--test/functional/normal/put_spec.lua4
-rw-r--r--test/functional/options/defaults_spec.lua33
-rw-r--r--test/functional/options/num_options_spec.lua20
-rw-r--r--test/functional/plugin/lsp_spec.lua547
-rw-r--r--test/functional/terminal/buffer_spec.lua12
-rw-r--r--test/functional/terminal/mouse_spec.lua34
-rw-r--r--test/functional/ui/float_spec.lua22
-rw-r--r--test/functional/ui/fold_spec.lua74
-rw-r--r--test/functional/ui/highlight_spec.lua42
-rw-r--r--test/functional/ui/inccommand_spec.lua72
-rw-r--r--test/functional/ui/messages_spec.lua87
-rw-r--r--test/functional/ui/multibyte_spec.lua34
-rw-r--r--test/functional/ui/multigrid_spec.lua187
-rw-r--r--test/functional/ui/options_spec.lua14
-rw-r--r--test/functional/ui/popupmenu_spec.lua136
-rw-r--r--test/functional/ui/screen.lua67
-rw-r--r--test/functional/ui/wildmode_spec.lua38
34 files changed, 2168 insertions, 251 deletions
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index d901a5e2eb..2e9d0f57ac 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -23,6 +23,7 @@ local pcall_err = helpers.pcall_err
local format_string = helpers.format_string
local intchar2lua = helpers.intchar2lua
local mergedicts_copy = helpers.mergedicts_copy
+local endswith = helpers.endswith
describe('API', function()
before_each(clear)
@@ -570,6 +571,28 @@ describe('API', function()
eq({0,7,1,0}, funcs.getpos('.'))
eq(false, nvim('get_option', 'paste'))
end)
+ it('Replace-mode', function()
+ -- Within single line
+ nvim('put', {'aabbccdd', 'eeffgghh', 'iijjkkll'}, "c", true, false)
+ command('normal l')
+ command('startreplace')
+ nvim('paste', '123456', true, -1)
+ expect([[
+ a123456d
+ eeffgghh
+ iijjkkll]])
+ command('%delete _')
+ -- Across lines
+ nvim('put', {'aabbccdd', 'eeffgghh', 'iijjkkll'}, "c", true, false)
+ command('normal l')
+ command('startreplace')
+ nvim('paste', '123\n456', true, -1)
+ expect([[
+ a123
+ 456d
+ eeffgghh
+ iijjkkll]])
+ end)
it('crlf=false does not break lines at CR, CRLF', function()
nvim('paste', 'line 1\r\n\r\rline 2\nline 3\rline 4\r', false, -1)
expect('line 1\r\n\r\rline 2\nline 3\rline 4\r')
@@ -1853,4 +1876,27 @@ describe('API', function()
command('silent! call nvim_create_buf(0, 1)')
end)
end)
+
+ describe('nvim_get_runtime_file', function()
+ it('works', function()
+ eq({}, meths.get_runtime_file("bork.borkbork", false))
+ eq({}, meths.get_runtime_file("bork.borkbork", true))
+ eq(1, #meths.get_runtime_file("autoload/msgpack.vim", false))
+ eq(1, #meths.get_runtime_file("autoload/msgpack.vim", true))
+ local val = meths.get_runtime_file("autoload/remote/*.vim", true)
+ eq(2, #val)
+ local p = helpers.alter_slashes
+ if endswith(val[1], "define.vim") then
+ ok(endswith(val[1], p("autoload/remote/define.vim")))
+ ok(endswith(val[2], p("autoload/remote/host.vim")))
+ else
+ ok(endswith(val[1], p("autoload/remote/host.vim")))
+ ok(endswith(val[2], p("autoload/remote/define.vim")))
+ end
+ val = meths.get_runtime_file("autoload/remote/*.vim", false)
+ eq(1, #val)
+ ok(endswith(val[1], p("autoload/remote/define.vim"))
+ or endswith(val[1], p("autoload/remote/host.vim")))
+ end)
+ end)
end)
diff --git a/test/functional/eval/ctx_functions_spec.lua b/test/functional/eval/ctx_functions_spec.lua
index c81dad9645..f23adbc556 100644
--- a/test/functional/eval/ctx_functions_spec.lua
+++ b/test/functional/eval/ctx_functions_spec.lua
@@ -6,7 +6,7 @@ local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
local feed = helpers.feed
-local map = helpers.map
+local map = helpers.tbl_map
local nvim = helpers.nvim
local parse_context = helpers.parse_context
local redir_exec = helpers.redir_exec
diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua
index 10c7230896..408ce52b8c 100644
--- a/test/functional/ex_cmds/echo_spec.lua
+++ b/test/functional/ex_cmds/echo_spec.lua
@@ -11,31 +11,57 @@ local dedent = helpers.dedent
local command = helpers.command
local exc_exec = helpers.exc_exec
local redir_exec = helpers.redir_exec
+local matches = helpers.matches
+
+describe(':echo :echon :echomsg :echoerr', function()
+ local fn_tbl = {'String', 'StringN', 'StringMsg', 'StringErr'}
+ local function assert_same_echo_dump(expected, input, use_eval)
+ for _,v in pairs(fn_tbl) do
+ eq(expected, use_eval and eval(v..'('..input..')') or funcs[v](input))
+ end
+ end
+ local function assert_matches_echo_dump(expected, input, use_eval)
+ for _,v in pairs(fn_tbl) do
+ matches(expected, use_eval and eval(v..'('..input..')') or funcs[v](input))
+ end
+ end
-describe(':echo', function()
before_each(function()
clear()
source([[
function String(s)
return execute('echo a:s')[1:]
endfunction
+ function StringMsg(s)
+ return execute('echomsg a:s')[1:]
+ endfunction
+ function StringN(s)
+ return execute('echon a:s')
+ endfunction
+ function StringErr(s)
+ try
+ execute 'echoerr a:s'
+ catch
+ return substitute(v:exception, '^Vim(echoerr):', '', '')
+ endtry
+ endfunction
]])
end)
describe('used to represent floating-point values', function()
it('dumps NaN values', function()
- eq('str2float(\'nan\')', eval('String(str2float(\'nan\'))'))
+ assert_same_echo_dump("str2float('nan')", "str2float('nan')", true)
end)
it('dumps infinite values', function()
- eq('str2float(\'inf\')', eval('String(str2float(\'inf\'))'))
- eq('-str2float(\'inf\')', eval('String(str2float(\'-inf\'))'))
+ assert_same_echo_dump("str2float('inf')", "str2float('inf')", true)
+ assert_same_echo_dump("-str2float('inf')", "str2float('-inf')", true)
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)'))
+ assert_same_echo_dump('1.5', 1.5)
+ assert_same_echo_dump('1.56e-20', 1.56000e-020)
+ assert_same_echo_dump('0.0', '0.0', true)
end)
it('dumps special v: values', function()
@@ -45,69 +71,81 @@ describe(':echo', function()
eq('v:true', funcs.String(true))
eq('v:false', funcs.String(false))
eq('v:null', funcs.String(NIL))
+ eq('true', eval('StringMsg(v:true)'))
+ eq('false', eval('StringMsg(v:false)'))
+ eq('null', eval('StringMsg(v:null)'))
+ eq('true', funcs.StringMsg(true))
+ eq('false', funcs.StringMsg(false))
+ eq('null', funcs.StringMsg(NIL))
+ eq('true', eval('StringErr(v:true)'))
+ eq('false', eval('StringErr(v:false)'))
+ eq('null', eval('StringErr(v:null)'))
+ eq('true', funcs.StringErr(true))
+ eq('false', funcs.StringErr(false))
+ eq('null', funcs.StringErr(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))
+ assert_same_echo_dump('1.234568e-20', 1.23456789123456789123456789e-020)
+ assert_same_echo_dump('1.234568', 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))
+ assert_same_echo_dump('1234567.891235', 1234567.89123456789123456789)
+ assert_same_echo_dump('1.234568e7', 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))
+ assert_same_echo_dump('-1.5', -1.5)
+ assert_same_echo_dump('-1.56e-20', -1.56000e-020)
+ assert_same_echo_dump('-1.234568e-20', -1.23456789123456789123456789e-020)
+ assert_same_echo_dump('-1.234568', -1.23456789123456789123456789)
+ assert_same_echo_dump('-1234567.891235', -1234567.89123456789123456789)
+ assert_same_echo_dump('-1.234568e7', -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))
+ assert_same_echo_dump('0', 0)
+ assert_same_echo_dump('-1', -1)
+ assert_same_echo_dump('1', 1)
end)
it('dumps large values', function()
- eq('2147483647', funcs.String(2^31-1))
- eq('-2147483648', funcs.String(-2^31))
+ assert_same_echo_dump('2147483647', 2^31-1)
+ assert_same_echo_dump('-2147483648', -2^31)
end)
end)
describe('used to represent strings', function()
it('dumps regular strings', function()
- eq('test', funcs.String('test'))
+ assert_same_echo_dump('test', 'test')
end)
it('dumps empty strings', function()
- eq('', funcs.String(''))
+ assert_same_echo_dump('', '')
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'))
+ it("dumps strings with ' inside", function()
+ assert_same_echo_dump("'''", "'''")
+ assert_same_echo_dump("a'b''", "a'b''")
+ assert_same_echo_dump("'b''d", "'b''d")
+ assert_same_echo_dump("a'b'c'd", "a'b'c'd")
end)
it('dumps NULL strings', function()
- eq('', eval('String($XXX_UNEXISTENT_VAR_XXX)'))
+ assert_same_echo_dump('', '$XXX_UNEXISTENT_VAR_XXX', true)
end)
it('dumps NULL lists', function()
- eq('[]', eval('String(v:_null_list)'))
+ assert_same_echo_dump('[]', 'v:_null_list', true)
end)
it('dumps NULL dictionaries', function()
- eq('{}', eval('String(v:_null_dict)'))
+ assert_same_echo_dump('{}', 'v:_null_dict', true)
end)
end)
@@ -129,15 +167,27 @@ describe(':echo', function()
it('dumps references to built-in functions', function()
eq('function', eval('String(function("function"))'))
+ eq("function('function')", eval('StringMsg(function("function"))'))
+ eq("function('function')", eval('StringErr(function("function"))'))
end)
it('dumps references to user functions', function()
eq('Test1', eval('String(function("Test1"))'))
eq('g:Test3', eval('String(function("g:Test3"))'))
+ eq("function('Test1')", eval("StringMsg(function('Test1'))"))
+ eq("function('g:Test3')", eval("StringMsg(function('g:Test3'))"))
+ eq("function('Test1')", eval("StringErr(function('Test1'))"))
+ eq("function('g:Test3')", eval("StringErr(function('g:Test3'))"))
end)
it('dumps references to script functions', function()
eq('<SNR>2_Test2', eval('String(Test2_f)'))
+ eq("function('<SNR>2_Test2')", eval('StringMsg(Test2_f)'))
+ eq("function('<SNR>2_Test2')", eval('StringErr(Test2_f)'))
+ end)
+
+ it('dump references to lambdas', function()
+ assert_matches_echo_dump("function%('<lambda>%d+'%)", '{-> 1234}', true)
end)
it('dumps partials with self referencing a partial', function()
@@ -156,19 +206,23 @@ describe(':echo', function()
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)'))
+ assert_same_echo_dump(
+ "function('<SNR>2_Test2', {'f': function('<SNR>2_Test2')})",
+ '{"f": Test2_f}.f',
+ true)
+ assert_same_echo_dump(
+ "function('<SNR>2_Test2', [1], {'f': function('<SNR>2_Test2', [1])})",
+ '{"f": function(Test2_f, [1])}.f',
+ true)
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]))'))
+ assert_same_echo_dump("function('Test3', [1, 2], {})",
+ "function('Test3', [1, 2], {})", true)
+ assert_same_echo_dump("function('Test3', [1, 2])",
+ "function('Test3', [1, 2])", true)
+ assert_same_echo_dump("function('Test3', {})",
+ "function('Test3', {})", true)
end)
it('does not crash or halt when dumping partials with reference cycles in self',
@@ -225,15 +279,19 @@ describe(':echo', function()
describe('used to represent lists', function()
it('dumps empty list', function()
- eq('[]', funcs.String({}))
+ assert_same_echo_dump('[]', {})
+ end)
+
+ it('dumps non-empty list', function()
+ assert_same_echo_dump('[1, 2]', {1,2})
end)
it('dumps nested lists', function()
- eq('[[[[[]]]]]', funcs.String({{{{{}}}}}))
+ assert_same_echo_dump('[[[[[]]]]]', {{{{{}}}}})
end)
it('dumps nested non-empty lists', function()
- eq('[1, [[3, [[5], 4]], 2]]', funcs.String({1, {{3, {{5}, 4}}, 2}}))
+ assert_same_echo_dump('[1, [[3, [[5], 4]], 2]]', {1, {{3, {{5}, 4}}, 2}})
end)
it('does not error when dumping recursive lists', function()
@@ -252,18 +310,18 @@ describe(':echo', function()
describe('used to represent dictionaries', function()
it('dumps empty dictionary', function()
- eq('{}', eval('String({})'))
+ assert_same_echo_dump('{}', '{}', true)
end)
it('dumps list with two same empty dictionaries, also in partials', function()
command('let d = {}')
- eq('[{}, {}]', eval('String([d, d])'))
+ assert_same_echo_dump('[{}, {}]', '[d, d]', true)
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}))
+ assert_same_echo_dump("{'t''est': 1}", {["t'est"]=1})
end)
it('does not error when dumping recursive dictionaries', function()
@@ -297,11 +355,20 @@ describe(':echo', function()
eq('<8e>', funcs.String(chr(0x8e)))
eq('<c2>', funcs.String(('«'):sub(1, 1)))
eq('«', funcs.String(('«'):sub(1, 2)))
+
+ eq('<80>', funcs.StringMsg(chr(0x80)))
+ eq('<81>', funcs.StringMsg(chr(0x81)))
+ eq('<8e>', funcs.StringMsg(chr(0x8e)))
+ eq('<c2>', funcs.StringMsg(('«'):sub(1, 1)))
+ eq('«', funcs.StringMsg(('«'):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')))
+ eq('^C', funcs.StringMsg(ctrl('c')))
+ eq('^A', funcs.StringMsg(ctrl('a')))
+ eq('^F', funcs.StringMsg(ctrl('f')))
end)
it('prints CR, NL and tab as-is', function()
eq('\n', funcs.String('\n'))
@@ -311,11 +378,15 @@ describe(':echo', function()
it('prints non-printable UTF-8 in <> notation', function()
-- SINGLE SHIFT TWO, unicode control
eq('<8e>', funcs.String(funcs.nr2char(0x8E)))
+ eq('<8e>', funcs.StringMsg(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)))
+ eq('<d83c>', funcs.StringMsg(funcs.nr2char(0xD83C)))
+ eq('<dca0>', funcs.StringMsg(funcs.nr2char(0xDCA0)))
+ eq('<d83c><dca0>', funcs.StringMsg(funcs.nr2char(0xD83C) .. funcs.nr2char(0xDCA0)))
end)
end)
end)
diff --git a/test/functional/ex_cmds/ls_spec.lua b/test/functional/ex_cmds/ls_spec.lua
index f7bacd7386..9853084c47 100644
--- a/test/functional/ex_cmds/ls_spec.lua
+++ b/test/functional/ex_cmds/ls_spec.lua
@@ -31,6 +31,18 @@ describe(':ls', function()
-- Terminal buffer [F]inished.
eq('\n 3 %aF', string.match(ls_output, '\n *3....'))
end)
+
+ retry(nil, 5000, function()
+ local ls_output = eval('execute("ls R")')
+ -- Just the [R]unning terminal buffer.
+ eq('\n 2 #aR ', string.match(ls_output, '^\n *2 ... '))
+ end)
+
+ retry(nil, 5000, function()
+ local ls_output = eval('execute("ls F")')
+ -- Just the [F]inished terminal buffer.
+ eq('\n 3 %aF ', string.match(ls_output, '^\n *3 ... '))
+ end)
end)
end)
diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua
index 855f8105aa..949724bb53 100644
--- a/test/functional/ex_cmds/mksession_spec.lua
+++ b/test/functional/ex_cmds/mksession_spec.lua
@@ -26,6 +26,27 @@ describe(':mksession', function()
rmdir(tab_dir)
end)
+ it('restores same :terminal buf in splits', function()
+ -- If the same :terminal is displayed in multiple windows, :mksession
+ -- should restore it as such.
+
+ -- Create two windows showing the same :terminal buffer.
+ command('terminal')
+ command('split')
+ command('terminal')
+ command('split')
+ command('mksession '..session_file)
+
+ -- Create a new test instance of Nvim.
+ command('qall!')
+ clear()
+ -- Restore session.
+ command('source '..session_file)
+
+ eq({3,3,2},
+ {funcs.winbufnr(1), funcs.winbufnr(2), funcs.winbufnr(3)})
+ end)
+
it('restores tab-local working directories', function()
local tmpfile_base = file_prefix .. '-tmpfile'
local cwd_dir = funcs.getcwd()
@@ -75,7 +96,8 @@ describe(':mksession', function()
it('restores CWD for :terminal buffers #11288', function()
local cwd_dir = funcs.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '')
- local session_path = cwd_dir..get_pathsep()..session_file
+ cwd_dir = cwd_dir:gsub([[\]], '/') -- :mksession always uses unix slashes.
+ local session_path = cwd_dir..'/'..session_file
command('cd '..tab_dir)
command('terminal echo $PWD')
@@ -87,7 +109,7 @@ describe(':mksession', function()
clear()
command('silent source '..session_path)
- local expected_cwd = cwd_dir..get_pathsep()..tab_dir
+ local expected_cwd = cwd_dir..'/'..tab_dir
matches('^term://'..pesc(expected_cwd)..'//%d+:', funcs.expand('%'))
command('qall!')
end)
diff --git a/test/functional/ex_cmds/profile_spec.lua b/test/functional/ex_cmds/profile_spec.lua
index f185db192a..2b92f8d0de 100644
--- a/test/functional/ex_cmds/profile_spec.lua
+++ b/test/functional/ex_cmds/profile_spec.lua
@@ -6,6 +6,9 @@ local eval = helpers.eval
local command = helpers.command
local eq, neq = helpers.eq, helpers.neq
local tempfile = helpers.tmpname()
+local source = helpers.source
+local matches = helpers.matches
+local read_file = helpers.read_file
-- tmpname() also creates the file on POSIX systems. Remove it again.
-- We just need the name, ignoring any race conditions.
@@ -32,20 +35,67 @@ describe(':profile', function()
end
end)
- it('dump', function()
- eq(0, eval('v:profiling'))
- command('profile start ' .. tempfile)
- eq(1, eval('v:profiling'))
- assert_file_exists_not(tempfile)
- command('profile dump')
- assert_file_exists(tempfile)
+ describe('dump', function()
+ it('works', function()
+ eq(0, eval('v:profiling'))
+ command('profile start ' .. tempfile)
+ eq(1, eval('v:profiling'))
+ assert_file_exists_not(tempfile)
+ command('profile dump')
+ assert_file_exists(tempfile)
+ end)
+
+ it('not resetting the profile', function()
+ source([[
+ function! Test()
+ endfunction
+ ]])
+ command('profile start ' .. tempfile)
+ assert_file_exists_not(tempfile)
+ command('profile func Test')
+ command('call Test()')
+ command('profile dump')
+ assert_file_exists(tempfile)
+ local profile = read_file(tempfile)
+ matches('Called 1 time', profile)
+ command('call Test()')
+ command('profile dump')
+ assert_file_exists(tempfile)
+ profile = read_file(tempfile)
+ matches('Called 2 time', profile)
+ command('profile stop')
+ end)
end)
- it('stop', function()
- command('profile start ' .. tempfile)
- assert_file_exists_not(tempfile)
- command('profile stop')
- assert_file_exists(tempfile)
- eq(0, eval('v:profiling'))
+ describe('stop', function()
+ it('works', function()
+ command('profile start ' .. tempfile)
+ assert_file_exists_not(tempfile)
+ command('profile stop')
+ assert_file_exists(tempfile)
+ eq(0, eval('v:profiling'))
+ end)
+
+ it('resetting the profile', function()
+ source([[
+ function! Test()
+ endfunction
+ ]])
+ command('profile start ' .. tempfile)
+ assert_file_exists_not(tempfile)
+ command('profile func Test')
+ command('call Test()')
+ command('profile stop')
+ assert_file_exists(tempfile)
+ local profile = read_file(tempfile)
+ matches('Called 1 time', profile)
+ command('profile start ' .. tempfile)
+ command('profile func Test')
+ command('call Test()')
+ command('profile stop')
+ assert_file_exists(tempfile)
+ profile = read_file(tempfile)
+ matches('Called 1 time', profile)
+ end)
end)
end)
diff --git a/test/functional/fixtures/lsp-test-rpc-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index 798883ced0..dca7f35923 100644
--- a/test/functional/fixtures/lsp-test-rpc-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -1,23 +1,14 @@
local protocol = require 'vim.lsp.protocol'
--- Internal utility methods.
--- TODO replace with a better implementation.
-local function json_encode(data)
- local status, result = pcall(vim.fn.json_encode, data)
- if status then
- return result
- else
- return nil, result
- end
-end
-local function json_decode(data)
- local status, result = pcall(vim.fn.json_decode, data)
- if status then
- return result
- else
- return nil, result
- end
+-- Logs to $NVIM_LOG_FILE.
+--
+-- TODO(justinmk): remove after https://github.com/neovim/neovim/pull/7062
+local function log(loglevel, area, msg)
+ vim.fn.writefile(
+ {string.format('%s %s: %s', loglevel, area, msg)},
+ vim.env.NVIM_LOG_FILE,
+ 'a')
end
local function message_parts(sep, ...)
@@ -49,16 +40,14 @@ local function format_message_with_content_length(encoded_message)
}
end
--- Server utility methods.
-
local function read_message()
local line = io.read("*l")
local length = line:lower():match("content%-length:%s*(%d+)")
- return assert(json_decode(io.read(2 + length):sub(2)), "read_message.json_decode")
+ return vim.fn.json_decode(io.read(2 + length):sub(2))
end
local function send(payload)
- io.stdout:write(format_message_with_content_length(json_encode(payload)))
+ io.stdout:write(format_message_with_content_length(vim.fn.json_encode(payload)))
end
local function respond(id, err, result)
@@ -390,7 +379,7 @@ function tests.basic_check_buffer_open_and_change_incremental()
}
end
-function tests.basic_check_buffer_open_and_change_incremental_editting()
+function tests.basic_check_buffer_open_and_change_incremental_editing()
skeleton {
on_init = function(params)
local expected_capabilities = protocol.make_client_capabilities()
@@ -443,6 +432,7 @@ local kill_timer = vim.loop.new_timer()
kill_timer:start(_G.TIMEOUT or 1e3, 0, function()
kill_timer:stop()
kill_timer:close()
+ log('ERROR', 'LSP', 'TIMEOUT')
io.stderr:write("TIMEOUT")
os.exit(100)
end)
@@ -453,7 +443,8 @@ local status, err = pcall(assert(tests[test_name], "Test not found"))
kill_timer:stop()
kill_timer:close()
if not status then
+ log('ERROR', 'LSP', tostring(err))
io.stderr:write(err)
- os.exit(1)
+ os.exit(101)
end
os.exit(0)
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 3ffc6137d6..e8435cd3b7 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -15,9 +15,9 @@ local check_cores = global_helpers.check_cores
local check_logs = global_helpers.check_logs
local dedent = global_helpers.dedent
local eq = global_helpers.eq
-local filter = global_helpers.filter
+local filter = global_helpers.tbl_filter
local is_os = global_helpers.is_os
-local map = global_helpers.map
+local map = global_helpers.tbl_map
local ok = global_helpers.ok
local sleep = global_helpers.sleep
local tbl_contains = global_helpers.tbl_contains
@@ -794,7 +794,7 @@ function module.alter_slashes(obj)
end
return ret
else
- assert(false, 'Could only alter slashes for tables of strings and strings')
+ assert(false, 'expected string or table of strings, got '..type(obj))
end
end
diff --git a/test/functional/legacy/021_control_wi_spec.lua b/test/functional/legacy/021_control_wi_spec.lua
index 87d9deed7a..94871433cd 100644
--- a/test/functional/legacy/021_control_wi_spec.lua
+++ b/test/functional/legacy/021_control_wi_spec.lua
@@ -18,7 +18,7 @@ describe('CTRL-W CTRL-I', function()
start found wrong line
test text]])
- -- Search for the second occurence of start and append to register
+ -- Search for the second occurrence of start and append to register
feed_command('/start')
feed('2[<C-i>')
feed_command('yank A')
diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua
index ccd93fed60..dd5a26ad3b 100644
--- a/test/functional/legacy/097_glob_path_spec.lua
+++ b/test/functional/legacy/097_glob_path_spec.lua
@@ -52,7 +52,7 @@ describe('glob() and globpath()', function()
command([[$put =glob('Xxx\{')]])
command([[$put =glob('Xxx\$')]])
- command('silent w! Xxx{')
+ command('silent w! Xxx\\{')
command([[w! Xxx\$]])
command([[$put =glob('Xxx\{')]])
command([[$put =glob('Xxx\$')]])
diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua
index c5d38d6d05..4198ea8bfe 100644
--- a/test/functional/legacy/eval_spec.lua
+++ b/test/functional/legacy/eval_spec.lua
@@ -360,7 +360,7 @@ describe('eval', function()
abcD3b]])
-- From now on we delete the buffer contents after each expect() to make
- -- the next expect() easier to write. This is neccessary because null
+ -- the next expect() easier to write. This is necessary because null
-- bytes on a line by itself don't play well together with the dedent
-- function used in expect().
command('%delete')
@@ -416,7 +416,7 @@ describe('eval', function()
' abcD3b50')
end)
- -- The tests for setting lists with NLs are split into seperate it() blocks
+ -- The tests for setting lists with NLs are split into separate it() blocks
-- to make the expect() calls easier to write. Otherwise the null byte can
-- make trouble on a line on its own.
it('setting lists with NLs with setreg(), part 1', function()
diff --git a/test/functional/legacy/expand_spec.lua b/test/functional/legacy/expand_spec.lua
index 1b735080f4..f238128b31 100644
--- a/test/functional/legacy/expand_spec.lua
+++ b/test/functional/legacy/expand_spec.lua
@@ -70,6 +70,40 @@ describe('expand file name', function()
call assert_match('\~', expand('%:p'))
bwipe!
endfunc
+
+ func Test_expandcmd()
+ let $FOO = 'Test'
+ call assert_equal('e x/Test/y', expandcmd('e x/$FOO/y'))
+ unlet $FOO
+
+ new
+ edit Xfile1
+ call assert_equal('e Xfile1', expandcmd('e %'))
+ edit Xfile2
+ edit Xfile1
+ call assert_equal('e Xfile2', expandcmd('e #'))
+ edit Xfile2
+ edit Xfile3
+ edit Xfile4
+ let bnum = bufnr('Xfile2')
+ call assert_equal('e Xfile2', expandcmd('e #' . bnum))
+ call setline('.', 'Vim!@#')
+ call assert_equal('e Vim', expandcmd('e <cword>'))
+ call assert_equal('e Vim!@#', expandcmd('e <cWORD>'))
+ enew!
+ edit Xfile.java
+ call assert_equal('e Xfile.py', expandcmd('e %:r.py'))
+ call assert_equal('make abc.java', expandcmd('make abc.%:e'))
+ call assert_equal('make Xabc.java', expandcmd('make %:s?file?abc?'))
+ edit a1a2a3.rb
+ call assert_equal('make b1b2b3.rb a1a2a3 Xfile.o', expandcmd('make %:gs?a?b? %< #<.o'))
+
+ call assert_fails('call expandcmd("make <afile>")', 'E495:')
+ call assert_fails('call expandcmd("make <afile>")', 'E495:')
+ enew
+ call assert_fails('call expandcmd("make %")', 'E499:')
+ close
+ endfunc
]])
end)
@@ -87,4 +121,9 @@ describe('expand file name', function()
call('Test_expand_tilde_filename')
expected_empty()
end)
+
+ it('works with expandcmd()', function()
+ call('Test_expandcmd')
+ expected_empty()
+ end)
end)
diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua
new file mode 100644
index 0000000000..513be807be
--- /dev/null
+++ b/test/functional/legacy/prompt_buffer_spec.lua
@@ -0,0 +1,153 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local feed= helpers.feed
+local source = helpers.source
+local clear = helpers.clear
+local feed_command = helpers.feed_command
+
+describe('prompt buffer', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 10)
+ screen:attach()
+ source([[
+ func TextEntered(text)
+ if a:text == "exit"
+ set nomodified
+ stopinsert
+ close
+ else
+ call append(line("$") - 1, 'Command: "' . a:text . '"')
+ set nomodfied
+ call timer_start(20, {id -> TimerFunc(a:text)})
+ endif
+ endfunc
+
+ func TimerFunc(text)
+ call append(line("$") - 1, 'Result: "' . a:text .'"')
+ endfunc
+ ]])
+ feed_command("set noshowmode | set laststatus=0")
+ feed_command("call setline(1, 'other buffer')")
+ feed_command("new")
+ feed_command("set buftype=prompt")
+ feed_command("call prompt_setcallback(bufnr(''), function('TextEntered'))")
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('works', function()
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ [Prompt] |
+ other buffer |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed("i")
+ feed("hello\n")
+ screen:expect([[
+ % hello |
+ Command: "hello" |
+ Result: "hello" |
+ % ^ |
+ [Prompt] [+] |
+ other buffer |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed("exit\n")
+ screen:expect([[
+ ^other buffer |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+
+ it('editing', function()
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ [Prompt] |
+ other buffer |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed("i")
+ feed("hello<BS><BS>")
+ screen:expect([[
+ % hel^ |
+ ~ |
+ ~ |
+ ~ |
+ [Prompt] [+] |
+ other buffer |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed("<Left><Left><Left><BS>-")
+ screen:expect([[
+ % -^hel |
+ ~ |
+ ~ |
+ ~ |
+ [Prompt] [+] |
+ other buffer |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed("<End>x")
+ screen:expect([[
+ % -helx^ |
+ ~ |
+ ~ |
+ ~ |
+ [Prompt] [+] |
+ other buffer |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ feed("<C-U>exit\n")
+ screen:expect([[
+ ^other buffer |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+
+end)
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index 990cb97fec..77f8189bb9 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -98,7 +98,7 @@ describe('lua: buffer event callbacks', function()
command('undo')
-- plugins can opt in to receive changedtick events, or choose
- -- to only recieve actual changes.
+ -- to only receive actual changes.
check_events({{ "test1", "lines", 1, tick, 3, 4, 5, 13 },
{ "test2", "lines", 1, tick, 3, 4, 5, 13 },
{ "test2", "changedtick", 1, tick+1 } })
@@ -111,7 +111,7 @@ describe('lua: buffer event callbacks', function()
tick = tick + 1
-- plugins can opt in to receive changedtick events, or choose
- -- to only recieve actual changes.
+ -- to only receive actual changes.
check_events({{ "test1", "lines", 1, tick, 6, 7, 9, 16 },
{ "test2", "lines", 1, tick, 6, 7, 9, 16 }})
@@ -203,4 +203,17 @@ describe('lua: buffer event callbacks', function()
{ "test1", "lines", 1, tick+1, 5, 6, 5, 27, 20, 20 }}, exec_lua("return get_events(...)" ))
end)
+ it('has valid cursor position while shifting', function()
+ meths.buf_set_lines(0, 0, -1, true, {'line1'})
+ exec_lua([[
+ vim.api.nvim_buf_attach(0, false, {
+ on_lines = function()
+ vim.api.nvim_set_var('listener_cursor_line', vim.api.nvim_win_get_cursor(0)[1])
+ end,
+ })
+ ]])
+ feed('>>')
+ eq(1, meths.get_var('listener_cursor_line'))
+ end)
+
end)
diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua
index 76e9899d34..f93185d1f6 100644
--- a/test/functional/lua/treesitter_spec.lua
+++ b/test/functional/lua/treesitter_spec.lua
@@ -6,7 +6,6 @@ local clear = helpers.clear
local eq = helpers.eq
local insert = helpers.insert
local exec_lua = helpers.exec_lua
-local iswin = helpers.iswin
local feed = helpers.feed
local pcall_err = helpers.pcall_err
local matches = helpers.matches
@@ -16,37 +15,35 @@ before_each(clear)
describe('treesitter API', function()
-- error tests not requiring a parser library
it('handles missing language', function()
- eq('Error executing lua: .../treesitter.lua: no such language: borklang',
+ eq("Error executing lua: .../treesitter.lua: no parser for 'borklang' language",
pcall_err(exec_lua, "parser = vim.treesitter.create_parser(0, 'borklang')"))
-- actual message depends on platform
- matches('Error executing lua: Failed to load parser: uv_dlopen: .+',
- pcall_err(exec_lua, "parser = vim.treesitter.add_language('borkbork.so', 'borklang')"))
+ matches("Error executing lua: Failed to load parser: uv_dlopen: .+",
+ pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')"))
- eq('Error executing lua: [string "<nvim>"]:1: no such language: borklang',
+ eq("Error executing lua: .../treesitter.lua: no parser for 'borklang' language",
pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')"))
end)
end)
describe('treesitter API with C parser', function()
- local ts_path = os.getenv("TREE_SITTER_DIR")
-
- -- The tests after this requires an actual parser
- if ts_path == nil then
- it("works", function() pending("TREE_SITTER_PATH not set, skipping treesitter parser tests") end)
- return
+ local function check_parser()
+ local status, msg = unpack(exec_lua([[ return {pcall(vim.treesitter.require_language, 'c')} ]]))
+ if not status then
+ if helpers.isCI() then
+ error("treesitter C parser not found, required on CI: " .. msg)
+ else
+ pending('no C parser, skipping')
+ end
+ end
+ return status
end
- before_each(function()
- local path = ts_path .. '/bin/c'..(iswin() and '.dll' or '.so')
- exec_lua([[
- local path = ...
- vim.treesitter.add_language(path,'c')
- ]], path)
- end)
-
it('parses buffer', function()
+ if not check_parser() then return end
+
insert([[
int main() {
int x = 3;
@@ -138,6 +135,8 @@ void ui_refresh(void)
]]
it('support query and iter by capture', function()
+ if not check_parser() then return end
+
insert(test_text)
local res = exec_lua([[
@@ -167,6 +166,8 @@ void ui_refresh(void)
end)
it('support query and iter by match', function()
+ if not check_parser() then return end
+
insert(test_text)
local res = exec_lua([[
@@ -198,6 +199,8 @@ void ui_refresh(void)
end)
it('supports highlighting', function()
+ if not check_parser() then return end
+
local hl_text = [[
/// Schedule Lua callback on main loop's event queue
static int nlua_schedule(lua_State *const lstate)
@@ -242,6 +245,11 @@ static int nlua_schedule(lua_State *const lstate)
(primitive_type) @type
(sized_type_specifier) @type
+; defaults to very magic syntax, for best compatibility
+((identifier) @Identifier (match? @Identifier "^l(u)a_"))
+; still support \M etc prefixes
+((identifier) @Constant (match? @Constant "\M^\[A-Z_]\+$"))
+
((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (eq? @WarningMsg.left @WarningMsg.right))
(comment) @comment
@@ -260,7 +268,7 @@ static int nlua_schedule(lua_State *const lstate)
[8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
[9] = {foreground = Screen.colors.Magenta, background = Screen.colors.Red},
[10] = {foreground = Screen.colors.Red, background = Screen.colors.Red},
-
+ [11] = {foreground = Screen.colors.Cyan4},
})
insert(hl_text)
@@ -294,10 +302,10 @@ static int nlua_schedule(lua_State *const lstate)
{2:/// Schedule Lua callback on main loop's event queue} |
{3:static} {3:int} nlua_schedule(lua_State *{3:const} lstate) |
{ |
- {4:if} (lua_type(lstate, {5:1}) != LUA_TFUNCTION |
+ {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
|| {6:lstate} != {6:lstate}) { |
- lua_pushliteral(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} lua_error(lstate); |
+ {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
+ {4:return} {11:lua_error}(lstate); |
} |
|
{7:LuaRef} cb = nlua_ref(lstate, {5:1}); |
@@ -316,10 +324,10 @@ static int nlua_schedule(lua_State *const lstate)
{2:/// Schedule Lua callback on main loop's event queue} |
{3:static} {3:int} nlua_schedule(lua_State *{3:const} lstate) |
{ |
- {4:if} (lua_type(lstate, {5:1}) != LUA_TFUNCTION |
+ {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
|| {6:lstate} != {6:lstate}) { |
- lua_pushliteral(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} lua_error(lstate); |
+ {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
+ {4:return} {11:lua_error}(lstate); |
{8:*^/} |
} |
|
@@ -357,41 +365,43 @@ static int nlua_schedule(lua_State *const lstate)
end)
it('inspects language', function()
- local keys, fields, symbols = unpack(exec_lua([[
- local lang = vim.treesitter.inspect_language('c')
- local keys, symbols = {}, {}
- for k,_ in pairs(lang) do
- keys[k] = true
- end
+ if not check_parser() then return end
- -- symbols array can have "holes" and is thus not a valid msgpack array
- -- but we don't care about the numbers here (checked in the parser test)
- for _, v in pairs(lang.symbols) do
- table.insert(symbols, v)
- end
- return {keys, lang.fields, symbols}
- ]]))
-
- eq({fields=true, symbols=true}, keys)
+ local keys, fields, symbols = unpack(exec_lua([[
+ local lang = vim.treesitter.inspect_language('c')
+ local keys, symbols = {}, {}
+ for k,_ in pairs(lang) do
+ keys[k] = true
+ end
- local fset = {}
- for _,f in pairs(fields) do
- eq("string", type(f))
- fset[f] = true
+ -- symbols array can have "holes" and is thus not a valid msgpack array
+ -- but we don't care about the numbers here (checked in the parser test)
+ for _, v in pairs(lang.symbols) do
+ table.insert(symbols, v)
end
- eq(true, fset["directive"])
- eq(true, fset["initializer"])
-
- local has_named, has_anonymous
- for _,s in pairs(symbols) do
- eq("string", type(s[1]))
- eq("boolean", type(s[2]))
- if s[1] == "for_statement" and s[2] == true then
- has_named = true
- elseif s[1] == "|=" and s[2] == false then
- has_anonymous = true
- end
+ return {keys, lang.fields, symbols}
+ ]]))
+
+ eq({fields=true, symbols=true}, keys)
+
+ local fset = {}
+ for _,f in pairs(fields) do
+ eq("string", type(f))
+ fset[f] = true
+ end
+ eq(true, fset["directive"])
+ eq(true, fset["initializer"])
+
+ local has_named, has_anonymous
+ for _,s in pairs(symbols) do
+ eq("string", type(s[1]))
+ eq("boolean", type(s[2]))
+ if s[1] == "for_statement" and s[2] == true then
+ has_named = true
+ elseif s[1] == "|=" and s[2] == false then
+ has_anonymous = true
end
- eq({true,true}, {has_named,has_anonymous})
+ end
+ eq({true,true}, {has_named,has_anonymous})
end)
end)
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
index 19b1eb1f61..a3b8e685e1 100644
--- a/test/functional/lua/uri_spec.lua
+++ b/test/functional/lua/uri_spec.lua
@@ -85,6 +85,15 @@ describe('URI methods', function()
eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
end)
+ it('file path includes only ascii charactors with encoded colon character', function()
+ local test_case = [[
+ local uri = 'file:///C%3A/Foo/Bar/Baz.txt'
+ return vim.uri_to_fname(uri)
+ ]]
+
+ eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
+ end)
+
it('file path including white space', function()
local test_case = [[
local uri = 'file:///C:/Foo%20/Bar/Baz.txt'
@@ -104,4 +113,15 @@ describe('URI methods', function()
end)
end)
end)
+
+ describe('uri to bufnr', function()
+ it('uri_to_bufnr & uri_from_bufnr returns original uri for non-file uris', function()
+ local uri = 'jdt://contents/java.base/java.util/List.class?=sql/%5C/home%5C/user%5C/.jabba%5C/jdk%5C/openjdk%5C@1.14.0%5C/lib%5C/jrt-fs.jar%60java.base=/javadoc_location=/https:%5C/%5C/docs.oracle.com%5C/en%5C/java%5C/javase%5C/14%5C/docs%5C/api%5C/=/%3Cjava.util(List.class'
+ local test_case = string.format([[
+ local uri = '%s'
+ return vim.uri_from_bufnr(vim.uri_to_bufnr(uri))
+ ]], uri)
+ eq(uri, exec_lua(test_case))
+ end)
+ end)
end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index e879f8b925..79d523b5c6 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -7,6 +7,7 @@ local meths = helpers.meths
local command = helpers.command
local clear = helpers.clear
local eq = helpers.eq
+local ok = helpers.ok
local eval = helpers.eval
local feed = helpers.feed
local pcall_err = helpers.pcall_err
@@ -310,18 +311,52 @@ describe('lua stdlib', function()
end)
it("vim.deepcopy", function()
- local is_dc = exec_lua([[
+ ok(exec_lua([[
local a = { x = { 1, 2 }, y = 5}
local b = vim.deepcopy(a)
- local count = 0
- for _ in pairs(b) do count = count + 1 end
-
- return b.x[1] == 1 and b.x[2] == 2 and b.y == 5 and count == 2
+ return b.x[1] == 1 and b.x[2] == 2 and b.y == 5 and vim.tbl_count(b) == 2
and tostring(a) ~= tostring(b)
- ]])
+ ]]))
+
+ ok(exec_lua([[
+ local a = {}
+ local b = vim.deepcopy(a)
+
+ return vim.tbl_islist(b) and vim.tbl_count(b) == 0 and tostring(a) ~= tostring(b)
+ ]]))
+
+ ok(exec_lua([[
+ local a = vim.empty_dict()
+ local b = vim.deepcopy(a)
+
+ return not vim.tbl_islist(b) and vim.tbl_count(b) == 0
+ ]]))
+
+ ok(exec_lua([[
+ local a = {x = vim.empty_dict(), y = {}}
+ local b = vim.deepcopy(a)
+
+ return not vim.tbl_islist(b.x) and vim.tbl_islist(b.y)
+ and vim.tbl_count(b) == 2
+ and tostring(a) ~= tostring(b)
+ ]]))
- assert(is_dc)
+ ok(exec_lua([[
+ local f1 = function() return 1 end
+ local f2 = function() return 2 end
+ local t1 = {f = f1}
+ local t2 = vim.deepcopy(t1)
+ t1.f = f2
+ return t1.f() ~= t2.f()
+ ]]))
+
+ eq('Error executing lua: .../shared.lua: Cannot deepcopy object of type thread',
+ pcall_err(exec_lua, [[
+ local thread = coroutine.create(function () return 0 end)
+ local t = {thr = thread}
+ vim.deepcopy(t)
+ ]]))
end)
it('vim.pesc', function()
@@ -353,6 +388,30 @@ describe('lua stdlib', function()
end
end)
+ it('vim.tbl_map', function()
+ eq({}, exec_lua([[
+ return vim.tbl_map(function(v) return v * 2 end, {})
+ ]]))
+ eq({2, 4, 6}, exec_lua([[
+ return vim.tbl_map(function(v) return v * 2 end, {1, 2, 3})
+ ]]))
+ eq({{i=2}, {i=4}, {i=6}}, exec_lua([[
+ return vim.tbl_map(function(v) return { i = v.i * 2 } end, {{i=1}, {i=2}, {i=3}})
+ ]]))
+ end)
+
+ it('vim.tbl_filter', function()
+ eq({}, exec_lua([[
+ return vim.tbl_filter(function(v) return (v % 2) == 0 end, {})
+ ]]))
+ eq({2}, exec_lua([[
+ return vim.tbl_filter(function(v) return (v % 2) == 0 end, {1, 2, 3})
+ ]]))
+ eq({{i=2}}, exec_lua([[
+ return vim.tbl_filter(function(v) return (v.i % 2) == 0 end, {{i=1}, {i=2}, {i=3}})
+ ]]))
+ end)
+
it('vim.tbl_islist', function()
eq(true, exec_lua("return vim.tbl_islist({})"))
eq(false, exec_lua("return vim.tbl_islist(vim.empty_dict())"))
@@ -369,6 +428,88 @@ describe('lua stdlib', function()
eq(false, exec_lua("return vim.tbl_isempty({a=1, b=2, c=3})"))
end)
+ it('vim.tbl_extend', function()
+ ok(exec_lua([[
+ local a = {x = 1}
+ local b = {y = 2}
+ local c = vim.tbl_extend("keep", a, b)
+
+ return c.x == 1 and b.y == 2 and vim.tbl_count(c) == 2
+ ]]))
+
+ ok(exec_lua([[
+ local a = {x = 1}
+ local b = {y = 2}
+ local c = {z = 3}
+ local d = vim.tbl_extend("keep", a, b, c)
+
+ return d.x == 1 and d.y == 2 and d.z == 3 and vim.tbl_count(d) == 3
+ ]]))
+
+ ok(exec_lua([[
+ local a = {x = 1}
+ local b = {x = 3}
+ local c = vim.tbl_extend("keep", a, b)
+
+ return c.x == 1 and vim.tbl_count(c) == 1
+ ]]))
+
+ ok(exec_lua([[
+ local a = {x = 1}
+ local b = {x = 3}
+ local c = vim.tbl_extend("force", a, b)
+
+ return c.x == 3 and vim.tbl_count(c) == 1
+ ]]))
+
+ ok(exec_lua([[
+ local a = vim.empty_dict()
+ local b = {}
+ local c = vim.tbl_extend("keep", a, b)
+
+ return not vim.tbl_islist(c) and vim.tbl_count(c) == 0
+ ]]))
+
+ ok(exec_lua([[
+ local a = {}
+ local b = vim.empty_dict()
+ local c = vim.tbl_extend("keep", a, b)
+
+ return vim.tbl_islist(c) and vim.tbl_count(c) == 0
+ ]]))
+
+ eq('Error executing lua: .../shared.lua: invalid "behavior": nil',
+ pcall_err(exec_lua, [[
+ return vim.tbl_extend()
+ ]])
+ )
+
+ eq('Error executing lua: .../shared.lua: wrong number of arguments (given 1, expected at least 3)',
+ pcall_err(exec_lua, [[
+ return vim.tbl_extend("keep")
+ ]])
+ )
+
+ eq('Error executing lua: .../shared.lua: wrong number of arguments (given 2, expected at least 3)',
+ pcall_err(exec_lua, [[
+ return vim.tbl_extend("keep", {})
+ ]])
+ )
+ end)
+
+ it('vim.tbl_count', function()
+ eq(0, exec_lua [[ return vim.tbl_count({}) ]])
+ eq(0, exec_lua [[ return vim.tbl_count(vim.empty_dict()) ]])
+ eq(0, exec_lua [[ return vim.tbl_count({nil}) ]])
+ eq(0, exec_lua [[ return vim.tbl_count({a=nil}) ]])
+ eq(1, exec_lua [[ return vim.tbl_count({1}) ]])
+ eq(2, exec_lua [[ return vim.tbl_count({1, 2}) ]])
+ eq(2, exec_lua [[ return vim.tbl_count({1, nil, 3}) ]])
+ eq(1, exec_lua [[ return vim.tbl_count({a=1}) ]])
+ eq(2, exec_lua [[ return vim.tbl_count({a=1, b=2}) ]])
+ eq(2, exec_lua [[ return vim.tbl_count({a=1, b=nil, c=3}) ]])
+ end)
+
it('vim.deep_equal', function()
eq(true, exec_lua [[ return vim.deep_equal({a=1}, {a=1}) ]])
eq(true, exec_lua [[ return vim.deep_equal({a={b=1}}, {a={b=1}}) ]])
@@ -545,6 +686,8 @@ describe('lua stdlib', function()
]]))
eq("{ {}, vim.empty_dict() }", exec_lua("return vim.inspect({{}, vim.empty_dict()})"))
+ eq('{}', exec_lua([[ return vim.fn.json_encode(vim.empty_dict()) ]]))
+ eq('{"a": {}, "b": []}', exec_lua([[ return vim.fn.json_encode({a=vim.empty_dict(), b={}}) ]]))
end)
it('vim.validate', function()
@@ -626,10 +769,96 @@ describe('lua stdlib', function()
exec_lua [[
vim.api.nvim_set_var("testing", "hi")
vim.api.nvim_set_var("other", 123)
+ vim.api.nvim_set_var("to_delete", {hello="world"})
]]
+
eq('hi', funcs.luaeval "vim.g.testing")
eq(123, funcs.luaeval "vim.g.other")
eq(NIL, funcs.luaeval "vim.g.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
+ exec_lua [[
+ vim.g.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.g.to_delete")
+ end)
+
+ it('vim.b', function()
+ exec_lua [[
+ vim.api.nvim_buf_set_var(0, "testing", "hi")
+ vim.api.nvim_buf_set_var(0, "other", 123)
+ vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"})
+ ]]
+
+ eq('hi', funcs.luaeval "vim.b.testing")
+ eq(123, funcs.luaeval "vim.b.other")
+ eq(NIL, funcs.luaeval "vim.b.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.b.to_delete")
+ exec_lua [[
+ vim.b.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.b.to_delete")
+
+ exec_lua [[
+ vim.cmd "vnew"
+ ]]
+
+ eq(NIL, funcs.luaeval "vim.b.testing")
+ eq(NIL, funcs.luaeval "vim.b.other")
+ eq(NIL, funcs.luaeval "vim.b.nonexistant")
+ end)
+
+ it('vim.w', function()
+ exec_lua [[
+ vim.api.nvim_win_set_var(0, "testing", "hi")
+ vim.api.nvim_win_set_var(0, "other", 123)
+ vim.api.nvim_win_set_var(0, "to_delete", {hello="world"})
+ ]]
+
+ eq('hi', funcs.luaeval "vim.w.testing")
+ eq(123, funcs.luaeval "vim.w.other")
+ eq(NIL, funcs.luaeval "vim.w.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.w.to_delete")
+ exec_lua [[
+ vim.w.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.w.to_delete")
+
+ exec_lua [[
+ vim.cmd "vnew"
+ ]]
+
+ eq(NIL, funcs.luaeval "vim.w.testing")
+ eq(NIL, funcs.luaeval "vim.w.other")
+ eq(NIL, funcs.luaeval "vim.w.nonexistant")
+ end)
+
+ it('vim.t', function()
+ exec_lua [[
+ vim.api.nvim_tabpage_set_var(0, "testing", "hi")
+ vim.api.nvim_tabpage_set_var(0, "other", 123)
+ vim.api.nvim_tabpage_set_var(0, "to_delete", {hello="world"})
+ ]]
+
+ eq('hi', funcs.luaeval "vim.t.testing")
+ eq(123, funcs.luaeval "vim.t.other")
+ eq(NIL, funcs.luaeval "vim.t.nonexistant")
+
+ eq({hello="world"}, funcs.luaeval "vim.t.to_delete")
+ exec_lua [[
+ vim.t.to_delete = nil
+ ]]
+ eq(NIL, funcs.luaeval "vim.t.to_delete")
+
+ exec_lua [[
+ vim.cmd "tabnew"
+ ]]
+
+ eq(NIL, funcs.luaeval "vim.t.testing")
+ eq(NIL, funcs.luaeval "vim.t.other")
+ eq(NIL, funcs.luaeval "vim.t.nonexistant")
end)
it('vim.env', function()
@@ -700,4 +929,22 @@ describe('lua stdlib', function()
eq('2', funcs.luaeval "BUF")
eq(2, funcs.luaeval "#vim.api.nvim_list_bufs()")
end)
+
+ it('vim.regex', function()
+ exec_lua [[
+ re1 = vim.regex"ab\\+c"
+ vim.cmd "set nomagic ignorecase"
+ re2 = vim.regex"xYz"
+ ]]
+ eq({}, exec_lua[[return {re1:match_str("x ac")}]])
+ eq({3,7}, exec_lua[[return {re1:match_str("ac abbc")}]])
+
+ meths.buf_set_lines(0, 0, -1, true, {"yy", "abc abbc"})
+ eq({}, exec_lua[[return {re1:match_line(0, 0)}]])
+ eq({0,3}, exec_lua[[return {re1:match_line(0, 1)}]])
+ eq({3,7}, exec_lua[[return {re1:match_line(0, 1, 1)}]])
+ eq({3,7}, exec_lua[[return {re1:match_line(0, 1, 1, 8)}]])
+ eq({}, exec_lua[[return {re1:match_line(0, 1, 1, 7)}]])
+ eq({0,3}, exec_lua[[return {re1:match_line(0, 1, 0, 7)}]])
+ end)
end)
diff --git a/test/functional/normal/put_spec.lua b/test/functional/normal/put_spec.lua
index 357fafec44..26967ecbba 100644
--- a/test/functional/normal/put_spec.lua
+++ b/test/functional/normal/put_spec.lua
@@ -6,8 +6,8 @@ local insert = helpers.insert
local feed = helpers.feed
local expect = helpers.expect
local eq = helpers.eq
-local map = helpers.map
-local filter = helpers.filter
+local map = helpers.tbl_map
+local filter = helpers.tbl_filter
local feed_command = helpers.feed_command
local curbuf_contents = helpers.curbuf_contents
local funcs = helpers.funcs
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index 57e5077989..11ce26410d 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -293,6 +293,14 @@ describe('XDG-based defaults', function()
-- TODO(jkeyes): tests below fail on win32 because of path separator.
if helpers.pending_win32(pending) then return end
+ local function vimruntime_and_libdir()
+ local vimruntime = eval('$VIMRUNTIME')
+ -- libdir is hard to calculate reliably across various ci platforms
+ -- local libdir = string.gsub(vimruntime, "share/nvim/runtime$", "lib/nvim")
+ local libdir = meths._get_lib_dir()
+ return vimruntime, libdir
+ end
+
describe('with too long XDG variables', function()
before_each(function()
clear({env={
@@ -308,6 +316,8 @@ describe('XDG-based defaults', function()
end)
it('are correctly set', function()
+ local vimruntime, libdir = vimruntime_and_libdir()
+
eq((('/x'):rep(4096) .. '/nvim'
.. ',' .. ('/a'):rep(2048) .. '/nvim'
.. ',' .. ('/b'):rep(2048) .. '/nvim'
@@ -316,7 +326,8 @@ describe('XDG-based defaults', function()
.. ',' .. ('/A'):rep(2048) .. '/nvim/site'
.. ',' .. ('/B'):rep(2048) .. '/nvim/site'
.. (',' .. '/C/nvim/site'):rep(512)
- .. ',' .. eval('$VIMRUNTIME')
+ .. ',' .. vimruntime
+ .. ',' .. libdir
.. (',' .. '/C/nvim/site/after'):rep(512)
.. ',' .. ('/B'):rep(2048) .. '/nvim/site/after'
.. ',' .. ('/A'):rep(2048) .. '/nvim/site/after'
@@ -339,7 +350,8 @@ describe('XDG-based defaults', function()
.. ',' .. ('/A'):rep(2048) .. '/nvim/site'
.. ',' .. ('/B'):rep(2048) .. '/nvim/site'
.. (',' .. '/C/nvim/site'):rep(512)
- .. ',' .. eval('$VIMRUNTIME')
+ .. ',' .. vimruntime
+ .. ',' .. libdir
.. (',' .. '/C/nvim/site/after'):rep(512)
.. ',' .. ('/B'):rep(2048) .. '/nvim/site/after'
.. ',' .. ('/A'):rep(2048) .. '/nvim/site/after'
@@ -368,11 +380,13 @@ describe('XDG-based defaults', function()
end)
it('are not expanded', function()
+ local vimruntime, libdir = vimruntime_and_libdir()
eq(('$XDG_DATA_HOME/nvim'
.. ',$XDG_DATA_DIRS/nvim'
.. ',$XDG_CONFIG_HOME/nvim/site'
.. ',$XDG_CONFIG_DIRS/nvim/site'
- .. ',' .. eval('$VIMRUNTIME')
+ .. ',' .. vimruntime
+ .. ',' .. libdir
.. ',$XDG_CONFIG_DIRS/nvim/site/after'
.. ',$XDG_CONFIG_HOME/nvim/site/after'
.. ',$XDG_DATA_DIRS/nvim/after'
@@ -387,7 +401,8 @@ describe('XDG-based defaults', function()
.. ',$XDG_DATA_DIRS/nvim'
.. ',$XDG_CONFIG_HOME/nvim/site'
.. ',$XDG_CONFIG_DIRS/nvim/site'
- .. ',' .. eval('$VIMRUNTIME')
+ .. ',' .. vimruntime
+ .. ',' .. libdir
.. ',$XDG_CONFIG_DIRS/nvim/site/after'
.. ',$XDG_CONFIG_HOME/nvim/site/after'
.. ',$XDG_DATA_DIRS/nvim/after'
@@ -402,7 +417,8 @@ describe('XDG-based defaults', function()
.. ',$XDG_DATA_DIRS/nvim'
.. ',$XDG_CONFIG_HOME/nvim/site'
.. ',$XDG_CONFIG_DIRS/nvim/site'
- .. ',' .. eval('$VIMRUNTIME')
+ .. ',' .. vimruntime
+ .. ',' .. libdir
.. ',$XDG_CONFIG_DIRS/nvim/site/after'
.. ',$XDG_CONFIG_HOME/nvim/site/after'
.. ',$XDG_DATA_DIRS/nvim/after'
@@ -426,13 +442,15 @@ describe('XDG-based defaults', function()
end)
it('are escaped properly', function()
+ local vimruntime, libdir = vimruntime_and_libdir()
eq(('\\, \\, \\,/nvim'
.. ',\\,-\\,-\\,/nvim'
.. ',-\\,-\\,-/nvim'
.. ',\\,=\\,=\\,/nvim/site'
.. ',\\,≡\\,≡\\,/nvim/site'
.. ',≡\\,≡\\,≡/nvim/site'
- .. ',' .. eval('$VIMRUNTIME')
+ .. ',' .. vimruntime
+ .. ',' .. libdir
.. ',≡\\,≡\\,≡/nvim/site/after'
.. ',\\,≡\\,≡\\,/nvim/site/after'
.. ',\\,=\\,=\\,/nvim/site/after'
@@ -451,7 +469,8 @@ describe('XDG-based defaults', function()
.. ',\\,=\\,=\\,/nvim/site'
.. ',\\,≡\\,≡\\,/nvim/site'
.. ',≡\\,≡\\,≡/nvim/site'
- .. ',' .. eval('$VIMRUNTIME')
+ .. ',' .. vimruntime
+ .. ',' .. libdir
.. ',≡\\,≡\\,≡/nvim/site/after'
.. ',\\,≡\\,≡\\,/nvim/site/after'
.. ',\\,=\\,=\\,/nvim/site/after'
diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua
index deda5c9118..4754c14f5b 100644
--- a/test/functional/options/num_options_spec.lua
+++ b/test/functional/options/num_options_spec.lua
@@ -65,14 +65,12 @@ describe(':set validation', function()
should_succeed('regexpengine', 2)
should_fail('report', -1, 'E487')
should_succeed('report', 0)
- should_fail('scrolloff', -1, 'E49')
- should_fail('sidescrolloff', -1, 'E487')
should_fail('sidescroll', -1, 'E487')
should_fail('cmdwinheight', 0, 'E487')
should_fail('updatetime', -1, 'E487')
should_fail('foldlevel', -5, 'E487')
- should_fail('foldcolumn', 13, 'E474')
+ should_fail('foldcolumn', '13', 'E474')
should_fail('conceallevel', 4, 'E474')
should_fail('numberwidth', 21, 'E474')
should_fail('numberwidth', 0, 'E487')
@@ -82,6 +80,22 @@ describe(':set validation', function()
meths.set_option('window', -10)
eq(23, meths.get_option('window'))
eq('', eval("v:errmsg"))
+
+ -- 'scrolloff' and 'sidescrolloff' can have a -1 value when
+ -- set for the current window, but not globally
+ feed_command('setglobal scrolloff=-1')
+ eq('E487', eval("v:errmsg"):match("E%d*"))
+
+ feed_command('setglobal sidescrolloff=-1')
+ eq('E487', eval("v:errmsg"):match("E%d*"))
+
+ feed_command('let v:errmsg=""')
+
+ feed_command('setlocal scrolloff=-1')
+ eq('', eval("v:errmsg"))
+
+ feed_command('setlocal sidescrolloff=-1')
+ eq('', eval("v:errmsg"))
end)
it('set wmh/wh wmw/wiw checks', function()
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 4829a33861..62dee7df90 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -1,12 +1,13 @@
local helpers = require('test.functional.helpers')(after_each)
+local assert_log = helpers.assert_log
local clear = helpers.clear
local buf_lines = helpers.buf_lines
local dedent = helpers.dedent
local exec_lua = helpers.exec_lua
local eq = helpers.eq
+local pesc = helpers.pesc
local insert = helpers.insert
-local iswin = helpers.iswin
local retry = helpers.retry
local NIL = helpers.NIL
@@ -14,20 +15,27 @@ local NIL = helpers.NIL
-- yield.
local run, stop = helpers.run, helpers.stop
+-- TODO(justinmk): hangs on Windows https://github.com/neovim/neovim/pull/11837
if helpers.pending_win32(pending) then return end
-local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua"
-if iswin() then
- lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\")
-end
+-- Fake LSP server.
+local fake_lsp_code = 'test/functional/fixtures/fake-lsp-server.lua'
+local fake_lsp_logfile = 'Xtest-fake-lsp.log'
+
+teardown(function()
+ os.remove(fake_lsp_logfile)
+end)
-local function test_rpc_server_setup(test_name, timeout_ms)
+local function fake_lsp_server_setup(test_name, timeout_ms)
exec_lua([=[
lsp = require('vim.lsp')
- local test_name, fixture_filename, timeout = ...
+ local test_name, fixture_filename, logfile, timeout = ...
TEST_RPC_CLIENT_ID = lsp.start_client {
+ cmd_env = {
+ NVIM_LOG_FILE = logfile;
+ };
cmd = {
- vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless',
+ vim.v.progpath, '-Es', '-u', 'NONE', '--headless',
"-c", string.format("lua TEST_NAME = %q", test_name),
"-c", string.format("lua TIMEOUT = %d", timeout),
"-c", "luafile "..fixture_filename,
@@ -48,13 +56,13 @@ local function test_rpc_server_setup(test_name, timeout_ms)
vim.rpcnotify(1, "exit", ...)
end;
}
- ]=], test_name, lsp_test_rpc_server_file, timeout_ms or 1e3)
+ ]=], test_name, fake_lsp_code, fake_lsp_logfile, timeout_ms or 1e3)
end
local function test_rpc_server(config)
if config.test_name then
clear()
- test_rpc_server_setup(config.test_name, config.timeout_ms or 1e3)
+ fake_lsp_server_setup(config.test_name, config.timeout_ms or 1e3)
end
local client = setmetatable({}, {
__index = function(_, name)
@@ -114,11 +122,14 @@ describe('LSP', function()
local test_name = "basic_init"
exec_lua([=[
lsp = require('vim.lsp')
- local test_name, fixture_filename = ...
+ local test_name, fixture_filename, logfile = ...
function test__start_client()
return lsp.start_client {
+ cmd_env = {
+ NVIM_LOG_FILE = logfile;
+ };
cmd = {
- vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless',
+ vim.v.progpath, '-Es', '-u', 'NONE', '--headless',
"-c", string.format("lua TEST_NAME = %q", test_name),
"-c", "luafile "..fixture_filename;
};
@@ -126,7 +137,7 @@ describe('LSP', function()
}
end
TEST_CLIENT1 = test__start_client()
- ]=], test_name, lsp_test_rpc_server_file)
+ ]=], test_name, fake_lsp_code, fake_lsp_logfile)
end)
after_each(function()
@@ -195,7 +206,8 @@ describe('LSP', function()
end;
-- If the program timed out, then code will be nil.
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
-- Note that NIL must be used here.
-- on_callback(err, method, result, client_id)
@@ -216,7 +228,10 @@ describe('LSP', function()
client.stop()
end;
on_exit = function(code, signal)
- eq(1, code, "exit code") eq(0, signal, "exit signal")
+ eq(101, code, "exit code", fake_lsp_logfile) -- See fake-lsp-server.lua
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ assert_log(pesc([[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]]),
+ fake_lsp_logfile)
end;
on_callback = function(...)
eq(table.remove(expected_callbacks), {...}, "expected callback")
@@ -226,7 +241,7 @@ describe('LSP', function()
it('should succeed with manual shutdown', function()
local expected_callbacks = {
- {NIL, "shutdown", {}, 1};
+ {NIL, "shutdown", {}, 1, NIL};
{NIL, "test", {}, 1};
}
test_rpc_server {
@@ -237,7 +252,8 @@ describe('LSP', function()
client.notify('exit')
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(...)
eq(table.remove(expected_callbacks), {...}, "expected callback")
@@ -255,7 +271,8 @@ describe('LSP', function()
client.stop()
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(...)
eq(table.remove(expected_callbacks), {...}, "expected callback")
@@ -294,7 +311,8 @@ describe('LSP', function()
client.notify('finish')
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
@@ -336,7 +354,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -378,7 +397,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -420,7 +440,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -468,7 +489,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -516,7 +538,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -536,7 +559,7 @@ describe('LSP', function()
end)
-- TODO(askhan) we don't support full for now, so we can disable these tests.
- pending('should check the body and didChange incremental normal mode editting', function()
+ pending('should check the body and didChange incremental normal mode editing', function()
local expected_callbacks = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
@@ -544,7 +567,7 @@ describe('LSP', function()
}
local client
test_rpc_server {
- test_name = "basic_check_buffer_open_and_change_incremental_editting";
+ test_name = "basic_check_buffer_open_and_change_incremental_editing";
on_setup = function()
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
@@ -564,7 +587,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -607,7 +631,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -657,7 +682,8 @@ describe('LSP', function()
]]
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
@@ -699,7 +725,8 @@ describe('LSP', function()
client.stop(true)
end;
on_exit = function(code, signal)
- eq(0, code, "exit code") eq(0, signal, "exit signal")
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_callback = function(err, method, params, client_id)
eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
@@ -727,27 +754,32 @@ describe('LSP', function()
it('highlight groups', function()
eq({'LspDiagnosticsError',
+ 'LspDiagnosticsErrorSign',
'LspDiagnosticsHint',
+ 'LspDiagnosticsHintSign',
'LspDiagnosticsInformation',
+ 'LspDiagnosticsInformationSign',
'LspDiagnosticsUnderline',
'LspDiagnosticsUnderlineError',
'LspDiagnosticsUnderlineHint',
'LspDiagnosticsUnderlineInformation',
'LspDiagnosticsUnderlineWarning',
'LspDiagnosticsWarning',
+ 'LspDiagnosticsWarningSign',
},
exec_lua([[require'vim.lsp'; return vim.fn.getcompletion('Lsp', 'highlight')]]))
end)
- describe('apply_edits', function()
+ describe('apply_text_edits', function()
before_each(function()
insert(dedent([[
First line of text
Second line of text
Third line of text
- Fourth line of text]]))
+ Fourth line of text
+ å å ɧ 汉语 ↥ 🤦 🦄]]))
end)
- it('applies apply simple edits', function()
+ it('applies simple edits', function()
local edits = {
make_edit(0, 0, 0, 0, {"123"});
make_edit(1, 0, 1, 1, {"2"});
@@ -759,6 +791,7 @@ describe('LSP', function()
'2econd line of text';
'3ird line of text';
'Fourth line of text';
+ 'å å ɧ 汉语 ↥ 🤦 🦄';
}, buf_lines(1))
end)
it('applies complex edits', function()
@@ -782,7 +815,455 @@ describe('LSP', function()
'The next line of text';
'another line of text';
'before this!';
+ 'å å ɧ 汉语 ↥ 🤦 🦄';
+ }, buf_lines(1))
+ end)
+ it('applies non-ASCII characters edits', function()
+ local edits = {
+ make_edit(4, 3, 4, 4, {"ä"});
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({
+ 'First line of text';
+ 'Second line of text';
+ 'Third line of text';
+ 'Fourth line of text';
+ 'å ä ɧ 汉语 ↥ 🤦 🦄';
}, buf_lines(1))
end)
end)
+
+ describe('apply_text_document_edit', function()
+ local target_bufnr
+ local text_document_edit = function(editVersion)
+ return {
+ edits = {
+ make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄")
+ },
+ textDocument = {
+ uri = "file://fake/uri";
+ version = editVersion
+ }
+ }
+ end
+ before_each(function()
+ target_bufnr = exec_lua [[
+ local bufnr = vim.uri_to_bufnr("file://fake/uri")
+ local lines = {"1st line of text", "2nd line of 语text"}
+ vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
+ return bufnr
+ ]]
+ end)
+ it('correctly goes ahead with the edit if all is normal', function()
+ exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(5))
+ eq({
+ 'First ↥ 🤦 🦄 line of text';
+ '2nd line of 语text';
+ }, buf_lines(target_bufnr))
+ end)
+ it('correctly goes ahead with the edit if the version is vim.NIL', function()
+ -- we get vim.NIL when we decode json null value.
+ local json = exec_lua[[
+ return vim.fn.json_decode("{ \"a\": 1, \"b\": null }")
+ ]]
+ eq(json.b, exec_lua("return vim.NIL"))
+
+ exec_lua('vim.lsp.util.apply_text_document_edit(...)', text_document_edit(exec_lua("return vim.NIL")))
+ eq({
+ 'First ↥ 🤦 🦄 line of text';
+ '2nd line of 语text';
+ }, buf_lines(target_bufnr))
+ end)
+ it('skips the edit if the version of the edit is behind the local buffer ', function()
+ local apply_edit_mocking_current_version = function(edit, versionedBuf)
+ exec_lua([[
+ local args = {...}
+ local versionedBuf = args[2]
+ vim.lsp.util.buf_versions[versionedBuf.bufnr] = versionedBuf.currentVersion
+ vim.lsp.util.apply_text_document_edit(...)
+ ]], edit, versionedBuf)
+ end
+
+ local baseText = {
+ '1st line of text';
+ '2nd line of 语text';
+ }
+
+ eq(baseText, buf_lines(target_bufnr))
+
+ -- Apply an edit for an old version, should skip
+ apply_edit_mocking_current_version(text_document_edit(2), {currentVersion=7; bufnr=target_bufnr})
+ eq(baseText, buf_lines(target_bufnr)) -- no change
+
+ -- Sanity check that next version to current does apply change
+ apply_edit_mocking_current_version(text_document_edit(8), {currentVersion=7; bufnr=target_bufnr})
+ eq({
+ 'First ↥ 🤦 🦄 line of text';
+ '2nd line of 语text';
+ }, buf_lines(target_bufnr))
+ end)
+ end)
+ describe('workspace_apply_edit', function()
+ it('workspace/applyEdit returns ApplyWorkspaceEditResponse', function()
+ local expected = {
+ applied = true;
+ failureReason = nil;
+ }
+ eq(expected, exec_lua [[
+ local apply_edit = {
+ label = nil;
+ edit = {};
+ }
+ return vim.lsp.callbacks['workspace/applyEdit'](nil, nil, apply_edit)
+ ]])
+ end)
+ end)
+ describe('completion_list_to_complete_items', function()
+ -- Completion option precedence:
+ -- textEdit.newText > insertText > label
+ -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
+ it('should choose right completion option', function ()
+ local prefix = 'foo'
+ local completion_list = {
+ -- resolves into label
+ { label='foobar' },
+ { label='foobar', textEdit={} },
+ -- resolves into insertText
+ { label='foocar', insertText='foobar' },
+ { label='foocar', insertText='foobar', textEdit={} },
+ -- resolves into textEdit.newText
+ { label='foocar', insertText='foodar', textEdit={newText='foobar'} },
+ { label='foocar', textEdit={newText='foobar'} }
+ }
+ local completion_list_items = {items=completion_list}
+ local expected = {
+ { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label = 'foobar' } } } } },
+ { abbr = 'foobar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foobar', textEdit={} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar' } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foobar', textEdit={} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', insertText='foodar', textEdit={newText='foobar'} } } } } },
+ { abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', textEdit={newText='foobar'} } } } } },
+ }
+
+ eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list, prefix))
+ eq(expected, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], completion_list_items, prefix))
+ eq({}, exec_lua([[return vim.lsp.util.text_document_completion_list_to_complete_items(...)]], {}, prefix))
+ end)
+ end)
+ describe('buf_diagnostics_save_positions', function()
+ it('stores the diagnostics in diagnostics_by_buf', function ()
+ local diagnostics = {
+ { range = {}; message = "diag1" },
+ { range = {}; message = "diag2" },
+ }
+ exec_lua([[
+ vim.lsp.util.buf_diagnostics_save_positions(...)]], 0, diagnostics)
+ eq(1, exec_lua [[ return #vim.lsp.util.diagnostics_by_buf ]])
+ eq(diagnostics, exec_lua [[
+ for _, diagnostics in pairs(vim.lsp.util.diagnostics_by_buf) do
+ return diagnostics
+ end
+ ]])
+ end)
+ end)
+ describe('lsp.util.show_line_diagnostics', function()
+ it('creates floating window and returns popup bufnr and winnr if current line contains diagnostics', function()
+ eq(3, exec_lua [[
+ local buffer = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(buffer, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ local diagnostics = {
+ {
+ range = {
+ start = { line = 0; character = 1; };
+ ["end"] = { line = 0; character = 3; };
+ };
+ severity = vim.lsp.protocol.DiagnosticSeverity.Error;
+ message = "Syntax error";
+ },
+ }
+ vim.api.nvim_win_set_buf(0, buffer)
+ vim.lsp.util.buf_diagnostics_save_positions(vim.fn.bufnr(buffer), diagnostics)
+ local popup_bufnr, winnr = vim.lsp.util.show_line_diagnostics()
+ return popup_bufnr
+ ]])
+ end)
+ end)
+ describe('lsp.util.symbols_to_items', function()
+ describe('convert DocumentSymbol[] to items', function()
+ it('DocumentSymbol has children', function()
+ local expected = {
+ {
+ col = 1,
+ filename = '',
+ kind = 'File',
+ lnum = 2,
+ text = '[File] TestA'
+ },
+ {
+ col = 1,
+ filename = '',
+ kind = 'Module',
+ lnum = 4,
+ text = '[Module] TestB'
+ },
+ {
+ col = 1,
+ filename = '',
+ kind = 'Namespace',
+ lnum = 6,
+ text = '[Namespace] TestC'
+ }
+ }
+ eq(expected, exec_lua [[
+ local doc_syms = {
+ {
+ deprecated = false,
+ detail = "A",
+ kind = 1,
+ name = "TestA",
+ range = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 0,
+ line = 2
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 4,
+ line = 1
+ }
+ },
+ children = {
+ {
+ children = {},
+ deprecated = false,
+ detail = "B",
+ kind = 2,
+ name = "TestB",
+ range = {
+ start = {
+ character = 0,
+ line = 3
+ },
+ ["end"] = {
+ character = 0,
+ line = 4
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 3
+ },
+ ["end"] = {
+ character = 4,
+ line = 3
+ }
+ }
+ }
+ }
+ },
+ {
+ deprecated = false,
+ detail = "C",
+ kind = 3,
+ name = "TestC",
+ range = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 0,
+ line = 6
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 4,
+ line = 5
+ }
+ }
+ }
+ }
+ return vim.lsp.util.symbols_to_items(doc_syms, nil)
+ ]])
+ end)
+ it('DocumentSymbol has no children', function()
+ local expected = {
+ {
+ col = 1,
+ filename = '',
+ kind = 'File',
+ lnum = 2,
+ text = '[File] TestA'
+ },
+ {
+ col = 1,
+ filename = '',
+ kind = 'Namespace',
+ lnum = 6,
+ text = '[Namespace] TestC'
+ }
+ }
+ eq(expected, exec_lua [[
+ local doc_syms = {
+ {
+ deprecated = false,
+ detail = "A",
+ kind = 1,
+ name = "TestA",
+ range = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 0,
+ line = 2
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 4,
+ line = 1
+ }
+ },
+ },
+ {
+ deprecated = false,
+ detail = "C",
+ kind = 3,
+ name = "TestC",
+ range = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 0,
+ line = 6
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 4,
+ line = 5
+ }
+ }
+ }
+ }
+ return vim.lsp.util.symbols_to_items(doc_syms, nil)
+ ]])
+ end)
+ end)
+ describe('convert SymbolInformation[] to items', function()
+ local expected = {
+ {
+ col = 1,
+ filename = 'test_a',
+ kind = 'File',
+ lnum = 2,
+ text = '[File] TestA'
+ },
+ {
+ col = 1,
+ filename = 'test_b',
+ kind = 'Module',
+ lnum = 4,
+ text = '[Module] TestB'
+ }
+ }
+ eq(expected, exec_lua [[
+ local sym_info = {
+ {
+ deprecated = false,
+ kind = 1,
+ name = "TestA",
+ location = {
+ range = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 0,
+ line = 2
+ }
+ },
+ uri = "file://test_a"
+ },
+ contanerName = "TestAContainer"
+ },
+ {
+ deprecated = false,
+ kind = 2,
+ name = "TestB",
+ location = {
+ range = {
+ start = {
+ character = 0,
+ line = 3
+ },
+ ["end"] = {
+ character = 0,
+ line = 4
+ }
+ },
+ uri = "file://test_b"
+ },
+ contanerName = "TestBContainer"
+ }
+ }
+ return vim.lsp.util.symbols_to_items(sym_info, nil)
+ ]])
+ end)
+ end)
+
+ describe('lsp.util._get_completion_item_kind_name', function()
+ describe('returns the name specified by protocol', function()
+ eq("Text", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1)"))
+ eq("TypeParameter", exec_lua("return vim.lsp.util._get_completion_item_kind_name(25)"))
+ end)
+ describe('returns the name not specified by protocol', function()
+ eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(nil)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(vim.NIL)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_completion_item_kind_name(1000)"))
+ end)
+ end)
+
+ describe('lsp.util._get_symbol_kind_name', function()
+ describe('returns the name specified by protocol', function()
+ eq("File", exec_lua("return vim.lsp.util._get_symbol_kind_name(1)"))
+ eq("TypeParameter", exec_lua("return vim.lsp.util._get_symbol_kind_name(26)"))
+ end)
+ describe('returns the name not specified by protocol', function()
+ eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(nil)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(vim.NIL)"))
+ eq("Unknown", exec_lua("return vim.lsp.util._get_symbol_kind_name(1000)"))
+ end)
+ end)
end)
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index d40baca871..f79aceaddf 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -17,6 +17,18 @@ describe(':terminal buffer', function()
screen = thelpers.screen_setup()
end)
+ it('terminal-mode forces various options', function()
+ feed([[<C-\><C-N>]])
+ command('setlocal cursorline cursorcolumn scrolloff=4 sidescrolloff=7')
+ eq({ 1, 1, 4, 7 }, eval('[&l:cursorline, &l:cursorcolumn, &l:scrolloff, &l:sidescrolloff]'))
+ eq('n', eval('mode()'))
+
+ -- Enter terminal-mode ("insert" mode in :terminal).
+ feed('i')
+ eq('t', eval('mode()'))
+ eq({ 0, 0, 0, 0 }, eval('[&l:cursorline, &l:cursorcolumn, &l:scrolloff, &l:sidescrolloff]'))
+ end)
+
describe('when a new file is edited', function()
before_each(function()
feed('<c-\\><c-n>:set bufhidden=wipe<cr>:enew<cr>')
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index ee3db7ae97..0eb5901b3b 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -87,6 +87,36 @@ describe(':terminal mouse', function()
{3:-- TERMINAL --} |
]])
end)
+
+ it('will forward mouse clicks to the program with the correct even if set nu', function()
+ if helpers.pending_win32(pending) then return end
+ nvim('command', 'set number')
+ -- When the display area such as a number is clicked, it returns to the
+ -- normal mode.
+ feed('<LeftMouse><3,0>')
+ eq('n', eval('mode()'))
+ screen:expect([[
+ {7: 11 }^line28 |
+ {7: 12 }line29 |
+ {7: 13 }line30 |
+ {7: 14 }mouse enabled |
+ {7: 15 }rows: 6, cols: 46 |
+ {7: 16 }{2: } |
+ |
+ ]])
+ -- If click on the coordinate (0,1) of the region of the terminal
+ -- (i.e. the coordinate (4,1) of vim), 'CSI !"' is sent to the terminal.
+ feed('i<LeftMouse><4,1>')
+ screen:expect([[
+ {7: 11 }line28 |
+ {7: 12 }line29 |
+ {7: 13 }line30 |
+ {7: 14 }mouse enabled |
+ {7: 15 }rows: 6, cols: 46 |
+ {7: 16 } !"{1: } |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
end)
describe('with a split window and other buffer', function()
@@ -148,7 +178,7 @@ describe(':terminal mouse', function()
end)
it('wont lose focus if another window is scrolled', function()
- feed('<ScrollWheelUp><0,0><ScrollWheelUp><0,0>')
+ feed('<ScrollWheelUp><4,0><ScrollWheelUp><4,0>')
screen:expect([[
{7: 21 }line │line30 |
{7: 22 }line │rows: 5, cols: 25 |
@@ -158,7 +188,7 @@ describe(':terminal mouse', function()
========== ========== |
{3:-- TERMINAL --} |
]])
- feed('<S-ScrollWheelDown><0,0>')
+ feed('<S-ScrollWheelDown><4,0>')
screen:expect([[
{7: 26 }line │line30 |
{7: 27 }line │rows: 5, cols: 25 |
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 7a5569c14b..639e311ae6 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -976,6 +976,28 @@ describe('floatwin', function()
{2:~ }|
]], float_pos={
[5] = {{id = 1002}, "NE", 4, 0, 50, true}
+ }, win_viewport = {
+ [2] = {
+ topline = 0,
+ botline = 3,
+ curline = 0,
+ curcol = 3,
+ win = { id = 1000 }
+ },
+ [4] = {
+ topline = 0,
+ botline = 3,
+ curline = 0,
+ curcol = 3,
+ win = { id = 1001 }
+ },
+ [5] = {
+ topline = 0,
+ botline = 2,
+ curline = 0,
+ curcol = 0,
+ win = { id = 1002 }
+ }
}}
else
screen:expect([[
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index 0b788e7afb..6ec45064da 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -96,8 +96,20 @@ describe("folded lines", function()
{1: ~}|
:set rightleft |
]]}
- end)
+ feed_command("set norightleft")
+ meths.input_mouse('left', 'press', '', 0, 0, 1)
+ screen:expect{grid=[[
+ {7:▾▸}{5:^+--- 5 lines: aa··························}|
+ {7:│ }ff |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :set norightleft |
+ ]]}
+ end)
it("works with multibyte text", function()
-- Currently the only allowed value of 'maxcombine'
@@ -283,4 +295,64 @@ describe("folded lines", function()
]])
end)
+
+ it("work with autoresize", function()
+
+ funcs.setline(1, 'line 1')
+ funcs.setline(2, 'line 2')
+ funcs.setline(3, 'line 3')
+ funcs.setline(4, 'line 4')
+
+ feed("zfj")
+ command("set foldcolumn=0")
+ screen:expect{grid=[[
+ {5:^+-- 2 lines: line 1·························}|
+ line 3 |
+ line 4 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ -- should adapt to the current nesting of folds (e.g., 1)
+ command("set foldcolumn=auto:1")
+ screen:expect{grid=[[
+ {7:+}{5:^+-- 2 lines: line 1························}|
+ {7: }line 3 |
+ {7: }line 4 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ -- fdc should not change with a new fold as the maximum is 1
+ feed("zf3j")
+
+ screen:expect{grid=[[
+ {7:+}{5:^+-- 4 lines: line 1························}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- relax the maximum fdc thus fdc should expand to
+ -- accomodate the current number of folds
+ command("set foldcolumn=auto:4")
+ screen:expect{grid=[[
+ {7:+ }{5:^+-- 4 lines: line 1·······················}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
end)
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index d7791a3107..28e4e88326 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -1186,6 +1186,7 @@ describe("'winhighlight' highlight", function()
[25] = {bold = true, foreground = Screen.colors.Green1},
[26] = {background = Screen.colors.Red},
[27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1},
+ [28] = {bold = true, foreground = Screen.colors.Brown},
})
command("hi Background1 guibg=DarkBlue")
command("hi Background2 guibg=DarkGreen")
@@ -1598,4 +1599,45 @@ describe("'winhighlight' highlight", function()
{21:-- }{22:match 1 of 3} |
]])
end)
+
+ it('can override CursorLine and CursorLineNr', function()
+ -- CursorLine used to be parsed as CursorLineNr, because strncmp
+ command('set cursorline number')
+ command('split')
+ command('set winhl=CursorLine:Background1')
+ screen:expect{grid=[[
+ {28: 1 }{1:^ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ {28: 1 }{18: }|
+ {0:~ }|
+ {4:[No Name] }|
+ |
+ ]]}
+
+ command('set winhl=CursorLineNr:Background2,CursorLine:Background1')
+ screen:expect{grid=[[
+ {5: 1 }{1:^ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ {28: 1 }{18: }|
+ {0:~ }|
+ {4:[No Name] }|
+ |
+ ]]}
+
+ feed('<c-w>w')
+ screen:expect{grid=[[
+ {5: 1 }{1: }|
+ {0:~ }|
+ {0:~ }|
+ {4:[No Name] }|
+ {28: 1 }{18:^ }|
+ {0:~ }|
+ {3:[No Name] }|
+ |
+ ]]}
+ end)
end)
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index b841574643..afb0c9cfa6 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -18,6 +18,7 @@ local wait = helpers.wait
local nvim = helpers.nvim
local sleep = helpers.sleep
local nvim_dir = helpers.nvim_dir
+local assert_alive = helpers.assert_alive
local default_text = [[
Inc substitution on
@@ -84,6 +85,7 @@ local function common_setup(screen, inccommand, text)
[14] = {foreground = Screen.colors.White, background = Screen.colors.Red},
[15] = {bold=true, foreground=Screen.colors.Blue},
[16] = {background=Screen.colors.Grey90}, -- cursorline
+ [17] = {foreground = Screen.colors.Blue1},
vis = {background=Screen.colors.LightGrey}
})
end
@@ -2291,6 +2293,76 @@ describe(":substitute", function()
]])
end)
+ it("inccommand=split, contraction of two subsequent NL chars", function()
+ -- luacheck: push ignore 611
+ local text = [[
+ AAA AA
+
+ BBB BB
+
+ CCC CC
+
+]]
+ -- luacheck: pop
+
+ -- This used to crash, but more than 20 highlight entries are required
+ -- to reproduce it (so that the marktree has multiple nodes)
+ common_setup(screen, "split", string.rep(text,10))
+ feed(":%s/\\n\\n/<c-v><c-m>/g")
+ screen:expect{grid=[[
+ CCC CC |
+ AAA AA |
+ BBB BB |
+ CCC CC |
+ |
+ {11:[No Name] [+] }|
+ | 1| AAA AA |
+ | 2|{12: }BBB BB |
+ | 3|{12: }CCC CC |
+ | 4|{12: }AAA AA |
+ | 5|{12: }BBB BB |
+ | 6|{12: }CCC CC |
+ | 7|{12: }AAA AA |
+ {10:[Preview] }|
+ :%s/\n\n/{17:^M}/g^ |
+ ]]}
+ assert_alive()
+ end)
+
+ it("inccommand=nosplit, contraction of two subsequent NL chars", function()
+ -- luacheck: push ignore 611
+ local text = [[
+ AAA AA
+
+ BBB BB
+
+ CCC CC
+
+]]
+ -- luacheck: pop
+
+ common_setup(screen, "nosplit", string.rep(text,10))
+ feed(":%s/\\n\\n/<c-v><c-m>/g")
+ screen:expect{grid=[[
+ CCC CC |
+ AAA AA |
+ BBB BB |
+ CCC CC |
+ AAA AA |
+ BBB BB |
+ CCC CC |
+ AAA AA |
+ BBB BB |
+ CCC CC |
+ AAA AA |
+ BBB BB |
+ CCC CC |
+ |
+ :%s/\n\n/{17:^M}/g^ |
+ ]]}
+ assert_alive()
+ end)
+
it("inccommand=split, multibyte text", function()
common_setup(screen, "split", multibyte_text)
feed(":%s/£.*ѫ/X¥¥")
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index dfc3d045e8..efc02db159 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -811,6 +811,8 @@ describe('ui/builtin messages', function()
[5] = {foreground = Screen.colors.Blue1},
[6] = {bold = true, foreground = Screen.colors.Magenta},
[7] = {background = Screen.colors.Grey20},
+ [8] = {reverse = true},
+ [9] = {background = Screen.colors.LightRed}
})
end)
@@ -962,6 +964,91 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
zbc |
]]}
end)
+
+ it('redraws NOT_VALID correctly after message', function()
+ -- edge case: only one window was set NOT_VALID. Orginal report
+ -- used :make, but fake it using one command to set the current
+ -- window NOT_VALID and another to show a long message.
+ command("set more")
+ feed(':new<cr><c-w><c-w>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {8:[No Name] }|
+ ^ |
+ {1:~ }|
+ {3:[No Name] }|
+ :new |
+ ]]}
+
+ feed(':set colorcolumn=10 | digraphs<cr>')
+ screen:expect{grid=[[
+ :set colorcolumn=10 | digraphs |
+ NU {5:^@} 10 SH {5:^A} 1 SX {5:^B} 2 EX {5:^C} 3 |
+ ET {5:^D} 4 EQ {5:^E} 5 AK {5:^F} 6 BL {5:^G} 7 |
+ BS {5:^H} 8 HT {5:^I} 9 LF {5:^@} 10 VT {5:^K} 11 |
+ FF {5:^L} 12 CR {5:^M} 13 SO {5:^N} 14 SI {5:^O} 15 |
+ DL {5:^P} 16 D1 {5:^Q} 17 D2 {5:^R} 18 D3 {5:^S} 19 |
+ {4:-- More --}^ |
+ ]]}
+
+ feed('q')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {8:[No Name] }|
+ ^ {9: } |
+ {1:~ }|
+ {3:[No Name] }|
+ |
+ ]]}
+
+ -- edge case: just covers statusline
+ feed(':set colorcolumn=5 | lua error("x\\n\\nx")<cr>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {3: }|
+ {2:E5108: Error executing lua [string ":lua"]:1: x} |
+ |
+ {2:x} |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {8:[No Name] }|
+ ^ {9: } |
+ {1:~ }|
+ {3:[No Name] }|
+ |
+ ]]}
+
+ -- edge case: just covers lowest window line
+ feed(':set colorcolumn=5 | lua error("x\\n\\n\\nx")<cr>')
+ screen:expect{grid=[[
+ |
+ {3: }|
+ {2:E5108: Error executing lua [string ":lua"]:1: x} |
+ |
+ |
+ {2:x} |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {8:[No Name] }|
+ ^ {9: } |
+ {1:~ }|
+ {3:[No Name] }|
+ |
+ ]]}
+ end)
end)
describe('ui/ext_messages', function()
diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua
index 8122cb08a3..e6a79feadc 100644
--- a/test/functional/ui/multibyte_spec.lua
+++ b/test/functional/ui/multibyte_spec.lua
@@ -123,6 +123,10 @@ describe('multibyte rendering: statusline', function()
before_each(function()
clear()
screen = Screen.new(40, 4)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {bold = true, reverse = true},
+ })
screen:attach()
command('set laststatus=2')
end)
@@ -131,8 +135,8 @@ describe('multibyte rendering: statusline', function()
command('set statusline=你好')
screen:expect([[
^ |
- ~ |
- 你好 |
+ {1:~ }|
+ {2:你好 }|
|
]])
end)
@@ -140,8 +144,8 @@ describe('multibyte rendering: statusline', function()
command('set statusline=abc')
screen:expect([[
^ |
- ~ |
- abc |
+ {1:~ }|
+ {2:abc }|
|
]])
end)
@@ -149,8 +153,8 @@ describe('multibyte rendering: statusline', function()
command('set statusline=Ÿ')
screen:expect([[
^ |
- ~ |
- <9f> |
+ {1:~ }|
+ {2:<9f> }|
|
]])
end)
@@ -159,8 +163,8 @@ describe('multibyte rendering: statusline', function()
-- o + U+1DF0 + U+20EF + U+0338 + U+20D0 + U+20E7 + U+20DD
screen:expect([[
^ |
- ~ |
- o̸⃯ᷰ⃐⃧⃝ |
+ {1:~ }|
+ {2:o̸⃯ᷰ⃐⃧⃝ }|
|
]])
end)
@@ -169,9 +173,19 @@ describe('multibyte rendering: statusline', function()
-- U+9F + U+1DF0 + U+20EF + U+0338 + U+20D0 + U+20E7 + U+20DD
screen:expect([[
^ |
- ~ |
- <9f><1df0><20ef><0338><20d0><20e7><20dd>|
+ {1:~ }|
+ {2:<9f><1df0><20ef><0338><20d0><20e7><20dd>}|
|
]])
end)
+
+ it('hidden group %( %) does not cause invalid unicode', function()
+ command("let &statusline = '%#StatColorHi2#%(✓%#StatColorHi2#%) Q≡'")
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {2: Q≡ }|
+ |
+ ]]}
+ end)
end)
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index 01ffe80be3..e4d1187dea 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -1962,4 +1962,191 @@ describe('ext_multigrid', function()
{1:~ }|
]]}
end)
+
+ it('has viewport information', function()
+ screen:try_resize(48, 8)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ {11:[No Name] }|
+ [3:------------------------------------------------]|
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ ]], win_viewport={
+ [2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0}
+ }}
+ insert([[
+ Lorem ipsum dolor sit amet, consectetur
+ adipisicing elit, sed do eiusmod tempor
+ incididunt ut labore et dolore magna aliqua.
+ Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex
+ ea commodo consequat. Duis aute irure dolor in
+ reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur. Excepteur sint
+ occaecat cupidatat non proident, sunt in culpa
+ qui officia deserunt mollit anim id est
+ laborum.]])
+
+ screen:expect{grid=[[
+ ## grid 1
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [3:------------------------------------------------]|
+ ## grid 2
+ ea commodo consequat. Duis aute irure dolor in |
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ occaecat cupidatat non proident, sunt in culpa |
+ qui officia deserunt mollit anim id est |
+ laborum^. |
+ ## grid 3
+ |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7},
+ }}
+
+
+ feed('<c-u>')
+ screen:expect{grid=[[
+ ## grid 1
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [3:------------------------------------------------]|
+ ## grid 2
+ incididunt ut labore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ ea commodo consequat. Duis aute irure dolor in |
+ reprehenderit in voluptate velit esse cillum |
+ ^dolore eu fugiat nulla pariatur. Excepteur sint |
+ ## grid 3
+ |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 2, botline = 9, curline = 7, curcol = 0},
+ }}
+
+ command("split")
+ screen:expect{grid=[[
+ ## grid 1
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [3:------------------------------------------------]|
+ ## grid 2
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ ## grid 3
+ |
+ ## grid 4
+ ea commodo consequat. Duis aute irure dolor in |
+ reprehenderit in voluptate velit esse cillum |
+ ^dolore eu fugiat nulla pariatur. Excepteur sint |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0},
+ [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 7, curcol = 0},
+ }}
+
+ feed("b")
+ screen:expect{grid=[[
+ ## grid 1
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [3:------------------------------------------------]|
+ ## grid 2
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ ## grid 3
+ |
+ ## grid 4
+ ea commodo consequat. Duis aute irure dolor in |
+ reprehenderit in voluptate velit esse ^cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0},
+ [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 6, curcol = 38},
+ }}
+
+ feed("2k")
+ screen:expect{grid=[[
+ ## grid 1
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [3:------------------------------------------------]|
+ ## grid 2
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ ## grid 3
+ |
+ ## grid 4
+ exercitation ullamco laboris nisi ut a^liquip ex |
+ ea commodo consequat. Duis aute irure dolor in |
+ reprehenderit in voluptate velit esse cillum |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0},
+ [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38},
+ }}
+
+ -- handles non-current window
+ meths.win_set_cursor(1000, {1, 10})
+ screen:expect{grid=[[
+ ## grid 1
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ [4:------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:------------------------------------------------]|
+ [2:------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [3:------------------------------------------------]|
+ ## grid 2
+ Lorem ipsum dolor sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ ## grid 3
+ |
+ ## grid 4
+ exercitation ullamco laboris nisi ut a^liquip ex |
+ ea commodo consequat. Duis aute irure dolor in |
+ reprehenderit in voluptate velit esse cillum |
+ ]], win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 10},
+ [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38},
+ }}
+ end)
end)
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
index 581e196bbb..9646c3fdad 100644
--- a/test/functional/ui/options_spec.lua
+++ b/test/functional/ui/options_spec.lua
@@ -20,6 +20,8 @@ describe('UI receives option updates', function()
pumblend=0,
showtabline=1,
termguicolors=false,
+ ttimeout=true,
+ ttimeoutlen=50,
ext_cmdline=false,
ext_popupmenu=false,
ext_tabline=false,
@@ -108,6 +110,18 @@ describe('UI receives option updates', function()
eq(expected, screen.options)
end)
+ command("set nottimeout")
+ expected.ttimeout = false
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ command("set ttimeoutlen=100")
+ expected.ttimeoutlen = 100
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
command("set all&")
screen:expect(function()
eq(defaults, screen.options)
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index b2ebf7af19..c1c5d1ce2e 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -8,7 +8,7 @@ local command = helpers.command
local funcs = helpers.funcs
local get_pathsep = helpers.get_pathsep
local eq = helpers.eq
-local matches = helpers.matches
+local pcall_err = helpers.pcall_err
describe('ui/ext_popupmenu', function()
local screen
@@ -382,7 +382,7 @@ describe('ui/ext_popupmenu', function()
end
describe('pum_set_height', function()
- it('can be set pum height', function()
+ it('can set pum height', function()
source_complete_month()
local month_expected = {
{'January', '', '', ''},
@@ -421,22 +421,79 @@ describe('ui/ext_popupmenu', function()
end)
it('an error occurs if set 0 or less', function()
- local ok, err, _
- ok, _ = pcall(meths.ui_pum_set_height, 1)
- eq(ok, true)
- ok, err = pcall(meths.ui_pum_set_height, 0)
- eq(ok, false)
- matches('.*: Expected pum height > 0', err)
+ meths.ui_pum_set_height(1)
+ eq('Expected pum height > 0',
+ pcall_err(meths.ui_pum_set_height, 0))
end)
it('an error occurs when ext_popupmenu is false', function()
- local ok, err, _
- ok, _ = pcall(meths.ui_pum_set_height, 1)
- eq(ok, true)
+ meths.ui_pum_set_height(1)
screen:set_option('ext_popupmenu', false)
- ok, err = pcall(meths.ui_pum_set_height, 1)
- eq(ok, false)
- matches('.*: It must support the ext_popupmenu option', err)
+ eq('It must support the ext_popupmenu option',
+ pcall_err(meths.ui_pum_set_height, 1))
+ end)
+ end)
+
+ describe('pum_set_bounds', function()
+ it('can set pum bounds', function()
+ source_complete_month()
+ local month_expected = {
+ {'January', '', '', ''},
+ {'February', '', '', ''},
+ {'March', '', '', ''},
+ {'April', '', '', ''},
+ {'May', '', '', ''},
+ {'June', '', '', ''},
+ {'July', '', '', ''},
+ {'August', '', '', ''},
+ {'September', '', '', ''},
+ {'October', '', '', ''},
+ {'November', '', '', ''},
+ {'December', '', '', ''},
+ }
+ local pum_height = 6
+ feed('o<C-r>=TestCompleteMonth()<CR>')
+ meths.ui_pum_set_height(pum_height)
+ -- set bounds w h r c
+ meths.ui_pum_set_bounds(10.5, 5.2, 6.3, 7.4)
+ feed('<PageDown>')
+ -- pos becomes pum_height-2 because it is subtracting 2 to keep some
+ -- context in ins_compl_key2count()
+ screen:expect{grid=[[
+ |
+ January^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=month_expected,
+ pos=pum_height-2,
+ anchor={1,1,0},
+ }}
+ end)
+
+ it('no error occurs if row or col set less than 0', function()
+ meths.ui_pum_set_bounds(1.0, 1.0, 0.0, 1.5)
+ meths.ui_pum_set_bounds(1.0, 1.0, -1.0, 0.0)
+ meths.ui_pum_set_bounds(1.0, 1.0, 0.0, -1.0)
+ end)
+
+ it('an error occurs if width or height set 0 or less', function()
+ meths.ui_pum_set_bounds(1.0, 1.0, 0.0, 1.5)
+ eq('Expected width > 0',
+ pcall_err(meths.ui_pum_set_bounds, 0.0, 1.0, 1.0, 0.0))
+ eq('Expected height > 0',
+ pcall_err(meths.ui_pum_set_bounds, 1.0, -1.0, 1.0, 0.0))
+ end)
+
+ it('an error occurs when ext_popupmenu is false', function()
+ meths.ui_pum_set_bounds(1.0, 1.0, 0.0, 1.5)
+ screen:set_option('ext_popupmenu', false)
+ eq('UI must support the ext_popupmenu option',
+ pcall_err(meths.ui_pum_set_bounds, 1.0, 1.0, 0.0, 1.5))
end)
end)
@@ -1338,7 +1395,7 @@ describe('builtin popupmenu', function()
end)
it('with rightleft window', function()
- command("set rl")
+ command("set rl wildoptions+=pum")
feed('isome rightleft ')
screen:expect([[
^ tfelthgir emos|
@@ -1435,6 +1492,55 @@ describe('builtin popupmenu', function()
{1: ~}|
{2:-- INSERT --} |
]])
+
+ -- not rightleft on the cmdline
+ feed('<esc>:sign ')
+ screen:expect{grid=[[
+ drow tfelthgir emos|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ :sign ^ |
+ ]]}
+
+ feed('<tab>')
+ screen:expect{grid=[[
+ drow tfelthgir emos|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: }{s: define }{1: ~}|
+ {1: }{n: jump }{1: ~}|
+ {1: }{n: list }{1: ~}|
+ {1: }{n: place }{1: ~}|
+ {1: }{n: undefine }{1: ~}|
+ {1: }{n: unplace }{1: ~}|
+ :sign define^ |
+ ]]}
end)
it('with multiline messages', function()
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 64f784afe3..bf979e89f4 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -158,6 +158,7 @@ function Screen.new(width, height)
wildmenu_items = nil,
wildmenu_selected = nil,
win_position = {},
+ win_viewport = {},
float_pos = {},
msg_grid = nil,
msg_grid_pos = nil,
@@ -254,7 +255,7 @@ end
-- canonical order of ext keys, used to generate asserts
local ext_keys = {
'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos',
- 'messages', 'showmode', 'showcmd', 'ruler', 'float_pos',
+ 'messages', 'showmode', 'showcmd', 'ruler', 'float_pos', 'win_viewport'
}
-- Asserts that the screen state eventually matches an expected state.
@@ -421,6 +422,9 @@ screen:redraw_debug() to show all intermediate screen states. ]])
if expected.mode ~= nil then
extstate.mode = self.mode
end
+ if expected.win_viewport == nil then
+ extstate.win_viewport = nil
+ end
-- Convert assertion errors into invalid screen state descriptions.
for _, k in ipairs(concat_tables(ext_keys, {'mode'})) do
@@ -726,6 +730,7 @@ function Screen:_handle_grid_destroy(grid)
self._grids[grid] = nil
if self._options.ext_multigrid then
self.win_position[grid] = nil
+ self.win_viewport[grid] = nil
end
end
@@ -746,14 +751,24 @@ function Screen:_handle_grid_cursor_goto(grid, row, col)
end
function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height)
- self.win_position[grid] = {
- win = win,
- startrow = startrow,
- startcol = startcol,
- width = width,
- height = height
- }
- self.float_pos[grid] = nil
+ self.win_position[grid] = {
+ win = win,
+ startrow = startrow,
+ startcol = startcol,
+ width = width,
+ height = height
+ }
+ self.float_pos[grid] = nil
+end
+
+function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol)
+ self.win_viewport[grid] = {
+ win = win,
+ topline = topline,
+ botline = botline,
+ curline = curline,
+ curcol = curcol
+ }
end
function Screen:_handle_win_float_pos(grid, ...)
@@ -1130,6 +1145,8 @@ function Screen:_extstate_repr(attr_state)
messages[i] = {kind=entry[1], content=self:_chunks_repr(entry[2], attr_state)}
end
+ local win_viewport = (next(self.win_viewport) and self.win_viewport) or nil
+
return {
popupmenu=self.popupmenu,
cmdline=cmdline,
@@ -1141,7 +1158,8 @@ function Screen:_extstate_repr(attr_state)
showcmd=self:_chunks_repr(self.showcmd, attr_state),
ruler=self:_chunks_repr(self.ruler, attr_state),
msg_history=msg_history,
- float_pos=self.float_pos
+ float_pos=self.float_pos,
+ win_viewport=win_viewport,
}
end
@@ -1216,10 +1234,6 @@ function Screen:render(headers, attr_state, preview)
return rv
end
-local remove_all_metatables = function(item, path)
- if path[#path] ~= inspect.METATABLE then return item end
-end
-
-- Returns the current screen state in the form of a screen:expect()
-- keyword-args map.
function Screen:get_snapshot(attrs, ignore)
@@ -1269,6 +1283,26 @@ function Screen:get_snapshot(attrs, ignore)
return kwargs, ext_state, attr_state
end
+local function fmt_ext_state(name, state)
+ if name == "win_viewport" then
+ local str = "{\n"
+ for k,v in pairs(state) do
+ str = (str.." ["..k.."] = {win = {id = "..v.win.id.."}, topline = "
+ ..v.topline..", botline = "..v.botline..", curline = "..v.curline
+ ..", curcol = "..v.curcol.."},\n")
+ end
+ return str .. "}"
+ else
+ -- TODO(bfredl): improve formatting of more states
+ local function remove_all_metatables(item, path)
+ if path[#path] ~= inspect.METATABLE then
+ return item
+ end
+ end
+ return inspect(state,{process=remove_all_metatables})
+ end
+end
+
function Screen:print_snapshot(attrs, ignore)
local kwargs, ext_state, attr_state = self:get_snapshot(attrs, ignore)
local attrstr = ""
@@ -1291,9 +1325,8 @@ function Screen:print_snapshot(attrs, ignore)
print(kwargs.grid)
io.stdout:write( "]]"..attrstr)
for _, k in ipairs(ext_keys) do
- if ext_state[k] ~= nil then
- -- TODO(bfredl): improve formatting
- io.stdout:write(", "..k.."="..inspect(ext_state[k],{process=remove_all_metatables}))
+ if ext_state[k] ~= nil and not (k == "win_viewport" and not self.options.ext_multigrid) then
+ io.stdout:write(", "..k.."="..fmt_ext_state(k, ext_state[k]))
end
end
print("}\n")
diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua
index 56987d7bc2..99ebc4971e 100644
--- a/test/functional/ui/wildmode_spec.lua
+++ b/test/functional/ui/wildmode_spec.lua
@@ -16,6 +16,44 @@ describe("'wildmenu'", function()
screen:attach()
end)
+ it('C-E to cancel wildmenu completion restore original input', function()
+ feed(':sign <tab>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ define jump list > |
+ :sign define^ |
+ ]])
+ feed('<C-E>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :sign ^ |
+ ]])
+ end)
+
+ it('C-Y to apply selection and end wildmenu completion', function()
+ feed(':sign <tab>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ define jump list > |
+ :sign define^ |
+ ]])
+ feed('<tab><C-Y>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :sign jump^ |
+ ]])
+ end)
+
it(':sign <tab> shows wildmenu completions', function()
command('set wildmenu wildmode=full')
feed(':sign <tab>')