aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/config/paths.lua.in1
-rw-r--r--test/functional/api/buffer_spec.lua55
-rw-r--r--test/functional/api/command_spec.lua2
-rw-r--r--test/functional/api/keymap_spec.lua12
-rw-r--r--test/functional/api/vim_spec.lua266
-rw-r--r--test/functional/api/window_spec.lua26
-rw-r--r--test/functional/autocmd/dirchanged_spec.lua120
-rw-r--r--test/functional/core/startup_spec.lua64
-rw-r--r--test/functional/editor/meta_key_spec.lua15
-rw-r--r--test/functional/editor/mode_visual_spec.lua1
-rw-r--r--test/functional/ex_cmds/source_spec.lua20
-rw-r--r--test/functional/fixtures/autoload/health/full_render.vim8
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua62
-rw-r--r--test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim3
-rw-r--r--test/functional/fixtures/lua/test_plug/health/init.lua11
-rw-r--r--test/functional/fixtures/lua/test_plug/submodule/health.lua11
-rw-r--r--test/functional/fixtures/lua/test_plug/submodule_failed/health.lua12
-rw-r--r--test/functional/fixtures/middle/filen.lua1
-rw-r--r--test/functional/fixtures/pack/foo/opt/funky/filen.lua12
-rw-r--r--test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_y.lua1
-rw-r--r--test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_z.lua1
-rw-r--r--test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x.lua1
-rw-r--r--test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x/init.lua1
-rw-r--r--test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_y/init.lua2
-rw-r--r--test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua3
-rw-r--r--test/functional/legacy/delete_spec.lua9
-rw-r--r--test/functional/legacy/eval_spec.lua3
-rw-r--r--test/functional/legacy/expand_spec.lua2
-rw-r--r--test/functional/legacy/file_perm_spec.lua7
-rw-r--r--test/functional/legacy/fnamemodify_spec.lua11
-rw-r--r--test/functional/lua/api_spec.lua4
-rw-r--r--test/functional/lua/buffer_updates_spec.lua96
-rw-r--r--test/functional/lua/diagnostic_spec.lua472
-rw-r--r--test/functional/lua/ffi_spec.lua62
-rw-r--r--test/functional/lua/json_spec.lua133
-rw-r--r--test/functional/lua/luaeval_spec.lua7
-rw-r--r--test/functional/lua/ui_spec.lua46
-rw-r--r--test/functional/lua/uri_spec.lua17
-rw-r--r--test/functional/lua/vim_spec.lua78
-rw-r--r--test/functional/plugin/health_spec.lua157
-rw-r--r--test/functional/plugin/lsp/codelens_spec.lua28
-rw-r--r--test/functional/plugin/lsp/diagnostic_spec.lua26
-rw-r--r--test/functional/plugin/lsp_spec.lua295
-rw-r--r--test/functional/terminal/buffer_spec.lua24
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua15
-rw-r--r--test/functional/terminal/mouse_spec.lua10
-rw-r--r--test/functional/terminal/window_split_tab_spec.lua2
-rw-r--r--test/functional/ui/decorations_spec.lua508
-rw-r--r--test/functional/ui/float_spec.lua443
-rw-r--r--test/functional/ui/inccommand_spec.lua23
-rw-r--r--test/functional/ui/mouse_spec.lua187
-rw-r--r--test/functional/vimscript/json_functions_spec.lua8
52 files changed, 3048 insertions, 336 deletions
diff --git a/test/config/paths.lua.in b/test/config/paths.lua.in
index 7fe5d8ad80..e3979981ba 100644
--- a/test/config/paths.lua.in
+++ b/test/config/paths.lua.in
@@ -19,5 +19,6 @@ if module.test_luajit_prg == '' then
end
end
table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include")
+table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/src/nvim/auto")
return module
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index 81fad206da..01fcfab543 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -707,4 +707,59 @@ describe('api/buf', function()
eq({3, 0}, curbuf('get_mark', 'v'))
end)
end)
+
+ describe('nvim_buf_set_mark', function()
+ it('works with buffer local marks', function()
+ curbufmeths.set_lines(-1, -1, true, {'a', 'bit of', 'text'})
+ eq(true, curbufmeths.set_mark('z', 1, 1))
+ eq({1, 1}, curbufmeths.get_mark('z'))
+ end)
+ it('works with file/uppercase marks', function()
+ curbufmeths.set_lines(-1, -1, true, {'a', 'bit of', 'text'})
+ eq(true, curbufmeths.set_mark('Z', 3, 1))
+ eq({3, 1}, curbufmeths.get_mark('Z'))
+ end)
+ it('fails when invalid marks names are used', function()
+ eq(false, pcall(curbufmeths.set_mark, '!', 1, 0))
+ eq(false, pcall(curbufmeths.set_mark, 'fail', 1, 0))
+ end)
+ it('fails when invalid buffer number is used', function()
+ eq(false, pcall(meths.buf_set_mark, 99, 'a', 1, 1))
+ end)
+ end)
+
+ describe('nvim_buf_del_mark', function()
+ it('works with buffer local marks', function()
+ curbufmeths.set_lines(-1, -1, true, {'a', 'bit of', 'text'})
+ curbufmeths.set_mark('z', 3, 1)
+ eq(true, curbufmeths.del_mark('z'))
+ eq({0, 0}, curbufmeths.get_mark('z'))
+ end)
+ it('works with file/uppercase marks', function()
+ curbufmeths.set_lines(-1, -1, true, {'a', 'bit of', 'text'})
+ curbufmeths.set_mark('Z', 3, 3)
+ eq(true, curbufmeths.del_mark('Z'))
+ eq({0, 0}, curbufmeths.get_mark('Z'))
+ end)
+ it('returns false in marks not set in this buffer', function()
+ local abuf = meths.create_buf(false,true)
+ bufmeths.set_lines(abuf, -1, -1, true, {'a', 'bit of', 'text'})
+ bufmeths.set_mark(abuf, 'A', 2, 2)
+ eq(false, curbufmeths.del_mark('A'))
+ eq({2, 2}, bufmeths.get_mark(abuf, 'A'))
+ end)
+ it('returns false if mark was not deleted', function()
+ curbufmeths.set_lines(-1, -1, true, {'a', 'bit of', 'text'})
+ curbufmeths.set_mark('z', 3, 1)
+ eq(true, curbufmeths.del_mark('z'))
+ eq(false, curbufmeths.del_mark('z')) -- Mark was already deleted
+ end)
+ it('fails when invalid marks names are used', function()
+ eq(false, pcall(curbufmeths.del_mark, '!'))
+ eq(false, pcall(curbufmeths.del_mark, 'fail'))
+ end)
+ it('fails when invalid buffer number is used', function()
+ eq(false, pcall(meths.buf_del_mark, 99, 'a'))
+ end)
+ end)
end)
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index 37331d11c7..6f929ad1ca 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -21,7 +21,7 @@ describe('nvim_get_commands', function()
it('validates input', function()
eq('builtin=true not implemented', pcall_err(meths.get_commands,
{builtin=true}))
- eq('unexpected key: foo', pcall_err(meths.get_commands,
+ eq("Invalid key: 'foo'", pcall_err(meths.get_commands,
{foo='blah'}))
end)
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index 4194945645..dd8eef7ca0 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -436,16 +436,16 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
end)
it('error on invalid optnames', function()
- eq('Invalid key: silentt',
+ eq("Invalid key: 'silentt'",
pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true}))
- eq('Invalid key: sidd',
+ eq("Invalid key: 'sidd'",
pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {sidd = false}))
- eq('Invalid key: nowaiT',
+ eq("Invalid key: 'nowaiT'",
pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {nowaiT = false}))
end)
it('error on <buffer> option key', function()
- eq('Invalid key: buffer',
+ eq("Invalid key: 'buffer'",
pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true}))
end)
@@ -454,8 +454,8 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
-- note: need '%' to escape hyphens, which have special meaning in lua
it('throws an error when given non-boolean value for '..opt, function()
local opts = {}
- opts[opt] = 2
- eq('Gave non-boolean value for an opt: '..opt,
+ opts[opt] = 'fooo'
+ eq(opt..' is not a boolean',
pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', opts))
end)
end
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index ffef6a6066..c95c24fabe 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -12,14 +12,17 @@ local funcs = helpers.funcs
local iswin = helpers.iswin
local meths = helpers.meths
local matches = helpers.matches
+local mkdir_p = helpers.mkdir_p
local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed
local is_os = helpers.is_os
local parse_context = helpers.parse_context
local request = helpers.request
+local rmdir = helpers.rmdir
local source = helpers.source
local next_msg = helpers.next_msg
local tmpname = helpers.tmpname
local write_file = helpers.write_file
+local exec_lua = helpers.exec_lua
local pcall_err = helpers.pcall_err
local format_string = helpers.format_string
@@ -89,6 +92,14 @@ describe('API', function()
nvim('exec', 'set nowrap', false)
eq('nowrap\n\tLast set from anonymous :source',
nvim('exec', 'verbose set wrap?', true))
+
+ -- Using script var to force creation of a script item
+ nvim('exec', [[
+ let s:a = 1
+ set nowrap
+ ]], false)
+ eq('nowrap\n\tLast set from anonymous :source (script id 1)',
+ nvim('exec', 'verbose set wrap?', true))
end)
it('multiline input', function()
@@ -130,6 +141,43 @@ describe('API', function()
-- try no spaces before continuations to catch off-by-one error
nvim('exec', 'let ab = #{\n\\a: 98,\n"\\ b: 2\n\\}', false)
eq({a = 98}, request('nvim_eval', 'g:ab'))
+
+ -- Script scope (s:)
+ eq('ahoy! script-scoped varrrrr', nvim('exec', [[
+ let s:pirate = 'script-scoped varrrrr'
+ function! s:avast_ye_hades(s) abort
+ return a:s .. ' ' .. s:pirate
+ endfunction
+ echo <sid>avast_ye_hades('ahoy!')
+ ]], true))
+
+ eq('ahoy! script-scoped varrrrr', nvim('exec', [[
+ let s:pirate = 'script-scoped varrrrr'
+ function! Avast_ye_hades(s) abort
+ return a:s .. ' ' .. s:pirate
+ endfunction
+ echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1)
+ ]], true))
+
+ eq('Vim(call):E5555: API call: Vim(echo):E121: Undefined variable: s:pirate',
+ pcall_err(request, 'nvim_exec', [[
+ let s:pirate = 'script-scoped varrrrr'
+ call nvim_exec('echo s:pirate', 1)
+ ]], false))
+
+ -- Script items are created only on script var access
+ eq('1\n0', nvim('exec', [[
+ echo expand("<SID>")->empty()
+ let s:a = 123
+ echo expand("<SID>")->empty()
+ ]], true))
+
+ eq('1\n0', nvim('exec', [[
+ echo expand("<SID>")->empty()
+ function s:a() abort
+ endfunction
+ echo expand("<SID>")->empty()
+ ]], true))
end)
it('non-ASCII input', function()
@@ -1117,7 +1165,7 @@ describe('API', function()
describe('nvim_get_context', function()
it('validates args', function()
- eq('unexpected key: blah',
+ eq("Invalid key: 'blah'",
pcall_err(nvim, 'get_context', {blah={}}))
eq('invalid value for key: types',
pcall_err(nvim, 'get_context', {types=42}))
@@ -1574,6 +1622,18 @@ describe('API', function()
end)
describe('nvim_list_runtime_paths', function()
+ setup(function()
+ local pathsep = helpers.get_pathsep()
+ mkdir_p('Xtest'..pathsep..'a')
+ mkdir_p('Xtest'..pathsep..'b')
+ end)
+ teardown(function()
+ rmdir 'Xtest'
+ end)
+ before_each(function()
+ meths.set_current_dir 'Xtest'
+ end)
+
it('returns nothing with empty &runtimepath', function()
meths.set_option('runtimepath', '')
eq({}, meths.list_runtime_paths())
@@ -1601,8 +1661,7 @@ describe('API', function()
local long_path = ('/a'):rep(8192)
meths.set_option('runtimepath', long_path)
local paths_list = meths.list_runtime_paths()
- neq({long_path}, paths_list)
- eq({long_path:sub(1, #(paths_list[1]))}, paths_list)
+ eq({}, paths_list)
end)
end)
@@ -2206,6 +2265,9 @@ describe('API', function()
[2] = {background = tonumber('0xffff40'), bg_indexed = true};
[3] = {background = Screen.colors.Plum1, fg_indexed = true, foreground = tonumber('0x00e000')};
[4] = {bold = true, reverse = true, background = Screen.colors.Plum1};
+ [5] = {foreground = Screen.colors.Blue, background = Screen.colors.LightMagenta, bold = true};
+ [6] = {bold = true};
+ [7] = {reverse = true, background = Screen.colors.LightMagenta};
})
end)
@@ -2253,5 +2315,203 @@ describe('API', function()
|
]]}
end)
+
+ it('can handle input', function()
+ screen:try_resize(50, 10)
+ eq({3, 2}, exec_lua [[
+ buf = vim.api.nvim_create_buf(1,1)
+
+ stream = ''
+ do_the_echo = false
+ function input(_,t1,b1,data)
+ stream = stream .. data
+ _G.vals = {t1, b1}
+ if do_the_echo then
+ vim.api.nvim_chan_send(t1, data)
+ end
+ end
+
+ term = vim.api.nvim_open_term(buf, {on_input=input})
+ vim.api.nvim_open_win(buf, true, {width=40, height=5, row=1, col=1, relative='editor'})
+ return {term, buf}
+ ]])
+
+ screen:expect{grid=[[
+ |
+ {0:~}{1:^ }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+
+ feed 'iba<c-x>bla'
+ screen:expect{grid=[[
+ |
+ {0:~}{7: }{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:-- TERMINAL --} |
+ ]]}
+
+ eq('ba\024bla', exec_lua [[ return stream ]])
+ eq({3,2}, exec_lua [[ return vals ]])
+
+ exec_lua [[ do_the_echo = true ]]
+ feed 'herrejösses!'
+
+ screen:expect{grid=[[
+ |
+ {0:~}{1:herrejösses!}{7: }{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~}{1: }{0: }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {6:-- TERMINAL --} |
+ ]]}
+ eq('ba\024blaherrejösses!', exec_lua [[ return stream ]])
+ end)
+ end)
+
+ describe('nvim_del_mark', function()
+ it('works', function()
+ local buf = meths.create_buf(false,true)
+ meths.buf_set_lines(buf, -1, -1, true, {'a', 'bit of', 'text'})
+ eq(true, meths.buf_set_mark(buf, 'F', 2, 2))
+ eq(true, meths.del_mark('F'))
+ eq({0, 0}, meths.buf_get_mark(buf, 'F'))
+ end)
+ it('fails when invalid marks are used', function()
+ eq(false, pcall(meths.del_mark, 'f'))
+ eq(false, pcall(meths.del_mark, '!'))
+ eq(false, pcall(meths.del_mark, 'fail'))
+ end)
+ end)
+ describe('nvim_get_mark', function()
+ it('works', function()
+ local buf = meths.create_buf(false,true)
+ meths.buf_set_lines(buf, -1, -1, true, {'a', 'bit of', 'text'})
+ meths.buf_set_mark(buf, 'F', 2, 2)
+ meths.buf_set_name(buf, "mybuf")
+ local mark = meths.get_mark('F')
+ -- Compare the path tail ony
+ assert(string.find(mark[4], "mybuf$"))
+ eq({2, 2, buf.id, mark[4]}, mark)
+ end)
+ it('fails when invalid marks are used', function()
+ eq(false, pcall(meths.del_mark, 'f'))
+ eq(false, pcall(meths.del_mark, '!'))
+ eq(false, pcall(meths.del_mark, 'fail'))
+ end)
+ it('returns the expected when mark is not set', function()
+ eq(true, meths.del_mark('A'))
+ eq({0, 0, 0, ''}, meths.get_mark('A'))
+ end)
+ it('works with deleted buffers', function()
+ local fname = tmpname()
+ write_file(fname, 'a\nbit of\text')
+ nvim("command", "edit " .. fname)
+ local buf = meths.get_current_buf()
+
+ meths.buf_set_mark(buf, 'F', 2, 2)
+ nvim("command", "new") -- Create new buf to avoid :bd failing
+ nvim("command", "bd! " .. buf.id)
+ os.remove(fname)
+
+ local mark = meths.get_mark('F')
+ -- To avoid comparing relative vs absolute path
+ local mfname = mark[4]
+ local tail_patt = [[[\/][^\/]*$]]
+ -- tail of paths should be equals
+ eq(fname:match(tail_patt), mfname:match(tail_patt))
+ eq({2, 2, buf.id, mark[4]}, mark)
+ end)
+ end)
+ describe('nvim_eval_statusline', function()
+ it('works', function()
+ eq({
+ str = '%StatusLineStringWithHighlights',
+ width = 31
+ },
+ meths.eval_statusline(
+ '%%StatusLineString%#WarningMsg#WithHighlights',
+ {}))
+ end)
+ it('doesn\'t exceed maxwidth', function()
+ eq({
+ str = 'Should be trun>',
+ width = 15
+ },
+ meths.eval_statusline(
+ 'Should be truncated%<',
+ { maxwidth = 15 }))
+ end)
+ describe('highlight parsing', function()
+ it('works', function()
+ eq({
+ str = "TextWithWarningHighlightTextWithUserHighlight",
+ width = 45,
+ highlights = {
+ { start = 0, group = 'WarningMsg' },
+ { start = 24, group = 'User1' }
+ },
+ },
+ meths.eval_statusline(
+ '%#WarningMsg#TextWithWarningHighlight%1*TextWithUserHighlight',
+ { highlights = true }))
+ end)
+ it('works with no highlight', function()
+ eq({
+ str = "TextWithNoHighlight",
+ width = 19,
+ highlights = {
+ { start = 0, group = 'StatusLine' },
+ },
+ },
+ meths.eval_statusline(
+ 'TextWithNoHighlight',
+ { highlights = true }))
+ end)
+ it('works with inactive statusline', function()
+ command('split')
+
+ eq({
+ str = 'TextWithNoHighlightTextWithWarningHighlight',
+ width = 43,
+ highlights = {
+ { start = 0, group = 'StatusLineNC' },
+ { start = 19, group = 'WarningMsg' }
+ }
+ },
+ meths.eval_statusline(
+ 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
+ { winid = meths.list_wins()[2].id, highlights = true }))
+ end)
+ it('works with tabline', function()
+ eq({
+ str = 'TextWithNoHighlightTextWithWarningHighlight',
+ width = 43,
+ highlights = {
+ { start = 0, group = 'TabLineFill' },
+ { start = 19, group = 'WarningMsg' }
+ }
+ },
+ meths.eval_statusline(
+ 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight',
+ { use_tabline = true, highlights = true }))
+ end)
+ end)
end)
end)
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index c49d6405f4..11755a9d97 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -1,8 +1,9 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq,
- ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf,
+ ok, feed, insert, eval, tabpage = helpers.clear, helpers.nvim, helpers.curbuf,
helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq,
- helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval
+ helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval,
+ helpers.tabpage
local poke_eventloop = helpers.poke_eventloop
local curwinmeths = helpers.curwinmeths
local funcs = helpers.funcs
@@ -11,6 +12,7 @@ local NIL = helpers.NIL
local meths = helpers.meths
local command = helpers.command
local pcall_err = helpers.pcall_err
+local assert_alive = helpers.assert_alive
-- check if str is visible at the beginning of some line
local function is_visible(str)
@@ -206,7 +208,7 @@ describe('API/win', function()
end)
end)
- describe('{get,set}_option', function()
+ describe('nvim_win_get_option, nvim_win_set_option', function()
it('works', function()
curwin('set_option', 'colorcolumn', '4,3')
eq('4,3', curwin('get_option', 'colorcolumn'))
@@ -224,6 +226,18 @@ describe('API/win', function()
pcall_err(curwin, 'get_option', 'statusline'))
eq('', eval('&l:statusline')) -- confirm local value was not copied
end)
+
+ it('after switching windows #15390', function()
+ nvim('command', 'tabnew')
+ local tab1 = unpack(nvim('list_tabpages'))
+ local win1 = unpack(tabpage('list_wins', tab1))
+ window('set_option', win1, 'statusline', 'window-status')
+ nvim('command', 'split')
+ nvim('command', 'wincmd J')
+ nvim('command', 'wincmd j')
+ eq('window-status', window('get_option', win1, 'statusline'))
+ assert_alive()
+ end)
end)
describe('get_position', function()
@@ -354,13 +368,13 @@ describe('API/win', function()
local win = meths.open_win(0, true, {
relative='editor', row=10, col=10, width=50, height=10
})
- local tabpage = eval('tabpagenr()')
+ local tab = eval('tabpagenr()')
command('tabprevious')
eq(1, eval('tabpagenr()'))
meths.win_close(win, false)
- eq(1001, meths.tabpage_get_win(tabpage).id)
- helpers.assert_alive()
+ eq(1001, meths.tabpage_get_win(tab).id)
+ assert_alive()
end)
end)
diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua
index 6f2da24cf2..f4a1642ebf 100644
--- a/test/functional/autocmd/dirchanged_spec.lua
+++ b/test/functional/autocmd/dirchanged_spec.lua
@@ -6,6 +6,7 @@ local command = h.command
local eq = h.eq
local eval = h.eval
local request = h.request
+local iswin = h.iswin
describe('autocmd DirChanged', function()
local curdir = string.gsub(lfs.currentdir(), '\\', '/')
@@ -14,6 +15,11 @@ describe('autocmd DirChanged', function()
curdir .. '/Xtest-functional-autocmd-dirchanged.dir2',
curdir .. '/Xtest-functional-autocmd-dirchanged.dir3',
}
+ local win_dirs = {
+ curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR1',
+ curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR2',
+ curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR3',
+ }
setup(function() for _, dir in pairs(dirs) do h.mkdir(dir) end end)
teardown(function() for _, dir in pairs(dirs) do h.rmdir(dir) end end)
@@ -27,17 +33,20 @@ describe('autocmd DirChanged', function()
command([[autocmd DirChanged * let g:getcwd = substitute(g:getcwd, '\\', '/', 'g')]])
end)
- it('sets v:event', function()
+ it('sets v:event and <amatch>', function()
command('lcd '..dirs[1])
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
eq(1, eval('g:cdcount'))
command('tcd '..dirs[2])
- eq({cwd=dirs[2], scope='tab', changed_window=false}, eval('g:ev'))
+ eq({cwd=dirs[2], scope='tabpage', changed_window=false}, eval('g:ev'))
+ eq('tabpage', eval('g:amatch'))
eq(2, eval('g:cdcount'))
command('cd '..dirs[3])
eq({cwd=dirs[3], scope='global', changed_window=false}, eval('g:ev'))
+ eq('global', eval('g:amatch'))
eq(3, eval('g:cdcount'))
end)
@@ -63,17 +72,6 @@ describe('autocmd DirChanged', function()
eq(dirs[3], eval('getcwd()'))
end)
- it('sets <amatch> to CWD "scope"', function()
- command('lcd '..dirs[1])
- eq('window', eval('g:amatch'))
-
- command('tcd '..dirs[2])
- eq('tab', eval('g:amatch'))
-
- command('cd '..dirs[3])
- eq('global', eval('g:amatch'))
- end)
-
it('does not trigger if :cd fails', function()
command('let g:ev = {}')
@@ -106,13 +104,79 @@ describe('autocmd DirChanged', function()
command('split '..dirs[1]..'/foo')
eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatch'))
command('split '..dirs[2]..'/bar')
eq({cwd=dirs[2], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatch'))
eq(2, eval('g:cdcount'))
end)
+ it('does not trigger if directory has not changed', function()
+ command('lcd '..dirs[1])
+ eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
+ eq(1, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('lcd '..dirs[1])
+ eq({}, eval('g:ev'))
+ eq(1, eval('g:cdcount'))
+
+ if iswin() then
+ command('lcd '..win_dirs[1])
+ eq({}, eval('g:ev'))
+ eq(1, eval('g:cdcount'))
+ end
+
+ command('tcd '..dirs[2])
+ eq({cwd=dirs[2], scope='tabpage', changed_window=false}, eval('g:ev'))
+ eq('tabpage', eval('g:amatch'))
+ eq(2, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('tcd '..dirs[2])
+ eq({}, eval('g:ev'))
+ eq(2, eval('g:cdcount'))
+
+ if iswin() then
+ command('tcd '..win_dirs[2])
+ eq({}, eval('g:ev'))
+ eq(2, eval('g:cdcount'))
+ end
+
+ command('cd '..dirs[3])
+ eq({cwd=dirs[3], scope='global', changed_window=false}, eval('g:ev'))
+ eq('global', eval('g:amatch'))
+ eq(3, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('cd '..dirs[3])
+ eq({}, eval('g:ev'))
+ eq(3, eval('g:cdcount'))
+
+ if iswin() then
+ command('cd '..win_dirs[3])
+ eq({}, eval('g:ev'))
+ eq(3, eval('g:cdcount'))
+ end
+
+ command('set autochdir')
+
+ command('split '..dirs[1]..'/foo')
+ eq({cwd=dirs[1], scope='window', changed_window=false}, eval('g:ev'))
+ eq('auto', eval('g:amatch'))
+ eq(4, eval('g:cdcount'))
+ command('let g:ev = {}')
+ command('split '..dirs[1]..'/bar')
+ eq({}, eval('g:ev'))
+ eq(4, eval('g:cdcount'))
+
+ if iswin() then
+ command('split '..win_dirs[1]..'/baz')
+ eq({}, eval('g:ev'))
+ eq(4, eval('g:cdcount'))
+ end
+ end)
+
it("is triggered by switching to win/tab with different CWD #6054", function()
command('lcd '..dirs[3]) -- window 3
command('split '..dirs[2]..'/foo') -- window 2
@@ -122,6 +186,7 @@ describe('autocmd DirChanged', function()
command('2wincmd w') -- window 2
eq({cwd=dirs[2], scope='window', changed_window=true}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
eq(4, eval('g:cdcount'))
command('tabnew') -- tab 2 (tab-local CWD)
@@ -129,8 +194,10 @@ describe('autocmd DirChanged', function()
command('tcd '..dirs[3])
command('tabnext') -- tab 1 (no tab-local CWD)
eq({cwd=dirs[2], scope='window', changed_window=true}, eval('g:ev'))
+ eq('window', eval('g:amatch'))
command('tabnext') -- tab 2
- eq({cwd=dirs[3], scope='tab', changed_window=true}, eval('g:ev'))
+ eq({cwd=dirs[3], scope='tabpage', changed_window=true}, eval('g:ev'))
+ eq('tabpage', eval('g:amatch'))
eq(7, eval('g:cdcount'))
command('tabnext') -- tab 1
@@ -138,6 +205,31 @@ describe('autocmd DirChanged', function()
eq(9, eval('g:cdcount'))
command('tabnext') -- tab 2 (has the *same* CWD)
eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+
+ if iswin() then
+ command('tabnew') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tcd '..win_dirs[3])
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 1
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 2
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 1
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('lcd '..win_dirs[3]) -- window 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 2
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabnext') -- tab 1
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ command('tabprevious') -- tab 3
+ eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
+ end
end)
it('is triggered by nvim_set_current_dir()', function()
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index f057420dde..cc6e2c8067 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -20,6 +20,7 @@ local retry = helpers.retry
local rmdir = helpers.rmdir
local sleep = helpers.sleep
local iswin = helpers.iswin
+local startswith = helpers.startswith
local write_file = helpers.write_file
local meths = helpers.meths
@@ -310,7 +311,8 @@ describe('startup', function()
end)
local function pack_clear(cmd)
- clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}}
+ -- add packages after config dir in rtp but before config/after
+ clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}}
end
@@ -326,6 +328,15 @@ describe('startup', function()
eq({9003, '\thowdy'}, exec_lua [[ return { _G.y, _G.z } ]])
end)
+ it("handles require from &packpath in an async handler", function()
+ -- NO! you cannot just speed things up by calling async functions during startup!
+ -- It doesn't make anything actually faster! NOOOO!
+ pack_clear [[ lua require'async_leftpad'('brrrr', 'async_res') ]]
+
+ -- haha, async leftpad go brrrrr
+ eq('\tbrrrr', exec_lua [[ return _G.async_res ]])
+ end)
+
it("handles :packadd during startup", function()
-- control group: opt/bonus is not availabe by default
pack_clear [[
@@ -351,12 +362,57 @@ describe('startup', function()
it("handles the correct order with start packages and after/", function()
pack_clear [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]]
- eq({'ordinary', 'FANCY', 'ordinary after', 'FANCY after'}, exec_lua [[ return _G.test_loadorder ]])
+ eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
+ end)
+
+ it("handles the correct order with start packages and after/ after startup", function()
+ pack_clear [[ lua _G.test_loadorder = {} ]]
+ command [[ runtime! filen.lua ]]
+ eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
+ end)
+
+ it("handles the correct order with globpath(&rtp, ...)", function()
+ pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]]
+ command [[
+ for x in globpath(&rtp, "filen.lua",1,1)
+ call v:lua.dofile(x)
+ endfor
+ ]]
+ eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
+
+ local rtp = meths.get_option'rtp'
+ ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), 'rtp='..rtp)
end)
it("handles the correct order with opt packages and after/", function()
pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! superspecial\nruntime! filen.lua" ]]
- eq({'ordinary', 'SuperSpecial', 'FANCY', 'SuperSpecial after', 'ordinary after', 'FANCY after'}, exec_lua [[ return _G.test_loadorder ]])
+ eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
+ end)
+
+ it("handles the correct order with opt packages and after/ after startup", function()
+ pack_clear [[ lua _G.test_loadorder = {} ]]
+ command [[
+ packadd! superspecial
+ runtime! filen.lua
+ ]]
+ eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
+ end)
+
+ it("handles the correct order with opt packages and globpath(&rtp, ...)", function()
+ pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]]
+ command [[
+ packadd! superspecial
+ for x in globpath(&rtp, "filen.lua",1,1)
+ call v:lua.dofile(x)
+ endfor
+ ]]
+ eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'SuperSpecial after', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
+ end)
+
+ it("handles the correct order with a package that changes packpath", function()
+ pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! funky\nruntime! filen.lua" ]]
+ eq({'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
+ eq({'ordinary', 'funky!', 'mittel', 'ordinary after'}, exec_lua [[ return _G.nested_order ]])
end)
end)
@@ -472,7 +528,7 @@ describe('user config init', function()
clear{ args_rm={'-u' }, env=xenv }
eq(1, eval('g:lua_rc'))
- eq(init_lua_path, eval('$MYVIMRC'))
+ eq(funcs.fnamemodify(init_lua_path, ':p'), eval('$MYVIMRC'))
end)
describe 'with explicitly provided config'(function()
diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua
index 2a9541ba96..c219204409 100644
--- a/test/functional/editor/meta_key_spec.lua
+++ b/test/functional/editor/meta_key_spec.lua
@@ -11,15 +11,20 @@ describe('meta-keys #8226 #13042', function()
end)
it('ALT/META, normal-mode', function()
- -- Unmapped ALT-chords behave as ESC+c
+ -- Unmapped ALT-chord behaves as ESC+c.
insert('hello')
feed('0<A-x><M-x>')
expect('llo')
+ -- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869
+ command('nnoremap <ESC> A<lt>ESC><Esc>')
+ command('nnoremap ; A;<Esc>')
+ feed('<A-;><M-;>')
+ expect('llo<ESC>;<ESC>;')
-- Mapped ALT-chord behaves as mapped.
command('nnoremap <M-l> Ameta-l<Esc>')
command('nnoremap <A-j> Aalt-j<Esc>')
feed('<A-j><M-l>')
- expect('lloalt-jmeta-l')
+ expect('llo<ESC>;<ESC>;alt-jmeta-l')
end)
it('ALT/META, visual-mode', function()
@@ -27,11 +32,15 @@ describe('meta-keys #8226 #13042', function()
insert('peaches')
feed('viw<A-x>viw<M-x>')
expect('peach')
+ -- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869
+ command('vnoremap <ESC> A<lt>ESC>')
+ feed('viw<A-;><ESC>viw<M-;><ESC>')
+ expect('peach<ESC>;<ESC>;')
-- Mapped ALT-chord behaves as mapped.
command('vnoremap <M-l> Ameta-l<Esc>')
command('vnoremap <A-j> Aalt-j<Esc>')
feed('viw<A-j>viw<M-l>')
- expect('peachalt-jmeta-l')
+ expect('peach<ESC>;<ESC>;alt-jmeta-l')
end)
it('ALT/META insert-mode', function()
diff --git a/test/functional/editor/mode_visual_spec.lua b/test/functional/editor/mode_visual_spec.lua
index e9c117a1e5..468ae00e01 100644
--- a/test/functional/editor/mode_visual_spec.lua
+++ b/test/functional/editor/mode_visual_spec.lua
@@ -14,6 +14,7 @@ describe('visual-mode', function()
it("select-mode Ctrl-O doesn't cancel Ctrl-O mode when processing event #15688", function()
feed('iHello World<esc>gh<c-o>')
eq({mode='vs', blocking=false}, meths.get_mode()) -- fast event
+ eq({mode='vs', blocking=false}, meths.get_mode()) -- again #15288
eq(2, eval('1+1')) -- causes K_EVENT key
eq({mode='vs', blocking=false}, meths.get_mode()) -- still in ctrl-o mode
feed('^')
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index bdf6ae76d1..fa650d611b 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -25,12 +25,19 @@ describe(':source', function()
let b = #{
\ k: "v"
"\ (o_o)
- \ }]])
+ \ }
+ let c = expand("<SID>")->empty()
+ let s:s = 0zbeef.cafe
+ let d = s:s]])
command('source')
eq('2', meths.exec('echo a', true))
eq("{'k': 'v'}", meths.exec('echo b', true))
+ -- Script items are created only on script var access
+ eq("1", meths.exec('echo c', true))
+ eq("0zBEEFCAFE", meths.exec('echo d', true))
+
exec('set cpoptions+=C')
eq('Vim(let):E15: Invalid expression: #{', exc_exec('source'))
end)
@@ -43,7 +50,11 @@ describe(':source', function()
let b = #{
"\ (>_<)
\ K: "V"
- \ }]])
+ \ }
+ function! s:C() abort
+ return expand("<SID>") .. "C()"
+ endfunction
+ let D = {-> s:C()}]])
-- Source the 2nd line only
feed('ggjV')
@@ -55,6 +66,11 @@ describe(':source', function()
feed_command(':source')
eq('4', meths.exec('echo a', true))
eq("{'K': 'V'}", meths.exec('echo b', true))
+ eq("<SNR>1_C()", meths.exec('echo D()', true))
+
+ -- Source last line only
+ feed_command(':$source')
+ eq('Vim(echo):E117: Unknown function: s:C', exc_exec('echo D()'))
exec('set cpoptions+=C')
eq('Vim(let):E15: Invalid expression: #{', exc_exec("'<,'>source"))
diff --git a/test/functional/fixtures/autoload/health/full_render.vim b/test/functional/fixtures/autoload/health/full_render.vim
new file mode 100644
index 0000000000..2064b8606e
--- /dev/null
+++ b/test/functional/fixtures/autoload/health/full_render.vim
@@ -0,0 +1,8 @@
+function! health#full_render#check()
+ call health#report_start("report 1")
+ call health#report_ok("life is fine")
+ call health#report_warn("no what installed", ["pip what", "make what"])
+ call health#report_start("report 2")
+ call health#report_info("stuff is stable")
+ call health#report_error("why no hardcopy", [":h :hardcopy", ":h :TOhtml"])
+endfunction
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index 9579525502..9abf478070 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -43,11 +43,11 @@ end
local function read_message()
local line = io.read("*l")
local length = line:lower():match("content%-length:%s*(%d+)")
- return vim.fn.json_decode(io.read(2 + length):sub(2))
+ return vim.json.decode(io.read(2 + length):sub(2))
end
local function send(payload)
- io.stdout:write(format_message_with_content_length(vim.fn.json_encode(payload)))
+ io.stdout:write(format_message_with_content_length(vim.json.encode(payload)))
end
local function respond(id, err, result)
@@ -246,6 +246,35 @@ function tests.capabilities_for_client_supports_method()
}
end
+function tests.check_forward_request_cancelled()
+ skeleton {
+ on_init = function(_)
+ return { capabilities = {} }
+ end;
+ body = function()
+ expect_request("error_code_test", function()
+ return {code = -32800}, nil, {method = "error_code_test", client_id=1}
+ end)
+ notify('finish')
+ end;
+ }
+end
+
+function tests.check_forward_content_modified()
+ skeleton {
+ on_init = function(_)
+ return { capabilities = {} }
+ end;
+ body = function()
+ expect_request("error_code_test", function()
+ return {code = -32801}, nil, {method = "error_code_test", client_id=1}
+ end)
+ expect_notification('finish')
+ notify('finish')
+ end;
+ }
+end
+
function tests.basic_finish()
skeleton {
on_init = function(params)
@@ -564,6 +593,35 @@ function tests.decode_nil()
}
end
+
+function tests.code_action_with_resolve()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ codeActionProvider = {
+ resolveProvider = true
+ }
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ local cmd = {
+ title = 'Command 1',
+ command = 'dummy1'
+ }
+ expect_request('textDocument/codeAction', function()
+ return nil, { cmd, }
+ end)
+ expect_request('codeAction/resolve', function()
+ return nil, cmd
+ end)
+ notify('shutdown')
+ end;
+ }
+end
+
-- Tests will be indexed by TEST_NAME
local kill_timer = vim.loop.new_timer()
diff --git a/test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim b/test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim
new file mode 100644
index 0000000000..de05f56e9e
--- /dev/null
+++ b/test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim
@@ -0,0 +1,3 @@
+function! health#success1#check()
+ call health#report_start("If you see this I'm broken")
+endfunction
diff --git a/test/functional/fixtures/lua/test_plug/health/init.lua b/test/functional/fixtures/lua/test_plug/health/init.lua
new file mode 100644
index 0000000000..d07632cff4
--- /dev/null
+++ b/test/functional/fixtures/lua/test_plug/health/init.lua
@@ -0,0 +1,11 @@
+local M = {}
+local health = require("health")
+
+M.check = function()
+ health.report_start("report 1")
+ health.report_ok("everything is fine")
+ health.report_start("report 2")
+ health.report_ok("nothing to see here")
+end
+
+return M
diff --git a/test/functional/fixtures/lua/test_plug/submodule/health.lua b/test/functional/fixtures/lua/test_plug/submodule/health.lua
new file mode 100644
index 0000000000..d07632cff4
--- /dev/null
+++ b/test/functional/fixtures/lua/test_plug/submodule/health.lua
@@ -0,0 +1,11 @@
+local M = {}
+local health = require("health")
+
+M.check = function()
+ health.report_start("report 1")
+ health.report_ok("everything is fine")
+ health.report_start("report 2")
+ health.report_ok("nothing to see here")
+end
+
+return M
diff --git a/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua b/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua
new file mode 100644
index 0000000000..3a8af6ebb2
--- /dev/null
+++ b/test/functional/fixtures/lua/test_plug/submodule_failed/health.lua
@@ -0,0 +1,12 @@
+local M = {}
+local health = require("health")
+
+M.check = function()
+ health.report_start("report 1")
+ health.report_ok("everything is fine")
+ health.report_warn("About to add a number to nil")
+ local a = nil + 2
+ return a
+end
+
+return M
diff --git a/test/functional/fixtures/middle/filen.lua b/test/functional/fixtures/middle/filen.lua
new file mode 100644
index 0000000000..fce50cc776
--- /dev/null
+++ b/test/functional/fixtures/middle/filen.lua
@@ -0,0 +1 @@
+table.insert(_G.test_loadorder, "mittel")
diff --git a/test/functional/fixtures/pack/foo/opt/funky/filen.lua b/test/functional/fixtures/pack/foo/opt/funky/filen.lua
new file mode 100644
index 0000000000..a33b83c2a7
--- /dev/null
+++ b/test/functional/fixtures/pack/foo/opt/funky/filen.lua
@@ -0,0 +1,12 @@
+table.insert(_G.test_loadorder, "funky!")
+
+if not _G.nesty then
+ _G.nesty = true
+ local save_order = _G.test_loadorder
+ _G.test_loadorder = {}
+ _G.vim.o.pp = "" -- funky!
+ vim.cmd [[runtime! filen.lua ]]
+ _G.nested_order = _G.test_loadorder
+ _G.test_loadorder = save_order
+ _G.nesty = nil
+end
diff --git a/test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_y.lua b/test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_y.lua
new file mode 100644
index 0000000000..7daa7733a0
--- /dev/null
+++ b/test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_y.lua
@@ -0,0 +1 @@
+return "I am fancy_y.lua"
diff --git a/test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_z.lua b/test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_z.lua
new file mode 100644
index 0000000000..6e81afdd70
--- /dev/null
+++ b/test/functional/fixtures/pack/foo/start/fancyplugin/after/lua/fancy_z.lua
@@ -0,0 +1 @@
+return "I am fancy_z.lua"
diff --git a/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x.lua b/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x.lua
new file mode 100644
index 0000000000..1b897a96cc
--- /dev/null
+++ b/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x.lua
@@ -0,0 +1 @@
+return "I am fancy_x.lua"
diff --git a/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x/init.lua b/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x/init.lua
new file mode 100644
index 0000000000..8c27a43cab
--- /dev/null
+++ b/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_x/init.lua
@@ -0,0 +1 @@
+return "I am init.lua of fancy_x!"
diff --git a/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_y/init.lua b/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_y/init.lua
new file mode 100644
index 0000000000..b66cbee4f6
--- /dev/null
+++ b/test/functional/fixtures/pack/foo/start/fancyplugin/lua/fancy_y/init.lua
@@ -0,0 +1,2 @@
+
+return "I am init.lua of fancy_y!"
diff --git a/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua b/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua
new file mode 100644
index 0000000000..a312572c5b
--- /dev/null
+++ b/test/functional/fixtures/start/nvim-leftpad/lua/async_leftpad.lua
@@ -0,0 +1,3 @@
+return function (val, res)
+ vim.loop.new_async(function() _G[res] = require'leftpad'(val) end):send()
+end
diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua
index f2ced8942d..141d9583e6 100644
--- a/test/functional/legacy/delete_spec.lua
+++ b/test/functional/legacy/delete_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, source = helpers.clear, helpers.source
local eq, eval, command = helpers.eq, helpers.eval, helpers.command
+local exc_exec = helpers.exc_exec
describe('Test for delete()', function()
before_each(clear)
@@ -38,7 +39,7 @@ describe('Test for delete()', function()
eq(eval("['a', 'b']"), eval("readfile('Xdir1/Xfile')"))
eq(1, eval("isdirectory('Xdir1/subdir')"))
eq(eval("['a', 'b']"), eval("readfile('Xdir1/subdir/Xfile')"))
- eq(1, eval("isdirectory('Xdir1/empty')"))
+ eq(1, eval("'Xdir1/empty'->isdirectory()"))
eq(0, eval("delete('Xdir1', 'rf')"))
eq(0, eval("isdirectory('Xdir1')"))
eq(-1, eval("delete('Xdir1', 'd')"))
@@ -114,4 +115,10 @@ describe('Test for delete()', function()
eq(0, eval("delete('Xdir4/Xfile')"))
eq(0, eval("delete('Xdir4', 'd')"))
end)
+
+ it('gives correct emsgs', function()
+ eq('Vim(call):E474: Invalid argument', exc_exec("call delete('')"))
+ eq('Vim(call):E15: Invalid expression: 0',
+ exc_exec("call delete('foo', 0)"))
+ end)
end)
diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua
index 3b407ce5f5..b5de5cd232 100644
--- a/test/functional/legacy/eval_spec.lua
+++ b/test/functional/legacy/eval_spec.lua
@@ -600,7 +600,6 @@ describe('eval', function()
command([[call ErrExe('call setreg(1)')]])
command([[call ErrExe('call setreg(1, 2, 3, 4)')]])
command([=[call ErrExe('call setreg([], 2)')]=])
- command([[call ErrExe('call setreg(1, {})')]])
command([=[call ErrExe('call setreg(1, 2, [])')]=])
command([=[call ErrExe('call setreg("/", ["1", "2"])')]=])
command([=[call ErrExe('call setreg("=", ["1", "2"])')]=])
@@ -615,8 +614,6 @@ describe('eval', function()
Vim(call):E118: Too many arguments for function: setreg
Executing call setreg([], 2)
Vim(call):E730: using List as a String
- Executing call setreg(1, {})
- Vim(call):E731: using Dictionary as a String
Executing call setreg(1, 2, [])
Vim(call):E730: using List as a String
Executing call setreg("/", ["1", "2"])
diff --git a/test/functional/legacy/expand_spec.lua b/test/functional/legacy/expand_spec.lua
index f238128b31..cd3713eabe 100644
--- a/test/functional/legacy/expand_spec.lua
+++ b/test/functional/legacy/expand_spec.lua
@@ -81,7 +81,7 @@ describe('expand file name', function()
call assert_equal('e Xfile1', expandcmd('e %'))
edit Xfile2
edit Xfile1
- call assert_equal('e Xfile2', expandcmd('e #'))
+ call assert_equal('e Xfile2', 'e #'->expandcmd())
edit Xfile2
edit Xfile3
edit Xfile4
diff --git a/test/functional/legacy/file_perm_spec.lua b/test/functional/legacy/file_perm_spec.lua
index 8fdee95e91..ccdbfe0534 100644
--- a/test/functional/legacy/file_perm_spec.lua
+++ b/test/functional/legacy/file_perm_spec.lua
@@ -3,7 +3,7 @@ require('os')
local helpers = require('test.functional.helpers')(after_each)
local clear, call, eq = helpers.clear, helpers.call, helpers.eq
-local neq, exc_exec = helpers.neq, helpers.exc_exec
+local neq, exc_exec, eval = helpers.neq, helpers.exc_exec, helpers.eval
describe('Test getting and setting file permissions', function()
local tempfile = helpers.tmpname()
@@ -14,11 +14,12 @@ describe('Test getting and setting file permissions', function()
end)
it('file permissions', function()
+ -- eval() is used to test VimL method syntax for setfperm() and getfperm()
eq('', call('getfperm', tempfile))
- eq(0, call('setfperm', tempfile, 'r--------'))
+ eq(0, eval("'" .. tempfile .. "'->setfperm('r--------')"))
call('writefile', {'one'}, tempfile)
- eq(9, call('len', call('getfperm', tempfile)))
+ eq(9, eval("len('" .. tempfile .. "'->getfperm())"))
eq(1, call('setfperm', tempfile, 'rwx------'))
if helpers.is_os('win') then
diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua
index 7e859bf0cf..6a5538c26f 100644
--- a/test/functional/legacy/fnamemodify_spec.lua
+++ b/test/functional/legacy/fnamemodify_spec.lua
@@ -60,12 +60,6 @@ describe('filename modifiers', function()
call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S'))
endif
endfunc
-
- func Test_expand()
- new
- call assert_equal("", expand('%:S'))
- quit
- endfunc
]=])
end)
@@ -73,9 +67,4 @@ describe('filename modifiers', function()
call('Test_fnamemodify')
expected_empty()
end)
-
- it('works for :S in an unnamed buffer', function()
- call('Test_expand')
- expected_empty()
- end)
end)
diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua
index fdf79d55b2..8551c3d2a0 100644
--- a/test/functional/lua/api_spec.lua
+++ b/test/functional/lua/api_spec.lua
@@ -194,6 +194,10 @@ describe('luaeval(vim.api.…)', function()
exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]]))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]]))
+
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
+ exc_exec([[call luaeval("vim.api.nvim_set_keymap('', '', '', '')")]]))
+
-- TODO: check for errors with Tabpage argument
-- TODO: check for errors with Window argument
-- TODO: check for errors with Buffer argument
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index 073927bf22..c83a50b78b 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -1050,6 +1050,102 @@ describe('lua: nvim_buf_attach on_bytes', function()
}
end)
+ it("sends updates on U", function()
+ feed("ggiAAA<cr>BBB")
+ feed("<esc>gg$a CCC")
+
+ local check_events = setup_eventcheck(verify, nil)
+
+ feed("ggU")
+
+ check_events {
+ { "test1", "bytes", 1, 6, 0, 7, 7, 0, 0, 0, 0, 3, 3 };
+ }
+ end)
+
+ it("delete in completely empty buffer", function()
+ local check_events = setup_eventcheck(verify, nil)
+
+ command "delete"
+ check_events { }
+ end)
+
+ it("delete the only line of a buffer", function()
+ local check_events = setup_eventcheck(verify, {"AAA"})
+
+ command "delete"
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 1, 0, 4, 1, 0, 1 };
+ }
+ end)
+
+ it("delete the last line of a buffer with two lines", function()
+ local check_events = setup_eventcheck(verify, {"AAA", "BBB"})
+
+ command "2delete"
+ check_events {
+ { "test1", "bytes", 1, 3, 1, 0, 4, 1, 0, 4, 0, 0, 0 };
+ }
+ end)
+
+ it(":sort lines", function()
+ local check_events = setup_eventcheck(verify, {"CCC", "BBB", "AAA"})
+
+ command "%sort"
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 3, 0, 12, 3, 0, 12 };
+ }
+ end)
+
+ it("handles already sorted lines", function()
+ local check_events = setup_eventcheck(verify, {"AAA", "BBB", "CCC"})
+
+ command "%sort"
+ check_events { }
+ end)
+
+ local function test_lockmarks(mode)
+ local description = (mode ~= "") and mode or "(baseline)"
+ it("test_lockmarks " .. description .. " %delete _", function()
+ local check_events = setup_eventcheck(verify, {"AAA", "BBB", "CCC"})
+
+ command(mode .. " %delete _")
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 3, 0, 12, 1, 0, 1 };
+ }
+ end)
+
+ it("test_lockmarks " .. description .. " append()", function()
+ local check_events = setup_eventcheck(verify)
+
+ command(mode .. " call append(0, 'CCC')")
+ check_events {
+ { "test1", "bytes", 1, 2, 0, 0, 0, 0, 0, 0, 1, 0, 4 };
+ }
+
+ command(mode .. " call append(1, 'BBBB')")
+ check_events {
+ { "test1", "bytes", 1, 3, 1, 0, 4, 0, 0, 0, 1, 0, 5 };
+ }
+
+ command(mode .. " call append(2, '')")
+ check_events {
+ { "test1", "bytes", 1, 4, 2, 0, 9, 0, 0, 0, 1, 0, 1 };
+ }
+
+ command(mode .. " $delete _")
+ check_events {
+ { "test1", "bytes", 1, 5, 3, 0, 10, 1, 0, 1, 0, 0, 0 };
+ }
+
+ eq("CCC|BBBB|", table.concat(meths.buf_get_lines(0, 0, -1, true), "|"))
+ end)
+ end
+
+ -- check that behavior is identical with and without "lockmarks"
+ test_lockmarks ""
+ test_lockmarks "lockmarks"
+
teardown(function()
os.remove "Xtest-reload"
os.remove "Xtest-undofile"
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index 29dd5c60da..1cbfa224cc 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -14,48 +14,32 @@ describe('vim.diagnostic', function()
exec_lua [[
require('vim.diagnostic')
- function make_error(msg, x1, y1, x2, y2)
+ function make_diagnostic(msg, x1, y1, x2, y2, severity, source)
return {
lnum = x1,
col = y1,
end_lnum = x2,
end_col = y2,
message = msg,
- severity = vim.diagnostic.severity.ERROR,
+ severity = severity,
+ source = source,
}
end
- function make_warning(msg, x1, y1, x2, y2)
- return {
- lnum = x1,
- col = y1,
- end_lnum = x2,
- end_col = y2,
- message = msg,
- severity = vim.diagnostic.severity.WARN,
- }
+ function make_error(msg, x1, y1, x2, y2, source)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source)
end
- function make_info(msg, x1, y1, x2, y2)
- return {
- lnum = x1,
- col = y1,
- end_lnum = x2,
- end_col = y2,
- message = msg,
- severity = vim.diagnostic.severity.INFO,
- }
+ function make_warning(msg, x1, y1, x2, y2, source)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source)
end
- function make_hint(msg, x1, y1, x2, y2)
- return {
- lnum = x1,
- col = y1,
- end_lnum = x2,
- end_col = y2,
- message = msg,
- severity = vim.diagnostic.severity.HINT,
- }
+ function make_info(msg, x1, y1, x2, y2, source)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source)
+ end
+
+ function make_hint(msg, x1, y1, x2, y2, source)
+ return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source)
end
function count_diagnostics(bufnr, severity, namespace)
@@ -489,6 +473,78 @@ describe('vim.diagnostic', function()
end)
describe('config()', function()
+ it('works with global, namespace, and ephemeral options', function()
+ eq(1, exec_lua [[
+ vim.diagnostic.config({
+ virtual_text = false,
+ })
+
+ vim.diagnostic.config({
+ virtual_text = true,
+ underline = false,
+ }, diagnostic_ns)
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error('Some Error', 4, 4, 4, 4),
+ })
+
+ return count_extmarks(diagnostic_bufnr, diagnostic_ns)
+ ]])
+
+ eq(1, exec_lua [[
+ vim.diagnostic.config({
+ virtual_text = false,
+ })
+
+ vim.diagnostic.config({
+ virtual_text = false,
+ underline = false,
+ }, diagnostic_ns)
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_error('Some Error', 4, 4, 4, 4),
+ }, {virtual_text = true})
+
+ return count_extmarks(diagnostic_bufnr, diagnostic_ns)
+ ]])
+
+ eq(0, exec_lua [[
+ vim.diagnostic.config({
+ virtual_text = false,
+ })
+
+ vim.diagnostic.config({
+ virtual_text = {severity=vim.diagnostic.severity.ERROR},
+ underline = false,
+ }, diagnostic_ns)
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_warning('Some Warning', 4, 4, 4, 4),
+ }, {virtual_text = true})
+
+ return count_extmarks(diagnostic_bufnr, diagnostic_ns)
+ ]])
+
+ eq(1, exec_lua [[
+ vim.diagnostic.config({
+ virtual_text = false,
+ })
+
+ vim.diagnostic.config({
+ virtual_text = {severity=vim.diagnostic.severity.ERROR},
+ underline = false,
+ }, diagnostic_ns)
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_warning('Some Warning', 4, 4, 4, 4),
+ }, {
+ virtual_text = {} -- An empty table uses default values
+ })
+
+ return count_extmarks(diagnostic_bufnr, diagnostic_ns)
+ ]])
+ end)
+
it('can use functions for config values', function()
exec_lua [[
vim.diagnostic.config({
@@ -590,6 +646,118 @@ describe('vim.diagnostic', function()
eq({'Error', 'Warn', 'Info'}, result[1])
eq({'Info', 'Warn', 'Error'}, result[2])
end)
+
+ it('can show diagnostic sources in virtual text', function()
+ local result = exec_lua [[
+ local diagnostics = {
+ make_error('Some error', 0, 0, 0, 0, 'source x'),
+ }
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, {
+ underline = false,
+ virtual_text = {
+ prefix = '',
+ source = 'always',
+ }
+ })
+
+ local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true})
+ local virt_text = extmarks[1][4].virt_text[2][1]
+ return virt_text
+ ]]
+ eq(' source x: Some error', result)
+
+ result = exec_lua [[
+ vim.diagnostic.config({
+ underline = false,
+ virtual_text = {
+ prefix = '',
+ source = 'if_many',
+ }
+ }, diagnostic_ns)
+
+ local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true})
+ local virt_text = extmarks[1][4].virt_text[2][1]
+ return virt_text
+ ]]
+ eq(' Some error', result)
+
+ result = exec_lua [[
+ local diagnostics = {
+ make_error('Some error', 0, 0, 0, 0, 'source x'),
+ make_error('Another error', 1, 1, 1, 1, 'source y'),
+ }
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics, {
+ underline = false,
+ virtual_text = {
+ prefix = '',
+ source = 'if_many',
+ }
+ })
+
+ local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true})
+ local virt_text = {extmarks[1][4].virt_text[2][1], extmarks[2][4].virt_text[2][1]}
+ return virt_text
+ ]]
+ eq(' source x: Some error', result[1])
+ eq(' source y: Another error', result[2])
+ end)
+
+ it('supports a format function for diagnostic messages', function()
+ local result = exec_lua [[
+ vim.diagnostic.config({
+ underline = false,
+ virtual_text = {
+ prefix = '',
+ format = function(diagnostic)
+ if diagnostic.severity == vim.diagnostic.severity.ERROR then
+ return string.format("🔥 %s", diagnostic.message)
+ end
+ return string.format("👀 %s", diagnostic.message)
+ end,
+ }
+ })
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_warning('Warning', 0, 0, 0, 0),
+ make_error('Error', 1, 0, 1, 0),
+ })
+
+ local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true})
+ return {extmarks[1][4].virt_text, extmarks[2][4].virt_text}
+ ]]
+ eq(" 👀 Warning", result[1][2][1])
+ eq(" 🔥 Error", result[2][2][1])
+ end)
+
+ it('includes source for formatted diagnostics', function()
+ local result = exec_lua [[
+ vim.diagnostic.config({
+ underline = false,
+ virtual_text = {
+ prefix = '',
+ source = 'always',
+ format = function(diagnostic)
+ if diagnostic.severity == vim.diagnostic.severity.ERROR then
+ return string.format("🔥 %s", diagnostic.message)
+ end
+ return string.format("👀 %s", diagnostic.message)
+ end,
+ }
+ })
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {
+ make_warning('Warning', 0, 0, 0, 0, 'some_linter'),
+ make_error('Error', 1, 0, 1, 0, 'another_linter'),
+ })
+
+ local extmarks = vim.api.nvim_buf_get_extmarks(diagnostic_bufnr, diagnostic_ns, 0, -1, {details = true})
+ return {extmarks[1][4].virt_text, extmarks[2][4].virt_text}
+ ]]
+ eq(" some_linter: 👀 Warning", result[1][2][1])
+ eq(" another_linter: 🔥 Error", result[2][2][1])
+ end)
end)
describe('set()', function()
@@ -778,10 +946,124 @@ describe('vim.diagnostic', function()
return count_extmarks(diagnostic_bufnr, diagnostic_ns)
]])
end)
+
+ it('sets signs', function()
+ local result = exec_lua [[
+ vim.diagnostic.config({
+ signs = true,
+ })
+
+ local diagnostics = {
+ make_error('Error', 1, 1, 1, 2),
+ make_warning('Warning', 3, 3, 3, 3),
+ }
+
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+
+ return vim.fn.sign_getplaced(diagnostic_bufnr, {group = '*'})[1].signs
+ ]]
+
+ eq({2, 'DiagnosticSignError'}, {result[1].lnum, result[1].name})
+ eq({4, 'DiagnosticSignWarn'}, {result[2].lnum, result[2].name})
+ end)
end)
- describe('show_line_diagnostics()', function()
- it('creates floating window and returns popup bufnr and winnr if current line contains diagnostics', function()
+ describe('open_float()', function()
+ it('can show diagnostics from the whole buffer', function()
+ eq({'1. Syntax error', '2. Some warning'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 0, 1, 0, 3),
+ make_warning("Some warning", 1, 1, 1, 3),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header = false})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+ end)
+
+ it('can show diagnostics from a single line', function()
+ -- Using cursor position
+ eq({'1. Some warning'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 0, 1, 0, 3),
+ make_warning("Some warning", 1, 1, 1, 3),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ vim.api.nvim_win_set_cursor(0, {2, 1})
+ local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="line"})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ -- With specified position
+ eq({'1. Some warning'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 0, 1, 0, 3),
+ make_warning("Some warning", 1, 1, 1, 3),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ vim.api.nvim_win_set_cursor(0, {1, 1})
+ local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="line", pos=1})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+ end)
+
+ it('can show diagnostics from a specific position', function()
+ -- Using cursor position
+ eq({'1. Syntax error'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 1, 1, 1, 2),
+ make_warning("Some warning", 1, 3, 1, 4),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ vim.api.nvim_win_set_cursor(0, {2, 2})
+ local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor"})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ -- With specified position
+ eq({'1. Some warning'}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 1, 1, 1, 2),
+ make_warning("Some warning", 1, 3, 1, 4),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ vim.api.nvim_win_set_cursor(0, {1, 1})
+ local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor", pos={1,3}})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ -- With column position past the end of the line. #16062
+ eq({'1. Syntax error'}, exec_lua [[
+ local first_line_len = #vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, 1, true)[1]
+ local diagnostics = {
+ make_error("Syntax error", 0, first_line_len + 1, 1, 0),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ vim.api.nvim_win_set_cursor(0, {1, 1})
+ local float_bufnr, winnr = vim.diagnostic.open_float(0, {show_header=false, scope="cursor", pos={0,first_line_len}})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+ end)
+
+ it('creates floating window and returns float bufnr and winnr if current line contains diagnostics', function()
-- Two lines:
-- Diagnostic:
-- 1. <msg>
@@ -791,8 +1073,10 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
- local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics()
- return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {scope="line"})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return #lines
]])
end)
@@ -808,8 +1092,10 @@ describe('vim.diagnostic', function()
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, buf_1_diagnostics)
vim.diagnostic.set(other_ns, other_bufnr, buf_2_diagnostics)
- local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics()
- return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
+ local float_bufnr, winnr = vim.diagnostic.open_float()
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return #lines
]])
end)
@@ -824,12 +1110,14 @@ describe('vim.diagnostic', function()
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diagnostics)
vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diagnostics)
- local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics({namespace = diagnostic_ns})
- return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {namespace = diagnostic_ns})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return #lines
]])
end)
- it('creates floating window and returns popup bufnr and winnr without header, if requested', function()
+ it('creates floating window and returns float bufnr and winnr without header, if requested', function()
-- One line (since no header):
-- 1. <msg>
eq(1, exec_lua [[
@@ -838,39 +1126,105 @@ describe('vim.diagnostic', function()
}
vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
- local popup_bufnr, winnr = vim.diagnostic.show_line_diagnostics {show_header = false}
- return #vim.api.nvim_buf_get_lines(popup_bufnr, 0, -1, false)
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {show_header = false})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return #lines
]])
end)
- end)
- describe('set_signs()', function()
- -- TODO(tjdevries): Find out why signs are not displayed when set from Lua...??
- pending('sets signs by default', function()
- exec_lua [[
- vim.diagnostic.config({
- update_in_insert = true,
- signs = true,
+ it('clamps diagnostic line numbers within the valid range', function()
+ eq(1, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 6, 0, 6, 0),
+ }
+ vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {show_header = false, scope = "line", pos = 5})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return #lines
+ ]])
+ end)
+
+ it('can show diagnostic source', function()
+ exec_lua [[vim.api.nvim_win_set_buf(0, diagnostic_bufnr)]]
+
+ eq({"1. Syntax error"}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 0, 1, 0, 3, "source x"),
+ }
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {
+ show_header = false,
+ source = "if_many",
})
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ eq({"1. source x: Syntax error"}, exec_lua [[
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {
+ show_header = false,
+ source = "always",
+ })
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+ eq({"1. source x: Syntax error", "2. source y: Another error"}, exec_lua [[
local diagnostics = {
- make_error('Delayed Diagnostic', 1, 1, 1, 2),
- make_error('Delayed Diagnostic', 3, 3, 3, 3),
+ make_error("Syntax error", 0, 1, 0, 3, "source x"),
+ make_error("Another error", 0, 1, 0, 3, "source y"),
+ }
+ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {
+ show_header = false,
+ source = "if_many",
+ })
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+ end)
+
+ it('respects severity_sort', function()
+ exec_lua [[vim.api.nvim_win_set_buf(0, diagnostic_bufnr)]]
+
+ eq({"1. Syntax error", "2. Info", "3. Error", "4. Warning"}, exec_lua [[
+ local diagnostics = {
+ make_error("Syntax error", 0, 1, 0, 3),
+ make_info('Info', 0, 3, 0, 4),
+ make_error('Error', 0, 2, 0, 2),
+ make_warning('Warning', 0, 0, 0, 1),
}
- vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics)
- vim.diagnostic._set_signs(diagnostic_ns, diagnostic_bufnr, diagnostics)
- -- return vim.fn.sign_getplaced()
- ]]
+ vim.diagnostic.config({severity_sort = false})
- nvim("input", "o")
- nvim("input", "<esc>")
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false })
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
+
+ eq({"1. Syntax error", "2. Error", "3. Warning", "4. Info"}, exec_lua [[
+ vim.diagnostic.config({severity_sort = true})
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false })
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
+ ]])
- -- TODO(tjdevries): Find a way to get the signs to display in the test...
- eq(nil, exec_lua [[
- return im.fn.sign_getplaced()[1].signs
+ eq({"1. Info", "2. Warning", "3. Error", "4. Syntax error"}, exec_lua [[
+ vim.diagnostic.config({severity_sort = { reverse = true } })
+ local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, { show_header = false })
+ local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false)
+ vim.api.nvim_win_close(winnr, true)
+ return lines
]])
end)
end)
diff --git a/test/functional/lua/ffi_spec.lua b/test/functional/lua/ffi_spec.lua
new file mode 100644
index 0000000000..80c01a2b8c
--- /dev/null
+++ b/test/functional/lua/ffi_spec.lua
@@ -0,0 +1,62 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local clear = helpers.clear
+
+before_each(clear)
+
+describe('ffi.cdef', function()
+ it('can use Neovim core functions', function()
+ if not exec_lua("return pcall(require, 'ffi')") then
+ pending('missing LuaJIT FFI')
+ end
+
+ eq(12, exec_lua[[
+ local ffi = require('ffi')
+
+ ffi.cdef('int curwin_col_off(void);')
+
+ vim.cmd('set number numberwidth=4 signcolumn=yes:4')
+
+ return ffi.C.curwin_col_off()
+ ]])
+
+ eq(20, exec_lua[=[
+ local ffi = require('ffi')
+
+ ffi.cdef[[
+ typedef unsigned char char_u;
+ typedef struct window_S win_T;
+ typedef struct {} stl_hlrec_t;
+ typedef struct {} StlClickRecord;
+ typedef struct {} Error;
+
+ win_T *find_window_by_handle(int Window, Error *err);
+
+ int build_stl_str_hl(
+ win_T *wp,
+ char_u *out,
+ size_t outlen,
+ char_u *fmt,
+ int use_sandbox,
+ char_u fillchar,
+ int maxwidth,
+ stl_hlrec_t **hltab,
+ StlClickRecord **tabtab
+ );
+ ]]
+
+ return ffi.C.build_stl_str_hl(
+ ffi.C.find_window_by_handle(0, ffi.new('Error')),
+ ffi.new('char_u[1024]'),
+ 1024,
+ ffi.cast('char_u*', 'StatusLineOfLength20'),
+ 0,
+ 0,
+ 0,
+ nil,
+ nil
+ )
+ ]=])
+ end)
+end)
diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua
new file mode 100644
index 0000000000..fbb21bfd57
--- /dev/null
+++ b/test/functional/lua/json_spec.lua
@@ -0,0 +1,133 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local NIL = helpers.NIL
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+
+describe('vim.json.decode function', function()
+ before_each(function()
+ clear()
+ end)
+
+ it('parses null, true, false', function()
+ eq(NIL, exec_lua([[return vim.json.decode('null')]]))
+ eq(true, exec_lua([[return vim.json.decode('true')]]))
+ eq(false, exec_lua([[return vim.json.decode('false')]]))
+ end)
+
+ it('parses integer numbers', function()
+ eq(100000, exec_lua([[return vim.json.decode('100000')]]))
+ eq(-100000, exec_lua([[return vim.json.decode('-100000')]]))
+ eq(100000, exec_lua([[return vim.json.decode(' 100000 ')]]))
+ eq(-100000, exec_lua([[return vim.json.decode(' -100000 ')]]))
+ eq(0, exec_lua([[return vim.json.decode('0')]]))
+ eq(0, exec_lua([[return vim.json.decode('-0')]]))
+ end)
+
+ it('parses floating-point numbers', function()
+ -- This behavior differs from vim.fn.json_decode, which return '100000.0'
+ eq('100000', exec_lua([[return tostring(vim.json.decode('100000.0'))]]))
+ eq(100000.5, exec_lua([[return vim.json.decode('100000.5')]]))
+ eq(-100000.5, exec_lua([[return vim.json.decode('-100000.5')]]))
+ eq(-100000.5e50, exec_lua([[return vim.json.decode('-100000.5e50')]]))
+ eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e50')]]))
+ eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e+50')]]))
+ eq(-100000.5e-50, exec_lua([[return vim.json.decode('-100000.5e-50')]]))
+ eq(100000.5e-50, exec_lua([[return vim.json.decode('100000.5e-50')]]))
+ eq(100000e-50, exec_lua([[return vim.json.decode('100000e-50')]]))
+ eq(0.5, exec_lua([[return vim.json.decode('0.5')]]))
+ eq(0.005, exec_lua([[return vim.json.decode('0.005')]]))
+ eq(0.005, exec_lua([[return vim.json.decode('0.00500')]]))
+ eq(0.5, exec_lua([[return vim.json.decode('0.00500e+002')]]))
+ eq(0.00005, exec_lua([[return vim.json.decode('0.00500e-002')]]))
+
+ eq(-0.0, exec_lua([[return vim.json.decode('-0.0')]]))
+ eq(-0.0, exec_lua([[return vim.json.decode('-0.0e0')]]))
+ eq(-0.0, exec_lua([[return vim.json.decode('-0.0e+0')]]))
+ eq(-0.0, exec_lua([[return vim.json.decode('-0.0e-0')]]))
+ eq(-0.0, exec_lua([[return vim.json.decode('-0e-0')]]))
+ eq(-0.0, exec_lua([[return vim.json.decode('-0e-2')]]))
+ eq(-0.0, exec_lua([[return vim.json.decode('-0e+2')]]))
+
+ eq(0.0, exec_lua([[return vim.json.decode('0.0')]]))
+ eq(0.0, exec_lua([[return vim.json.decode('0.0e0')]]))
+ eq(0.0, exec_lua([[return vim.json.decode('0.0e+0')]]))
+ eq(0.0, exec_lua([[return vim.json.decode('0.0e-0')]]))
+ eq(0.0, exec_lua([[return vim.json.decode('0e-0')]]))
+ eq(0.0, exec_lua([[return vim.json.decode('0e-2')]]))
+ eq(0.0, exec_lua([[return vim.json.decode('0e+2')]]))
+ end)
+
+ it('parses containers', function()
+ eq({1}, exec_lua([[return vim.json.decode('[1]')]]))
+ eq({NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]]))
+ eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]]))
+ eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}},
+ exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]]))
+ end)
+
+ it('parses strings properly', function()
+ eq('\n', exec_lua([=[return vim.json.decode([["\n"]])]=]))
+ eq('', exec_lua([=[return vim.json.decode([[""]])]=]))
+ eq('\\/"\t\b\n\r\f', exec_lua([=[return vim.json.decode([["\\\/\"\t\b\n\r\f"]])]=]))
+ eq('/a', exec_lua([=[return vim.json.decode([["\/a"]])]=]))
+ -- Unicode characters: 2-byte, 3-byte
+ eq('«',exec_lua([=[return vim.json.decode([["«"]])]=]))
+ eq('ફ',exec_lua([=[return vim.json.decode([["ફ"]])]=]))
+ end)
+
+ it('parses surrogate pairs properly', function()
+ eq('\240\144\128\128', exec_lua([[return vim.json.decode('"\\uD800\\uDC00"')]]))
+ end)
+
+ it('accepts all spaces in every position where space may be put', function()
+ local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t'
+ local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s)
+ eq({key={'val', 'val2'}, key2=1}, exec_lua([[return vim.json.decode(...)]], str))
+ end)
+
+end)
+
+describe('vim.json.encode function', function()
+ before_each(function()
+ clear()
+ end)
+
+ it('dumps strings', function()
+ eq('"Test"', exec_lua([[return vim.json.encode('Test')]]))
+ eq('""', exec_lua([[return vim.json.encode('')]]))
+ eq('"\\t"', exec_lua([[return vim.json.encode('\t')]]))
+ eq('"\\n"', exec_lua([[return vim.json.encode('\n')]]))
+ -- vim.fn.json_encode return \\u001B
+ eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]]))
+ eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]]))
+ end)
+
+ it('dumps numbers', function()
+ eq('0', exec_lua([[return vim.json.encode(0)]]))
+ eq('10', exec_lua([[return vim.json.encode(10)]]))
+ eq('-10', exec_lua([[return vim.json.encode(-10)]]))
+ end)
+
+ it('dumps floats', function()
+ eq('10.5', exec_lua([[return vim.json.encode(10.5)]]))
+ eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]]))
+ eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]]))
+ end)
+
+ it('dumps lists', function()
+ eq('[]', exec_lua([[return vim.json.encode({})]]))
+ eq('[[]]', exec_lua([[return vim.json.encode({{}})]]))
+ eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]]))
+ end)
+
+ it('dumps dictionaries', function()
+ eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]]))
+ eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]]))
+ end)
+
+ it('dumps vim.NIL', function()
+ eq('null', exec_lua([[return vim.json.encode(vim.NIL)]]))
+ end)
+
+end)
diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua
index 255e99032f..0675ec9abd 100644
--- a/test/functional/lua/luaeval_spec.lua
+++ b/test/functional/lua/luaeval_spec.lua
@@ -86,14 +86,15 @@ describe('luaeval()', function()
-- meaningful later.
it('correctly evaluates scalars', function()
+ -- Also test method call (->) syntax
eq(1, funcs.luaeval('1'))
- eq(0, eval('type(luaeval("1"))'))
+ eq(0, eval('"1"->luaeval()->type()'))
eq(1.5, funcs.luaeval('1.5'))
- eq(5, eval('type(luaeval("1.5"))'))
+ eq(5, eval('"1.5"->luaeval()->type()'))
eq("test", funcs.luaeval('"test"'))
- eq(1, eval('type(luaeval("\'test\'"))'))
+ eq(1, eval('"\'test\'"->luaeval()->type()'))
eq('', funcs.luaeval('""'))
eq('\000', funcs.luaeval([['\0']]))
diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua
new file mode 100644
index 0000000000..94f1b5840b
--- /dev/null
+++ b/test/functional/lua/ui_spec.lua
@@ -0,0 +1,46 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
+local exec_lua = helpers.exec_lua
+local clear = helpers.clear
+
+describe('vim.ui', function()
+ before_each(function()
+ clear()
+ end)
+
+
+ describe('select', function()
+ it('can select an item', function()
+ local result = exec_lua[[
+ local items = {
+ { name = 'Item 1' },
+ { name = 'Item 2' },
+ }
+ local opts = {
+ format_item = function(entry)
+ return entry.name
+ end
+ }
+ local selected
+ local cb = function(item)
+ selected = item
+ end
+ -- inputlist would require input and block the test;
+ local choices
+ vim.fn.inputlist = function(x)
+ choices = x
+ return 1
+ end
+ vim.ui.select(items, opts, cb)
+ vim.wait(100, function() return selected ~= nil end)
+ return {selected, choices}
+ ]]
+ eq({ name = 'Item 1' }, result[1])
+ eq({
+ 'Select one of:',
+ '1: Item 1',
+ '2: Item 2',
+ }, result[2])
+ end)
+ end)
+end)
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
index 052a8a1ecd..81f1820986 100644
--- a/test/functional/lua/uri_spec.lua
+++ b/test/functional/lua/uri_spec.lua
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local exec_lua = helpers.exec_lua
local eq = helpers.eq
+local write_file = require('test.helpers').write_file
describe('URI methods', function()
before_each(function()
@@ -158,6 +159,22 @@ describe('URI methods', function()
end)
+ describe('uri from bufnr', function()
+ it('Windows paths should not be treated as uris', function()
+ if not helpers.iswin() then return end
+
+ local file = helpers.tmpname()
+ write_file(file, 'Test content')
+ local test_case = string.format([[
+ local file = '%s'
+ return vim.uri_from_bufnr(vim.fn.bufadd(file))
+ ]], file)
+ local expected_uri = 'file:///' .. file:gsub("\\", "/")
+ eq(expected_uri, exec_lua(test_case))
+ os.remove(file)
+ 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'
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index f3d265cb92..3123063b8c 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -237,29 +237,34 @@ describe('lua stdlib', function()
end)
it("vim.split", function()
- local split = function(str, sep, plain)
- return exec_lua('return vim.split(...)', str, sep, plain)
+ local split = function(str, sep, kwargs)
+ return exec_lua('return vim.split(...)', str, sep, kwargs)
end
local tests = {
- { "a,b", ",", false, { 'a', 'b' } },
- { ":aa::bb:", ":", false, { '', 'aa', '', 'bb', '' } },
- { "::ee::ff:", ":", false, { '', '', 'ee', '', 'ff', '' } },
- { "ab", ".", false, { '', '', '' } },
- { "a1b2c", "[0-9]", false, { 'a', 'b', 'c' } },
- { "xy", "", false, { 'x', 'y' } },
- { "here be dragons", " ", false, { "here", "be", "dragons"} },
- { "axaby", "ab?", false, { '', 'x', 'y' } },
- { "f v2v v3v w2w ", "([vw])2%1", false, { 'f ', ' v3v ', ' ' } },
- { "", "", false, {} },
- { "", "a", false, { '' } },
- { "x*yz*oo*l", "*", true, { 'x', 'yz', 'oo', 'l' } },
+ { "a,b", ",", false, false, { 'a', 'b' } },
+ { ":aa::bb:", ":", false, false, { '', 'aa', '', 'bb', '' } },
+ { ":aa::bb:", ":", false, true, { 'aa', '', 'bb' } },
+ { "::ee::ff:", ":", false, false, { '', '', 'ee', '', 'ff', '' } },
+ { "::ee::ff:", ":", false, true, { 'ee', '', 'ff' } },
+ { "ab", ".", false, false, { '', '', '' } },
+ { "a1b2c", "[0-9]", false, false, { 'a', 'b', 'c' } },
+ { "xy", "", false, false, { 'x', 'y' } },
+ { "here be dragons", " ", false, false, { "here", "be", "dragons"} },
+ { "axaby", "ab?", false, false, { '', 'x', 'y' } },
+ { "f v2v v3v w2w ", "([vw])2%1", false, false, { 'f ', ' v3v ', ' ' } },
+ { "", "", false, false, {} },
+ { "", "a", false, false, { '' } },
+ { "x*yz*oo*l", "*", true, false, { 'x', 'yz', 'oo', 'l' } },
}
for _, t in ipairs(tests) do
- eq(t[4], split(t[1], t[2], t[3]))
+ eq(t[5], split(t[1], t[2], {plain=t[3], trimempty=t[4]}))
end
+ -- Test old signature
+ eq({'x', 'yz', 'oo', 'l'}, split("x*yz*oo*l", "*", true))
+
local loops = {
{ "abc", ".-" },
}
@@ -283,9 +288,8 @@ describe('lua stdlib', function()
vim/shared.lua:0: in function <vim/shared.lua:0>]]),
pcall_err(split, 'string', 1))
eq(dedent([[
- Error executing lua: vim/shared.lua:0: plain: expected boolean, got number
+ Error executing lua: vim/shared.lua:0: kwargs: expected table, got number
stack traceback:
- vim/shared.lua:0: in function 'gsplit'
vim/shared.lua:0: in function <vim/shared.lua:0>]]),
pcall_err(split, 'string', 'string', 1))
end)
@@ -992,6 +996,9 @@ describe('lua stdlib', function()
vim.g.to_delete = nil
]]
eq(NIL, funcs.luaeval "vim.g.to_delete")
+
+ matches([[^Error executing lua: .*: attempt to index .* nil value]],
+ pcall_err(exec_lua, 'return vim.g[0].testing'))
end)
it('vim.b', function()
@@ -1001,18 +1008,25 @@ describe('lua stdlib', function()
vim.api.nvim_buf_set_var(0, "floaty", 5120.1)
vim.api.nvim_buf_set_var(0, "nullvar", vim.NIL)
vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"})
+ BUF = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_var(BUF, "testing", "bye")
]]
eq('hi', funcs.luaeval "vim.b.testing")
+ eq('bye', funcs.luaeval "vim.b[BUF].testing")
eq(123, funcs.luaeval "vim.b.other")
eq(5120.1, funcs.luaeval "vim.b.floaty")
eq(NIL, funcs.luaeval "vim.b.nonexistant")
+ eq(NIL, funcs.luaeval "vim.b[BUF].nonexistant")
eq(NIL, funcs.luaeval "vim.b.nullvar")
-- lost over RPC, so test locally:
eq({false, true}, exec_lua [[
return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL}
]])
+ matches([[^Error executing lua: .*: attempt to index .* nil value]],
+ pcall_err(exec_lua, 'return vim.b[BUF][0].testing'))
+
eq({hello="world"}, funcs.luaeval "vim.b.to_delete")
exec_lua [[
vim.b.to_delete = nil
@@ -1033,11 +1047,22 @@ describe('lua stdlib', function()
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"})
+ BUF = vim.api.nvim_create_buf(false, true)
+ WIN = vim.api.nvim_open_win(BUF, false, {
+ width=10, height=10,
+ relative='win', row=0, col=0
+ })
+ vim.api.nvim_win_set_var(WIN, "testing", "bye")
]]
eq('hi', funcs.luaeval "vim.w.testing")
+ eq('bye', funcs.luaeval "vim.w[WIN].testing")
eq(123, funcs.luaeval "vim.w.other")
eq(NIL, funcs.luaeval "vim.w.nonexistant")
+ eq(NIL, funcs.luaeval "vim.w[WIN].nonexistant")
+
+ matches([[^Error executing lua: .*: attempt to index .* nil value]],
+ pcall_err(exec_lua, 'return vim.w[WIN][0].testing'))
eq({hello="world"}, funcs.luaeval "vim.w.to_delete")
exec_lua [[
@@ -1064,6 +1089,12 @@ describe('lua stdlib', function()
eq('hi', funcs.luaeval "vim.t.testing")
eq(123, funcs.luaeval "vim.t.other")
eq(NIL, funcs.luaeval "vim.t.nonexistant")
+ eq('hi', funcs.luaeval "vim.t[0].testing")
+ eq(123, funcs.luaeval "vim.t[0].other")
+ eq(NIL, funcs.luaeval "vim.t[0].nonexistant")
+
+ matches([[^Error executing lua: .*: attempt to index .* nil value]],
+ pcall_err(exec_lua, 'return vim.t[0][0].testing'))
eq({hello="world"}, funcs.luaeval "vim.t.to_delete")
exec_lua [[
@@ -1092,6 +1123,8 @@ describe('lua stdlib', function()
eq(funcs.luaeval "vim.api.nvim_get_vvar('progpath')", funcs.luaeval "vim.v.progpath")
eq(false, funcs.luaeval "vim.v['false']")
eq(NIL, funcs.luaeval "vim.v.null")
+ matches([[^Error executing lua: .*: attempt to index .* nil value]],
+ pcall_err(exec_lua, 'return vim.v[0].progpath'))
end)
it('vim.bo', function()
@@ -2255,7 +2288,7 @@ end)
describe('lua: require("mod") from packages', function()
before_each(function()
- command('set rtp+=test/functional/fixtures')
+ command('set rtp+=test/functional/fixtures pp+=test/functional/fixtures')
end)
it('propagates syntax error', function()
@@ -2266,4 +2299,13 @@ describe('lua: require("mod") from packages', function()
matches("unexpected symbol", syntax_error_msg)
end)
+
+ it('uses the right order of mod.lua vs mod/init.lua', function()
+ -- lua/fancy_x.lua takes precedence over lua/fancy_x/init.lua
+ eq('I am fancy_x.lua', exec_lua [[ return require'fancy_x' ]])
+ -- but lua/fancy_y/init.lua takes precedence over after/lua/fancy_y.lua
+ eq('I am init.lua of fancy_y!', exec_lua [[ return require'fancy_y' ]])
+ -- safety check: after/lua/fancy_z.lua is still loaded
+ eq('I am fancy_z.lua', exec_lua [[ return require'fancy_z' ]])
+ end)
end)
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index 85c67be8f9..b84e9d1533 100644
--- a/test/functional/plugin/health_spec.lua
+++ b/test/functional/plugin/health_spec.lua
@@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
+local global_helpers = require('test.helpers')
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
@@ -35,6 +36,7 @@ describe(':checkhealth', function()
clear()
eq('nvim', getcompletion('nvim', 'checkhealth')[1])
eq('provider', getcompletion('prov', 'checkhealth')[1])
+ eq('vim.lsp', getcompletion('vim.ls', 'checkhealth')[1])
end)
end)
@@ -48,42 +50,34 @@ describe('health.vim', function()
command("set runtimepath+=test/functional/fixtures")
end)
- it("health#report_*()", function()
- helpers.source([[
- let g:health_report = execute([
- \ "call health#report_start('Check Bar')",
- \ "call health#report_ok('Bar status')",
- \ "call health#report_ok('Other Bar status')",
- \ "call health#report_warn('Zub')",
- \ "call health#report_start('Baz')",
- \ "call health#report_warn('Zim', ['suggestion 1', 'suggestion 2'])"
- \ ])
- ]])
- local result = helpers.eval("g:health_report")
-
- helpers.eq(helpers.dedent([[
-
-
- ## Check Bar
- - OK: Bar status
- - OK: Other Bar status
- - WARNING: Zub
-
- ## Baz
- - WARNING: Zim
+ describe(":checkhealth", function()
+ it("functions health#report_*() render correctly", function()
+ command("checkhealth full_render")
+ helpers.expect([[
+
+ full_render: health#full_render#check
+ ========================================================================
+ ## report 1
+ - OK: life is fine
+ - WARNING: no what installed
- ADVICE:
- - suggestion 1
- - suggestion 2]]),
- result)
- end)
+ - pip what
+ - make what
+ ## report 2
+ - INFO: stuff is stable
+ - ERROR: why no hardcopy
+ - ADVICE:
+ - :help |:hardcopy|
+ - :help |:TOhtml|
+ ]])
+ end)
- describe(":checkhealth", function()
it("concatenates multiple reports", function()
- command("checkhealth success1 success2")
+ command("checkhealth success1 success2 test_plug")
helpers.expect([[
- health#success1#check
+ success1: health#success1#check
========================================================================
## report 1
- OK: everything is fine
@@ -91,25 +85,111 @@ describe('health.vim', function()
## report 2
- OK: nothing to see here
- health#success2#check
+ success2: health#success2#check
========================================================================
## another 1
- OK: ok
+
+ test_plug: require("test_plug.health").check()
+ ========================================================================
+ ## report 1
+ - OK: everything is fine
+
+ ## report 2
+ - OK: nothing to see here
+ ]])
+ end)
+
+ it("lua plugins, skips vimscript healthchecks with the same name", function()
+ command("checkhealth test_plug")
+ -- Existing file in test/functional/fixtures/lua/test_plug/autoload/health/test_plug.vim
+ -- and the Lua healthcheck is used instead.
+ helpers.expect([[
+
+ test_plug: require("test_plug.health").check()
+ ========================================================================
+ ## report 1
+ - OK: everything is fine
+
+ ## report 2
+ - OK: nothing to see here
]])
end)
+ it("lua plugins submodules", function()
+ command("checkhealth test_plug.submodule")
+ helpers.expect([[
+
+ test_plug.submodule: require("test_plug.submodule.health").check()
+ ========================================================================
+ ## report 1
+ - OK: everything is fine
+
+ ## report 2
+ - OK: nothing to see here
+ ]])
+ end)
+
+ it("lua plugins submodules with expression '*'", function()
+ command("checkhealth test_plug*")
+ local buf_lines = helpers.curbuf('get_lines', 0, -1, true)
+ -- avoid dealing with path separators
+ local received = table.concat(buf_lines, '\n', 1, #buf_lines - 2)
+ local expected = helpers.dedent([[
+
+ test_plug: require("test_plug.health").check()
+ ========================================================================
+ ## report 1
+ - OK: everything is fine
+
+ ## report 2
+ - OK: nothing to see here
+
+ test_plug.submodule: require("test_plug.submodule.health").check()
+ ========================================================================
+ ## report 1
+ - OK: everything is fine
+
+ ## report 2
+ - OK: nothing to see here
+
+ test_plug.submodule_failed: require("test_plug.submodule_failed.health").check()
+ ========================================================================
+ - ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
+ function health#check, line 24]])
+ eq(expected, received)
+ end)
+
it("gracefully handles broken healthcheck", function()
command("checkhealth broken")
helpers.expect([[
- health#broken#check
+ broken: health#broken#check
========================================================================
- ERROR: Failed to run healthcheck for "broken" plugin. Exception:
- function health#check[21]..health#broken#check, line 1
+ function health#check[24]..health#broken#check, line 1
caused an error
]])
end)
+ it("gracefully handles broken lua healthcheck", function()
+ command("checkhealth test_plug.submodule_failed")
+ local buf_lines = helpers.curbuf('get_lines', 0, -1, true)
+ local received = table.concat(buf_lines, '\n', 1, #buf_lines - 2)
+ -- avoid dealing with path separators
+ local lua_err = "attempt to perform arithmetic on a nil value"
+ local last_line = buf_lines[#buf_lines - 1]
+ assert(string.find(last_line, lua_err) ~= nil, "Lua error not present")
+
+ local expected = global_helpers.dedent([[
+
+ test_plug.submodule_failed: require("test_plug.submodule_failed.health").check()
+ ========================================================================
+ - ERROR: Failed to run healthcheck for "test_plug.submodule_failed" plugin. Exception:
+ function health#check, line 24]])
+ eq(expected, received)
+ end)
+
it("highlights OK, ERROR", function()
local screen = Screen.new(72, 10)
screen:attach()
@@ -126,23 +206,24 @@ describe('health.vim', function()
command("set laststatus=0")
screen:expect{grid=[[
^ |
- {Heading:health#foo#check} |
+ {Heading:foo: } |
{Bar:========================================================================}|
- {Bullet: -} {Error:ERROR:} No healthcheck found for "foo" plugin. |
+ {Bullet: -} {Error:ERROR}: No healthcheck found for "foo" plugin. |
|
- {Heading:health#success1#check} |
+ {Heading:success1: health#success1#check} |
{Bar:========================================================================}|
{Heading2:##}{Heading: report 1} |
- {Bullet: -} {Ok:OK:} everything is fine |
+ {Bullet: -} {Ok:OK}: everything is fine |
|
]]}
end)
it("gracefully handles invalid healthcheck", function()
command("checkhealth non_existent_healthcheck")
+ -- luacheck: ignore 613
helpers.expect([[
- health#non_existent_healthcheck#check
+ non_existent_healthcheck:
========================================================================
- ERROR: No healthcheck found for "non_existent_healthcheck" plugin.
]])
diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua
index e48a0ad260..c8b75e65fc 100644
--- a/test/functional/plugin/lsp/codelens_spec.lua
+++ b/test/functional/plugin/lsp/codelens_spec.lua
@@ -58,5 +58,33 @@ describe('vim.lsp.codelens', function()
]], bufnr)
eq({[1] = {'Lens1', 'LspCodeLens'}}, virtual_text_chunks)
+
+ end)
+ it('codelens uses client commands', function()
+ local fake_uri = "file:///fake/uri"
+ local cmd = exec_lua([[
+ fake_uri = ...
+ local bufnr = vim.uri_to_bufnr(fake_uri)
+ vim.fn.bufload(bufnr)
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'One line'})
+ local lenses = {
+ {
+ range = {
+ start = { line = 0, character = 0, },
+ ['end'] = { line = 0, character = 8 }
+ },
+ command = { title = 'Lens1', command = 'Dummy' }
+ },
+ }
+ vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr})
+ local cmd_called = nil
+ vim.lsp.commands['Dummy'] = function(command)
+ cmd_called = command
+ end
+ vim.api.nvim_set_current_buf(bufnr)
+ vim.lsp.codelens.run()
+ return cmd_called
+ ]], fake_uri)
+ eq({ command = 'Dummy', title = 'Lens1' }, cmd)
end)
end)
diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua
index 353de667ea..243ad6bdb8 100644
--- a/test/functional/plugin/lsp/diagnostic_spec.lua
+++ b/test/functional/plugin/lsp/diagnostic_spec.lua
@@ -428,6 +428,32 @@ describe('vim.lsp.diagnostic', function()
end)
end)
end)
+
+ it('maintains LSP information when translating diagnostics', function()
+ local result = exec_lua [[
+ local diagnostics = {
+ make_error("Error 1", 1, 1, 1, 5),
+ }
+
+ diagnostics[1].code = 42
+ diagnostics[1].tags = {"foo", "bar"}
+ diagnostics[1].data = "Hello world"
+
+ vim.lsp.diagnostic.on_publish_diagnostics(nil, {
+ uri = fake_uri,
+ diagnostics = diagnostics,
+ }, {client_id=1})
+
+ return {
+ vim.diagnostic.get(diagnostic_bufnr, {lnum=1})[1],
+ vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1)[1],
+ }
+ ]]
+ eq({code = 42, tags = {"foo", "bar"}, data = "Hello world"}, result[1].user_data.lsp)
+ eq(42, result[2].code)
+ eq({"foo", "bar"}, result[2].tags)
+ eq("Hello world", result[2].data)
+ end)
end)
describe("vim.lsp.diagnostic.get_line_diagnostics", function()
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 6ad37110c7..ce50abb50d 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -132,37 +132,38 @@ local function test_rpc_server(config)
end
describe('LSP', function()
- describe('server_name specified', function()
- before_each(function()
- clear_notrace()
- -- Run an instance of nvim on the file which contains our "scripts".
- -- Pass TEST_NAME to pick the script.
- local test_name = "basic_init"
- exec_lua([=[
- lsp = require('vim.lsp')
- local test_name, fixture_filename, logfile = ...
- function test__start_client()
- return lsp.start_client {
- cmd_env = {
- NVIM_LOG_FILE = logfile;
- };
- cmd = {
- vim.v.progpath, '-Es', '-u', 'NONE', '--headless',
- "-c", string.format("lua TEST_NAME = %q", test_name),
- "-c", "luafile "..fixture_filename;
- };
- root_dir = vim.loop.cwd();
- }
- end
- TEST_CLIENT1 = test__start_client()
- ]=], test_name, fake_lsp_code, fake_lsp_logfile)
- end)
+ before_each(function()
+ clear_notrace()
- after_each(function()
- exec_lua("lsp._vim_exit_handler()")
- -- exec_lua("lsp.stop_all_clients(true)")
- end)
+ -- Run an instance of nvim on the file which contains our "scripts".
+ -- Pass TEST_NAME to pick the script.
+ local test_name = "basic_init"
+ exec_lua([=[
+ lsp = require('vim.lsp')
+ local test_name, fixture_filename, logfile = ...
+ function test__start_client()
+ return lsp.start_client {
+ cmd_env = {
+ NVIM_LOG_FILE = logfile;
+ };
+ cmd = {
+ vim.v.progpath, '-Es', '-u', 'NONE', '--headless',
+ "-c", string.format("lua TEST_NAME = %q", test_name),
+ "-c", "luafile "..fixture_filename;
+ };
+ root_dir = vim.loop.cwd();
+ }
+ end
+ TEST_CLIENT1 = test__start_client()
+ ]=], test_name, fake_lsp_code, fake_lsp_logfile)
+ end)
+ after_each(function()
+ exec_lua("lsp._vim_exit_handler()")
+ -- exec_lua("lsp.stop_all_clients(true)")
+ end)
+
+ describe('server_name specified', function()
it('start_client(), stop_client()', function()
retry(nil, 4000, function()
eq(1, exec_lua('return #lsp.get_active_clients()'))
@@ -334,7 +335,6 @@ describe('LSP', function()
}
end)
it('workspace/configuration returns NIL per section if client was started without config.settings', function()
- clear_notrace()
fake_lsp_server_setup('workspace/configuration no settings')
eq({ NIL, NIL, }, exec_lua [[
local result = {
@@ -405,7 +405,7 @@ describe('LSP', function()
}
end)
- it('should call unsupported_method when trying to call an unsupported method', function()
+ it('should not call unsupported_method when trying to call an unsupported method', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
}
@@ -415,24 +415,12 @@ describe('LSP', function()
exec_lua([=[
BUFFER = vim.api.nvim_get_current_buf()
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
- vim.lsp.handlers['textDocument/typeDefinition'] = function(err, result, ctx)
- local method = ctx.method
- vim.lsp._last_lsp_handler = { err = err; method = method }
- end
- vim.lsp._unsupported_method = function(method)
- vim.lsp._last_unsupported_method = method
- return 'fake-error'
- end
- vim.lsp.buf.type_definition()
+ vim.lsp.handlers['textDocument/typeDefinition'] = function() end
]=])
end;
on_init = function(client)
client.stop()
- local method = exec_lua("return vim.lsp._last_unsupported_method")
- eq("textDocument/typeDefinition", method)
- local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_handler")
- eq("fake-error", lsp_cb_call.err)
- eq("textDocument/typeDefinition", lsp_cb_call.method)
+ exec_lua("vim.lsp.buf.type_definition()")
exec_lua [[
vim.api.nvim_command(BUFFER.."bwipeout")
]]
@@ -447,7 +435,7 @@ describe('LSP', function()
}
end)
- it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function()
+ it('should not call unsupported_method when no client and trying to call an unsupported method', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
}
@@ -455,20 +443,12 @@ describe('LSP', function()
test_name = "capabilities_for_client_supports_method";
on_setup = function()
exec_lua([=[
- vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method)
- vim.lsp._last_lsp_handler = { err = err; method = method }
- end
- vim.lsp._unsupported_method = function(method)
- vim.lsp._last_unsupported_method = method
- return 'fake-error'
- end
- vim.lsp.buf.type_definition()
+ vim.lsp.handlers['textDocument/typeDefinition'] = function() end
]=])
end;
on_init = function(client)
client.stop()
- eq(NIL, exec_lua("return vim.lsp._last_unsupported_method"))
- eq(NIL, exec_lua("return vim.lsp._last_lsp_handler"))
+ exec_lua("vim.lsp.buf.type_definition()")
end;
on_exit = function(code, signal)
eq(0, code, "exit code", fake_lsp_logfile)
@@ -480,6 +460,55 @@ describe('LSP', function()
}
end)
+ it('should not forward RequestCancelled to callback', function()
+ local expected_handlers = {
+ {NIL, {}, {method="finish", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "check_forward_request_cancelled";
+ on_init = function(_client)
+ _client.request("error_code_test")
+ client = _client
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, #expected_handlers, "did not call expected handler")
+ end;
+ on_handler = function(err, _, ctx)
+ eq(table.remove(expected_handlers), {err, {}, ctx}, "expected handler")
+ if ctx.method == 'finish' then client.stop() end
+ end;
+ }
+ end)
+
+ it('should forward ContentModified to callback', function()
+ local expected_handlers = {
+ {NIL, {}, {method="finish", client_id=1}};
+ {{code = -32801}, NIL, {method = "error_code_test", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "check_forward_content_modified";
+ on_init = function(_client)
+ _client.request("error_code_test")
+ client = _client
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ eq(0, #expected_handlers, "did not call expected handler")
+ end;
+ on_handler = function(err, _, ctx)
+ eq(table.remove(expected_handlers), {err, _, ctx}, "expected handler")
+ -- if ctx.method == 'error_code_test' then client.notify("finish") end
+ if ctx.method ~= 'finish' then client.notify('finish') end
+ if ctx.method == 'finish' then client.stop() end
+ end;
+ }
+ end)
+
it('should not send didOpen if the buffer closes before init', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
@@ -1135,10 +1164,11 @@ describe('LSP', function()
eq({ 2, 6 }, funcs.nvim_win_get_cursor(0))
end)
- it('fix the cursor to the valid column if the content was removed', function()
+ it('fix the cursor to the valid col if the content was removed', function()
funcs.nvim_win_set_cursor(0, { 2, 6 })
local edits = {
- make_edit(1, 0, 1, 19, '')
+ make_edit(1, 0, 1, 6, ''),
+ make_edit(1, 6, 1, 19, '')
}
exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
eq({
@@ -1151,6 +1181,19 @@ describe('LSP', function()
eq({ 2, 0 }, funcs.nvim_win_get_cursor(0))
end)
+ it('fix the cursor to the valid row if the content was removed', function()
+ funcs.nvim_win_set_cursor(0, { 2, 6 })
+ local edits = {
+ make_edit(1, 0, 1, 6, ''),
+ make_edit(0, 18, 5, 0, '')
+ }
+ exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1)
+ eq({
+ 'First line of text';
+ }, buf_lines(1))
+ eq({ 1, 6 }, funcs.nvim_win_get_cursor(0))
+ end)
+
it('fix the cursor row', function()
funcs.nvim_win_set_cursor(0, { 3, 0 })
local edits = {
@@ -2022,83 +2065,6 @@ describe('LSP', function()
end)
end)
- describe('lsp.util.make_floating_popup_options', function()
- before_each(function()
- exec_lua [[
- local bufnr = vim.uri_to_bufnr("file:///fake/uri")
- local winheight = vim.fn.winheight(0)
- for i = 1, winheight do
- vim.api.nvim_buf_set_lines(bufnr, 0, 0, false, {''})
- end
- vim.api.nvim_win_set_buf(0, bufnr)
- vim.api.nvim_win_set_cursor(0, {winheight, 0})
- ]]
- end)
-
- local function popup_row(opts)
- return exec_lua([[
- return vim.lsp.util.make_floating_popup_options(...).row
- ]], 2, 2, opts)
- end
-
- local err_pattern = "^Error executing lua: %.%.%./util%.lua:0: invalid floating preview border: .*%. :help vim%.api%.nvim_open_win%(%)$"
-
- it('calculates default border height correctly', function()
- eq(0, popup_row())
- end)
-
- it('calculates string border height correctly', function()
- eq(0, popup_row({border = 'none'}))
- eq(-2, popup_row({border = 'single'}))
- eq(-2, popup_row({border = 'double'}))
- eq(-2, popup_row({border = 'rounded'}))
- eq(-2, popup_row({border = 'solid'}))
- eq(-1, popup_row({border = 'shadow'}))
- end)
-
- it('error on invalid string border', function()
- matches(err_pattern, pcall_err(popup_row, {border = ''}))
- matches(err_pattern, pcall_err(popup_row, {border = 'invalid'}))
- end)
-
- it('error on invalid array border length', function()
- matches(err_pattern, pcall_err(popup_row, {border = {}}))
- matches(err_pattern, pcall_err(popup_row, {border = {'', '', ''}}))
- matches(err_pattern, pcall_err(popup_row, {border = {'', '', '', '', ''}}))
- end)
-
- it('error on invalid array border member type', function()
- matches(err_pattern, pcall_err(popup_row, {border = {0}}))
- end)
-
- it('calculates 8-array border height correctly', function()
- eq(0, popup_row({border = {'', '', '', '', '', '', '', ''}}))
- eq(-2, popup_row({border = {'', '~', '', '~', '', '~', '', '~'}}))
- eq(-1, popup_row({border = {'', '', '', '~', '', '~', '', ''}}))
- eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}}}))
- eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}, '', ''}}))
- end)
-
- it('calculates 4-array border height correctly', function()
- eq(0, popup_row({border = {'', '', '', ''}}))
- eq(-2, popup_row({border = {'', '~', '', '~'}}))
- eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}}}))
- eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', ''}}))
- end)
-
- it('calculates 2-array border height correctly', function()
- eq(0, popup_row({border = {'', ''}}))
- eq(-2, popup_row({border = {'', '~'}}))
- eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}}}))
- end)
-
- it('calculates 1-array border height correctly', function()
- eq(0, popup_row({border = {''}}))
- eq(-2, popup_row({border = {'~'}}))
- eq(-2, popup_row({border = {{'~', 'NormalFloat'}}}))
- end)
- end)
-
describe('lsp.util._make_floating_popup_size', function()
before_each(function()
exec_lua [[ contents =
@@ -2353,6 +2319,10 @@ describe('LSP', function()
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_handler = function(err, result, ctx)
+ -- Don't compare & assert params, they're not relevant for the testcase
+ -- This allows us to be lazy and avoid declaring them
+ ctx.params = nil
+
eq(table.remove(test.expected_handlers), {err, result, ctx}, "expected handler")
if ctx.method == 'start' then
exec_lua("vim.lsp.buf.rename()")
@@ -2370,4 +2340,59 @@ describe('LSP', function()
end
end)
+ describe('vim.lsp.buf.code_action', function()
+ it('Calls client side command if available', function()
+ local client
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ test_rpc_server {
+ test_name = 'code_action_with_resolve',
+ on_init = function(client_)
+ client = client_
+ end,
+ on_setup = function()
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end,
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx})
+ if ctx.method == 'start' then
+ exec_lua([[
+ vim.lsp.commands['dummy1'] = function(cmd)
+ vim.lsp.commands['dummy2'] = function()
+ end
+ end
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ vim.fn.inputlist = function()
+ return 1
+ end
+ vim.lsp.buf.code_action()
+ ]])
+ elseif ctx.method == 'shutdown' then
+ eq('function', exec_lua[[return type(vim.lsp.commands['dummy2'])]])
+ client.stop()
+ end
+ end
+ }
+ end)
+ end)
+ describe('vim.lsp.commands', function()
+ it('Accepts only string keys', function()
+ matches(
+ '.*The key for commands in `vim.lsp.commands` must be a string',
+ pcall_err(exec_lua, 'vim.lsp.commands[1] = function() end')
+ )
+ end)
+ it('Accepts only function values', function()
+ matches(
+ '.*Command added to `vim.lsp.commands` must be a function',
+ pcall_err(exec_lua, 'vim.lsp.commands.dummy = 10')
+ )
+ end)
+ end)
end)
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 103ae59b8e..7dcca231ee 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -22,14 +22,28 @@ describe(':terminal buffer', function()
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()'))
+ command('setlocal cursorline cursorlineopt=both cursorcolumn scrolloff=4 sidescrolloff=7')
+ eq({ 'both', 1, 1, 4, 7 }, eval('[&l:cursorlineopt, &l:cursorline, &l:cursorcolumn, &l:scrolloff, &l:sidescrolloff]'))
+ eq('nt', eval('mode(1)'))
-- 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]'))
+ eq('t', eval('mode(1)'))
+ eq({ 'number', 1, 0, 0, 0 }, eval('[&l:cursorlineopt, &l:cursorline, &l:cursorcolumn, &l:scrolloff, &l:sidescrolloff]'))
+ end)
+
+ it('terminal-mode does not change cursorlineopt if cursorline is disabled', function()
+ feed([[<C-\><C-N>]])
+ command('setlocal nocursorline cursorlineopt=both')
+ feed('i')
+ eq({ 0, 'both' }, eval('[&l:cursorline, &l:cursorlineopt]'))
+ end)
+
+ it('terminal-mode disables cursorline when cursorlineopt is only set to "line', function()
+ feed([[<C-\><C-N>]])
+ command('setlocal cursorline cursorlineopt=line')
+ feed('i')
+ eq({ 0, 'line' }, eval('[&l:cursorline, &l:cursorlineopt]'))
end)
describe('when a new file is edited', function()
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index 707c355069..065dd72485 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -96,19 +96,28 @@ describe(':terminal', function()
eq(3, #jumps)
end)
+ it('nvim_get_mode() in :terminal', function()
+ command(':terminal')
+ eq({ blocking=false, mode='nt' }, nvim('get_mode'))
+ feed('i')
+ eq({ blocking=false, mode='t' }, nvim('get_mode'))
+ feed([[<C-\><C-N>]])
+ eq({ blocking=false, mode='nt' }, nvim('get_mode'))
+ end)
+
it(':stopinsert RPC request exits terminal-mode #7807', function()
command(':terminal')
feed('i[tui] insert-mode')
eq({ blocking=false, mode='t' }, nvim('get_mode'))
command('stopinsert')
- eq({ blocking=false, mode='n' }, nvim('get_mode'))
+ eq({ blocking=false, mode='nt' }, nvim('get_mode'))
end)
it(':stopinsert in normal mode doesn\'t break insert mode #9889', function()
command(':terminal')
- eq({ blocking=false, mode='n' }, nvim('get_mode'))
+ eq({ blocking=false, mode='nt' }, nvim('get_mode'))
command(':stopinsert')
- eq({ blocking=false, mode='n' }, nvim('get_mode'))
+ eq({ blocking=false, mode='nt' }, nvim('get_mode'))
feed('a')
eq({ blocking=false, mode='t' }, nvim('get_mode'))
end)
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index 0eb5901b3b..3d8441b93c 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -33,16 +33,16 @@ describe(':terminal mouse', function()
describe('when the terminal has focus', function()
it('will exit focus on mouse-scroll', function()
- eq('t', eval('mode()'))
+ eq('t', eval('mode(1)'))
feed('<ScrollWheelUp><0,0>')
- eq('n', eval('mode()'))
+ eq('nt', eval('mode(1)'))
end)
it('will exit focus on <C-\\> + mouse-scroll', function()
- eq('t', eval('mode()'))
+ eq('t', eval('mode(1)'))
feed('<C-\\>')
feed('<ScrollWheelUp><0,0>')
- eq('n', eval('mode()'))
+ eq('nt', eval('mode(1)'))
end)
describe('with mouse events enabled by the program', function()
@@ -94,7 +94,7 @@ describe(':terminal mouse', function()
-- When the display area such as a number is clicked, it returns to the
-- normal mode.
feed('<LeftMouse><3,0>')
- eq('n', eval('mode()'))
+ eq('nt', eval('mode(1)'))
screen:expect([[
{7: 11 }^line28 |
{7: 12 }line29 |
diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua
index 188afa1e84..c92107082e 100644
--- a/test/functional/terminal/window_split_tab_spec.lua
+++ b/test/functional/terminal/window_split_tab_spec.lua
@@ -111,7 +111,7 @@ describe(':terminal', function()
command('terminal')
feed('a<Cmd>wincmd j<CR>')
eq(2, eval("winnr()"))
- eq('t', eval('mode()'))
+ eq('t', eval('mode(1)'))
end)
end)
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 4373d17890..8074f91215 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -790,3 +790,511 @@ end]]
helpers.assert_alive()
end)
end)
+
+describe('decorations: virtual lines', function()
+ local screen, ns
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 12)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {bold=true, foreground=Screen.colors.Blue};
+ [2] = {foreground = Screen.colors.Cyan4};
+ [3] = {background = Screen.colors.Yellow1};
+ [4] = {bold = true};
+ [5] = {background = Screen.colors.Yellow, foreground = Screen.colors.Blue};
+ [6] = {foreground = Screen.colors.Blue};
+ [7] = {foreground = Screen.colors.SlateBlue};
+ [8] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue};
+ [9] = {foreground = Screen.colors.Brown};
+ }
+
+ ns = meths.create_namespace 'test'
+ end)
+
+ local example_text = [[
+if (h->n_buckets < new_n_buckets) { // expand
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t));
+ h->keys = new_keys;
+ if (kh_is_map && val_size) {
+ char *new_vals = krealloc( h->vals_buf, new_n_buckets * val_size);
+ h->vals_buf = new_vals;
+ }
+}]]
+
+ it('works with one line', function()
+ insert(example_text)
+ feed 'gg'
+ meths.buf_set_extmark(0, ns, 1, 33, {
+ virt_lines={ {{">> ", "NonText"}, {"krealloc", "Identifier"}, {": change the size of an allocation"}}};
+ virt_lines_above=true;
+ })
+
+ screen:expect{grid=[[
+ ^if (h->n_buckets < new_n_buckets) { // expand |
+ {1:>> }{2:krealloc}: change the size of an allocation |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ } |
+ |
+ ]]}
+
+ feed '/krealloc<cr>'
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ {1:>> }{2:krealloc}: change the size of an allocation |
+ khkey_t *new_keys = (khkey_t *){3:^krealloc}((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = {3:krealloc}( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ } |
+ /krealloc |
+ ]]}
+
+ -- virtual line remains anchored to the extmark
+ feed 'i<cr>'
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *) |
+ {1:>> }{2:krealloc}: change the size of an allocation |
+ {3:^krealloc}((void *)h->keys, new_n_buckets * sizeof(k|
+ hkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = {3:krealloc}( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ {4:-- INSERT --} |
+ ]]}
+
+ feed '<esc>3+'
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *) |
+ {1:>> }{2:krealloc}: change the size of an allocation |
+ {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k|
+ hkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ |
+ ]]}
+
+ meths.buf_set_extmark(0, ns, 5, 0, {
+ virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unneccesary?", "Comment"}} };
+ })
+ -- TODO: what about the cursor??
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *) |
+ {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k|
+ hkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = {3:krealloc}( h->vals_buf, new_n_|
+ buck^ets * val_size); |
+ {5:^^ REVIEW:}{6: new_vals variable seems unneccesary?} |
+ h->vals_buf = new_vals; |
+ } |
+ |
+ ]]}
+
+ meths.buf_clear_namespace(0, ns, 0, -1)
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *) |
+ {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k|
+ hkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = {3:krealloc}( h->vals_buf, new_n_|
+ buck^ets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ } |
+ |
+ ]]}
+ end)
+
+
+ it('works with text at the beginning of the buffer', function()
+ insert(example_text)
+ feed 'gg'
+
+ screen:expect{grid=[[
+ ^if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ } |
+ {1:~ }|
+ |
+ ]]}
+
+ meths.buf_set_extmark(0, ns, 0, 0, {
+ virt_lines={
+ {{"refactor(khash): ", "Special"}, {"take size of values as parameter"}};
+ {{"Author: Dev Devsson, "}, {"Tue Aug 31 10:13:37 2021", "Comment"}};
+ };
+ virt_lines_above=true;
+ right_gravity=false;
+ })
+
+ -- placing virt_text on topline does not automatically cause a scroll
+ screen:expect{grid=[[
+ ^if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ } |
+ {1:~ }|
+ |
+ ]], unchanged=true}
+
+ feed '<c-b>'
+ screen:expect{grid=[[
+ {7:refactor(khash): }take size of values as parameter |
+ Author: Dev Devsson, {6:Tue Aug 31 10:13:37 2021} |
+ ^if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ |
+ ]]}
+ end)
+
+ it('works with text et the end of the buffer', function()
+ insert(example_text)
+ feed 'G'
+
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ ^} |
+ {1:~ }|
+ |
+ ]]}
+
+ local id = meths.buf_set_extmark(0, ns, 7, 0, {
+ virt_lines={{{"Grugg"}}};
+ right_gravity=false;
+ })
+
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ ^} |
+ Grugg |
+ |
+ ]]}
+
+ meths.buf_del_extmark(0, ns, id)
+ screen:expect{grid=[[
+ if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ ^} |
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it('works with a block scrolling up', function()
+ screen:try_resize(30, 7)
+ insert("aa\nbb\ncc\ndd\nee\nff\ngg\nhh")
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 6, 0, {
+ virt_lines={
+ {{"they see me"}};
+ {{"scrolling", "Special"}};
+ {{"they"}};
+ {{"hatin'", "Special"}};
+ };
+ })
+
+ screen:expect{grid=[[
+ ^aa |
+ bb |
+ cc |
+ dd |
+ ee |
+ ff |
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ ^bb |
+ cc |
+ dd |
+ ee |
+ ff |
+ gg |
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ ^cc |
+ dd |
+ ee |
+ ff |
+ gg |
+ they see me |
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ ^dd |
+ ee |
+ ff |
+ gg |
+ they see me |
+ {7:scrolling} |
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ ^ee |
+ ff |
+ gg |
+ they see me |
+ {7:scrolling} |
+ they |
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ ^ff |
+ gg |
+ they see me |
+ {7:scrolling} |
+ they |
+ {7:hatin'} |
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ ^gg |
+ they see me |
+ {7:scrolling} |
+ they |
+ {7:hatin'} |
+ hh |
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ they see me |
+ {7:scrolling} |
+ they |
+ {7:hatin'} |
+ ^hh |
+ {1:~ }|
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ {7:scrolling} |
+ they |
+ {7:hatin'} |
+ ^hh |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ they |
+ {7:hatin'} |
+ ^hh |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ {7:hatin'} |
+ ^hh |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed '<c-e>'
+ screen:expect{grid=[[
+ ^hh |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it('works with sign and numbercolumns', function()
+ insert(example_text)
+ feed 'gg'
+ command 'set number signcolumn=yes'
+ screen:expect{grid=[[
+ {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan|
+ {8: }{9: }d |
+ {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v|
+ {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_|
+ {8: }{9: }t)); |
+ {8: }{9: 3 } h->keys = new_keys; |
+ {8: }{9: 4 } if (kh_is_map && val_size) { |
+ {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, |
+ {8: }{9: }new_n_buckets * val_size); |
+ {8: }{9: 6 } h->vals_buf = new_vals; |
+ {8: }{9: 7 } } |
+ |
+ ]]}
+
+ meths.buf_set_extmark(0, ns, 2, 0, {
+ virt_lines={
+ {{"Some special", "Special"}};
+ {{"remark about codes", "Comment"}};
+ };
+ })
+
+ screen:expect{grid=[[
+ {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan|
+ {8: }{9: }d |
+ {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v|
+ {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_|
+ {8: }{9: }t)); |
+ {8: }{9: 3 } h->keys = new_keys; |
+ {8: }{9: }{7:Some special} |
+ {8: }{9: }{6:remark about codes} |
+ {8: }{9: 4 } if (kh_is_map && val_size) { |
+ {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, |
+ {8: }{9: }new_n_buckets * val_size); |
+ |
+ ]]}
+
+ meths.buf_set_extmark(0, ns, 2, 0, {
+ virt_lines={
+ {{"Some special", "Special"}};
+ {{"remark about codes", "Comment"}};
+ };
+ virt_lines_leftcol=true;
+ })
+ screen:expect{grid=[[
+ {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan|
+ {8: }{9: }d |
+ {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v|
+ {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_|
+ {8: }{9: }t)); |
+ {8: }{9: 3 } h->keys = new_keys; |
+ {7:Some special} |
+ {6:remark about codes} |
+ {8: }{9: 4 } if (kh_is_map && val_size) { |
+ {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, |
+ {8: }{9: }new_n_buckets * val_size); |
+ |
+ ]]}
+ end)
+
+
+ it('works with hard tabs', function()
+ insert(example_text)
+ feed 'gg'
+ meths.buf_set_extmark(0, ns, 1, 0, {
+ virt_lines={ {{">>", "NonText"}, {"\tvery\ttabby", "Identifier"}, {"text\twith\ttabs"}}};
+ })
+ screen:expect{grid=[[
+ ^if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ {1:>>}{2: very tabby}text with tabs |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ } |
+ |
+ ]]}
+
+ command 'set tabstop=4'
+ screen:expect{grid=[[
+ ^if (h->n_buckets < new_n_buckets) { // expand |
+ khkey_t *new_keys = (khkey_t *)krealloc((void *)|
+ h->keys, new_n_buckets * sizeof(khkey_t)); |
+ {1:>>}{2: very tabby}text with tabs |
+ h->keys = new_keys; |
+ if (kh_is_map && val_size) { |
+ char *new_vals = krealloc( h->vals_buf, new_n_|
+ buckets * val_size); |
+ h->vals_buf = new_vals; |
+ } |
+ } |
+ |
+ ]]}
+ end)
+
+end)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index ccf5f963d1..6c2c4b398a 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -46,6 +46,7 @@ describe('float window', function()
[24] = {foreground = Screen.colors.Black, background = Screen.colors.Grey80};
[25] = {blend = 100, background = Screen.colors.Gray0};
[26] = {blend = 80, background = Screen.colors.Gray0};
+ [27] = {background = Screen.colors.LightGray};
}
it('behavior', function()
@@ -109,6 +110,21 @@ describe('float window', function()
assert_alive()
end)
+ it('closed immediately by autocmd after win_enter #15548', function()
+ eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately',
+ pcall_err(exec_lua, [[
+ vim.cmd "autocmd BufLeave * ++once quit!"
+ local buf = vim.api.nvim_create_buf(true, true)
+ vim.api.nvim_open_win(buf, true, {
+ relative = "win",
+ row = 0, col = 0,
+ width = 1, height = 1,
+ noautocmd = false,
+ })
+ ]]))
+ assert_alive()
+ end)
+
it('opened with correct height', function()
local height = exec_lua([[
vim.api.nvim_set_option("winheight", 20)
@@ -1514,7 +1530,7 @@ describe('float window', function()
it('API has proper error messages', function()
local buf = meths.create_buf(false,false)
- eq("Invalid key 'bork'",
+ eq("Invalid key: 'bork'",
pcall_err(meths.open_win,buf, false, {width=20,height=2,bork=true}))
eq("'win' key is only valid with relative='win'",
pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0}))
@@ -1527,13 +1543,15 @@ describe('float window', function()
eq("'relative' requires 'row'/'col' or 'bufpos'",
pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor'}))
eq("'width' key must be a positive Integer",
- pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor'}))
+ pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor', row=0, col=0}))
eq("'height' key must be a positive Integer",
- pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor'}))
+ pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor', row=0, col=0}))
eq("'height' key must be a positive Integer",
- pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor'}))
- eq("Must specify 'width' and 'height'",
- pcall_err(meths.open_win,buf, false, {relative='editor'}))
+ pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor', row=0, col=0}))
+ eq("Must specify 'width'",
+ pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0}))
+ eq("Must specify 'height'",
+ pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0, width=2}))
end)
it('can be placed relative window or cursor', function()
@@ -1866,6 +1884,293 @@ describe('float window', function()
end
end)
+ it('always anchor to corner including border', function()
+ screen:try_resize(40,13)
+ meths.buf_set_lines(0, 0, -1, true, {'just some example text', 'some more example text'})
+ feed('ggeee')
+ command('below split')
+ if multigrid then
+ screen:expect([[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] [+] }|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ {4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ just some exampl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ]])
+ else
+ screen:expect([[
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {5:[No Name] [+] }|
+ just some exampl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end
+
+ local buf = meths.create_buf(false, false)
+ meths.buf_set_lines(buf, 0, -1, true, {' halloj! ',
+ ' BORDAA '})
+ local win = meths.open_win(buf, false, {relative='cursor', width=9, height=2, row=1, col=-2, border="double"})
+
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] [+] }|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ {4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ just some exampl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 6
+ {5:╔═════════╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [6] = {{id = 1003}, "NW", 4, 1, 14, true}
+ }}
+ else
+ screen:expect([[
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {5:[No Name] [+] }|
+ just some exampl^e text |
+ some more exam{5:╔═════════╗} |
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end
+
+ meths.win_set_config(win, {relative='cursor', row=0, col=-2, anchor='NE'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] [+] }|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ {4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ just some exampl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 6
+ {5:╔═════════╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [6] = {{id = 1003}, "NE", 4, 0, 14, true}
+ }}
+ else
+ screen:expect([[
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {5:[No Name] [+] }|
+ jus{5:╔═════════╗}pl^e text |
+ som{5:║}{1: halloj! }{5:║}ple text |
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end
+
+ meths.win_set_config(win, {relative='cursor', row=1, col=-2, anchor='SE'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] [+] }|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ {4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ just some exampl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 6
+ {5:╔═════════╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [6] = {{id = 1003}, "SE", 4, 1, 14, true}
+ }}
+ else
+ screen:expect([[
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }{5:╔═════════╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {5:[No║}{1: BORDAA }{5:║ }|
+ jus{5:╚═════════╝}pl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end
+
+ meths.win_set_config(win, {relative='cursor', row=0, col=-2, anchor='SW'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ {5:[No Name] [+] }|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ [4:----------------------------------------]|
+ {4:[No Name] [+] }|
+ [3:----------------------------------------]|
+ ## grid 2
+ just some example text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ just some exampl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 6
+ {5:╔═════════╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]], float_pos={
+ [6] = {{id = 1003}, "SW", 4, 0, 14, true}
+ }}
+ else
+ screen:expect([[
+ just some example text |
+ some more example text |
+ {0:~ }{5:╔═════════╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {5:[No Name] [+] ╚═════════╝ }|
+ just some exampl^e text |
+ some more example text |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end
+ end)
+
it('can be placed relative text in a window', function()
screen:try_resize(30,5)
local firstwin = meths.get_current_win().id
@@ -5870,6 +6175,132 @@ describe('float window', function()
end)
end)
+ it("left drag changes visual selection in float window", function()
+ local buf = meths.create_buf(false,false)
+ meths.buf_set_lines(buf, 0, -1, true, {'foo', 'bar'})
+ meths.open_win(buf, false, {relative='editor', width=20, height=3, row=2, col=5})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {1:foo }|
+ {1:bar }|
+ {2:~ }|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 2, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 2};
+ }}
+ meths.input_mouse('left', 'press', '', 5, 0, 0)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 5
+ {1:^foo }|
+ {1:bar }|
+ {2:~ }|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 2, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 2};
+ }}
+ meths.input_mouse('left', 'drag', '', 5, 1, 2)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ {3:-- VISUAL --} |
+ ## grid 5
+ {27:foo}{1: }|
+ {27:ba}{1:^r }|
+ {2:~ }|
+ ]], float_pos={
+ [5] = {{id = 1002}, "NW", 1, 2, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1};
+ [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 1, curcol = 2, linecount = 2};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }{1:foo }{0: }|
+ {0:~ }{1:bar }{0: }|
+ {0:~ }{2:~ }{0: }|
+ {0:~ }|
+ |
+ ]]}
+
+ meths.input_mouse('left', 'press', '', 0, 2, 5)
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }{1:^foo }{0: }|
+ {0:~ }{1:bar }{0: }|
+ {0:~ }{2:~ }{0: }|
+ {0:~ }|
+ |
+ ]]}
+
+ meths.input_mouse('left', 'drag', '', 0, 3, 7)
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }{27:foo}{1: }{0: }|
+ {0:~ }{27:ba}{1:^r }{0: }|
+ {0:~ }{2:~ }{0: }|
+ {0:~ }|
+ {3:-- VISUAL --} |
+ ]]}
+ end
+ end)
+
it("'winblend' option", function()
screen:try_resize(50,9)
screen:set_default_attr_ids({
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index 712c1f377a..b6e2f2311f 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -1487,6 +1487,29 @@ describe("inccommand=nosplit", function()
]])
eq(eval('v:null'), eval('v:exiting'))
end)
+
+ it("does not break bar-separated command #8796", function()
+ source([[
+ function! F()
+ if v:false | return | endif
+ endfun
+ ]])
+ command('call timer_start(10, {-> F()}, {"repeat":-1})')
+ feed(':%s/')
+ sleep(20) -- Allow some timer activity.
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/^ |
+ ]])
+ end)
end)
describe(":substitute, 'inccommand' with a failing expression", function()
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index 7bca741ae3..baacef358f 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -585,6 +585,69 @@ describe('ui/mouse/input', function()
]])
end)
+ it('left drag changes visual selection in split layout', function()
+ screen:try_resize(53,14)
+ command('set mouse=a')
+ command('vsplit')
+ command('wincmd l')
+ command('below split')
+ command('enew')
+ feed('ifoo\nbar<esc>')
+
+ screen:expect{grid=[[
+ testing {4:│}testing |
+ mouse {4:│}mouse |
+ support and selection {4:│}support and selection |
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│[No Name] [+] }|
+ {0:~ }{4:│}foo{0:$} |
+ {0:~ }{4:│}ba^r{0:$} |
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {4:[No Name] [+] }{5:[No Name] [+] }|
+ |
+ ]]}
+
+ meths.input_mouse('left', 'press', '', 0, 6, 27)
+ screen:expect{grid=[[
+ testing {4:│}testing |
+ mouse {4:│}mouse |
+ support and selection {4:│}support and selection |
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│[No Name] [+] }|
+ {0:~ }{4:│}^foo{0:$} |
+ {0:~ }{4:│}bar{0:$} |
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {4:[No Name] [+] }{5:[No Name] [+] }|
+ |
+ ]]}
+ meths.input_mouse('left', 'drag', '', 0, 7, 30)
+
+ screen:expect{grid=[[
+ testing {4:│}testing |
+ mouse {4:│}mouse |
+ support and selection {4:│}support and selection |
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│[No Name] [+] }|
+ {0:~ }{4:│}{1:foo}{3:$} |
+ {0:~ }{4:│}{1:bar}{0:^$} |
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {0:~ }{4:│}{0:~ }|
+ {4:[No Name] [+] }{5:[No Name] [+] }|
+ {2:-- VISUAL --} |
+ ]]}
+ end)
+
it('two clicks will select the word and enter VISUAL', function()
feed('<LeftMouse><2,2><LeftMouse><2,2>')
screen:expect([[
@@ -1384,4 +1447,128 @@ describe('ui/mouse/input', function()
end) -- level 3 - wrapped
end)
+
+ it('getmousepos works correctly', function()
+ local winwidth = meths.get_option('winwidth')
+ -- Set winwidth=1 so that window sizes don't change.
+ meths.set_option('winwidth', 1)
+ command('tabedit')
+ local tabpage = meths.get_current_tabpage()
+ insert('hello')
+ command('vsplit')
+ local opts = {
+ relative='editor',
+ width=12,
+ height=1,
+ col=8,
+ row=1,
+ anchor='NW',
+ style='minimal',
+ border='single',
+ focusable=1
+ }
+ local float = meths.open_win(meths.get_current_buf(), false, opts)
+ command('redraw')
+ local lines = meths.get_option('lines')
+ local columns = meths.get_option('columns')
+
+ -- Test that screenrow and screencol are set properly for all positions.
+ for row = 0, lines - 1 do
+ for col = 0, columns - 1 do
+ -- Skip the X button that would close the tab.
+ if row ~= 0 or col ~= columns - 1 then
+ meths.input_mouse('left', 'press', '', 0, row, col)
+ meths.set_current_tabpage(tabpage)
+ local mousepos = funcs.getmousepos()
+ eq(row + 1, mousepos.screenrow)
+ eq(col + 1, mousepos.screencol)
+ -- All other values should be 0 when clicking on the command line.
+ if row == lines - 1 then
+ eq(0, mousepos.winid)
+ eq(0, mousepos.winrow)
+ eq(0, mousepos.wincol)
+ eq(0, mousepos.line)
+ eq(0, mousepos.column)
+ end
+ end
+ end
+ end
+
+ -- Test that mouse position values are properly set for the floating window
+ -- with a border. 1 is added to the height and width to account for the
+ -- border.
+ for win_row = 0, opts.height + 1 do
+ for win_col = 0, opts.width + 1 do
+ local row = win_row + opts.row
+ local col = win_col + opts.col
+ meths.input_mouse('left', 'press', '', 0, row, col)
+ local mousepos = funcs.getmousepos()
+ eq(float.id, mousepos.winid)
+ eq(win_row + 1, mousepos.winrow)
+ eq(win_col + 1, mousepos.wincol)
+ local line = 0
+ local column = 0
+ if win_row > 0 and win_row < opts.height + 1
+ and win_col > 0 and win_col < opts.width + 1 then
+ -- Because of border, win_row and win_col don't need to be
+ -- incremented by 1.
+ line = math.min(win_row, funcs.line('$'))
+ column = math.min(win_col, #funcs.getline(line) + 1)
+ end
+ eq(line, mousepos.line)
+ eq(column, mousepos.column)
+ end
+ end
+
+ -- Test that mouse position values are properly set for the floating
+ -- window, after removing the border.
+ opts.border = 'none'
+ meths.win_set_config(float, opts)
+ command('redraw')
+ for win_row = 0, opts.height - 1 do
+ for win_col = 0, opts.width - 1 do
+ local row = win_row + opts.row
+ local col = win_col + opts.col
+ meths.input_mouse('left', 'press', '', 0, row, col)
+ local mousepos = funcs.getmousepos()
+ eq(float.id, mousepos.winid)
+ eq(win_row + 1, mousepos.winrow)
+ eq(win_col + 1, mousepos.wincol)
+ local line = math.min(win_row + 1, funcs.line('$'))
+ local column = math.min(win_col + 1, #funcs.getline(line) + 1)
+ eq(line, mousepos.line)
+ eq(column, mousepos.column)
+ end
+ end
+
+ -- Test that mouse position values are properly set for ordinary windows.
+ -- Set the float to be unfocusable instead of closing, to additionally test
+ -- that getmousepos does not consider unfocusable floats. (see discussion
+ -- in PR #14937 for details).
+ opts.focusable = false
+ meths.win_set_config(float, opts)
+ command('redraw')
+ for nr = 1, 2 do
+ for win_row = 0, funcs.winheight(nr) - 1 do
+ for win_col = 0, funcs.winwidth(nr) - 1 do
+ local row = win_row + funcs.win_screenpos(nr)[1] - 1
+ local col = win_col + funcs.win_screenpos(nr)[2] - 1
+ meths.input_mouse('left', 'press', '', 0, row, col)
+ local mousepos = funcs.getmousepos()
+ eq(funcs.win_getid(nr), mousepos.winid)
+ eq(win_row + 1, mousepos.winrow)
+ eq(win_col + 1, mousepos.wincol)
+ local line = math.min(win_row + 1, funcs.line('$'))
+ local column = math.min(win_col + 1, #funcs.getline(line) + 1)
+ eq(line, mousepos.line)
+ eq(column, mousepos.column)
+ end
+ end
+ end
+
+ -- Restore state and release mouse.
+ command('tabclose!')
+ meths.set_option('winwidth', winwidth)
+ meths.input_mouse('left', 'release', '', 0, 0, 0)
+ end)
end)
diff --git a/test/functional/vimscript/json_functions_spec.lua b/test/functional/vimscript/json_functions_spec.lua
index c3b607b544..5d1597f53d 100644
--- a/test/functional/vimscript/json_functions_spec.lua
+++ b/test/functional/vimscript/json_functions_spec.lua
@@ -168,7 +168,8 @@ describe('json_decode() function', function()
end)
it('parses floating-point numbers', function()
- eq('100000.0', eval('string(json_decode("100000.0"))'))
+ -- Also test method call (->) syntax
+ eq('100000.0', eval('"100000.0"->json_decode()->string()'))
eq(100000.5, funcs.json_decode('100000.5'))
eq(-100000.5, funcs.json_decode('-100000.5'))
eq(-100000.5e50, funcs.json_decode('-100000.5e50'))
@@ -549,11 +550,12 @@ describe('json_encode() function', function()
end)
it('dumps floats', function()
- eq('0.0', eval('json_encode(0.0)'))
+ -- Also test method call (->) syntax
+ eq('0.0', eval('0.0->json_encode()'))
eq('10.5', funcs.json_encode(10.5))
eq('-10.5', funcs.json_encode(-10.5))
eq('-1.0e-5', funcs.json_encode(-1e-5))
- eq('1.0e50', eval('json_encode(1.0e50)'))
+ eq('1.0e50', eval('1.0e50->json_encode()'))
end)
it('fails to dump NaN and infinite values', function()