aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/functional/api/keymap_spec.lua44
-rw-r--r--test/functional/api/vim_spec.lua292
-rw-r--r--test/functional/editor/langmap_spec.lua8
-rw-r--r--test/functional/editor/mode_cmdline_spec.lua105
-rw-r--r--test/functional/editor/mode_insert_spec.lua5
-rw-r--r--test/functional/ex_cmds/cmd_map_spec.lua35
-rw-r--r--test/functional/ex_cmds/ctrl_c_spec.lua32
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua151
-rw-r--r--test/functional/legacy/eval_spec.lua2
-rw-r--r--test/functional/legacy/mapping_spec.lua57
-rw-r--r--test/functional/lua/vim_spec.lua4
-rw-r--r--test/functional/options/pastetoggle_spec.lua86
-rw-r--r--test/functional/plugin/lsp_spec.lua274
-rw-r--r--test/functional/ui/input_spec.lua132
-rw-r--r--test/functional/vimscript/system_spec.lua45
-rw-r--r--test/unit/keymap_spec.lua23
16 files changed, 1103 insertions, 192 deletions
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index c0edcde476..4fb2d55a76 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -582,7 +582,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('can set mappings containing literal keycodes', function()
meths.set_keymap('n', '\n\r\n', 'rhs', {})
local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs')
- eq(expected, get_mapargs('n', '<C-j><CR><C-j>'))
+ eq(expected, get_mapargs('n', '<NL><CR><NL>'))
end)
it('can set mappings whose RHS is a <Nop>', function()
@@ -874,6 +874,27 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
end)
+ it('no double-free when unmapping simplifiable lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap('n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_del_keymap('n', '<C-I>')
+ ]]
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
+ end)
+
it('can set descriptions on keymaps', function()
meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
eq(generate_mapargs('n', 'lhs', 'rhs', {desc="map description"}), get_mapargs('n', 'lhs'))
@@ -1040,4 +1061,25 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
eq(1, exec_lua[[return GlobalCount]])
eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
end)
+
+ it('no double-free when unmapping simplifiable lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap(0, 'n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_buf_del_keymap(0, 'n', '<C-I>')
+ ]]
+
+ feed('<C-I>\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>'))
+ end)
end)
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index f4b1a7fd59..e138e2cc38 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -3098,4 +3098,296 @@ describe('API', function()
end)
end)
end)
+ describe('nvim_parse_cmd', function()
+ it('works', function()
+ eq({
+ cmd = 'echo',
+ args = { 'foo' },
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ addr = 'none',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '*',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = 0
+ }
+ }, meths.parse_cmd('echo foo', {}))
+ end)
+ it('works with ranges', function()
+ eq({
+ cmd = 'substitute',
+ args = { '/math.random/math.max/' },
+ bang = false,
+ line1 = 4,
+ line2 = 6,
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '*',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = 0
+ }
+ }, meths.parse_cmd('4,6s/math.random/math.max/', {}))
+ end)
+ it('works with bang', function()
+ eq({
+ cmd = 'write',
+ args = {},
+ bang = true,
+ line1 = 1,
+ line2 = 1,
+ addr = 'line',
+ magic = {
+ file = true,
+ bar = true
+ },
+ nargs = '?',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = 0
+ },
+ }, meths.parse_cmd('w!', {}))
+ end)
+ it('works with modifiers', function()
+ eq({
+ cmd = 'split',
+ args = { 'foo.txt' },
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ addr = '?',
+ magic = {
+ file = true,
+ bar = true
+ },
+ nargs = '?',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = true,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = true,
+ vertical = false,
+ split = "topleft",
+ tab = 2,
+ verbose = 15
+ },
+ }, meths.parse_cmd('15verbose silent! aboveleft topleft tab split foo.txt', {}))
+ end)
+ it('works with user commands', function()
+ command('command -bang -nargs=+ -range -addr=lines MyCommand echo foo')
+ eq({
+ cmd = 'MyCommand',
+ args = { 'test', 'it' },
+ bang = true,
+ line1 = 4,
+ line2 = 6,
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '+',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = 0
+ }
+ }, meths.parse_cmd('4,6MyCommand! test it', {}))
+ end)
+ it('works for commands separated by bar', function()
+ eq({
+ cmd = 'argadd',
+ args = { 'a.txt' },
+ bang = false,
+ line1 = 0,
+ line2 = 0,
+ addr = 'arg',
+ magic = {
+ file = true,
+ bar = true
+ },
+ nargs = '*',
+ nextcmd = 'argadd b.txt',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = 0
+ }
+ }, meths.parse_cmd('argadd a.txt | argadd b.txt', {}))
+ end)
+ it('works for nargs=1', function()
+ command('command -nargs=1 MyCommand echo <q-args>')
+ eq({
+ cmd = 'MyCommand',
+ args = { 'test it' },
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ addr = 'none',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '1',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = 0
+ }
+ }, meths.parse_cmd('MyCommand test it', {}))
+ end)
+ it('sets correct default range', function()
+ command('command -range=% -addr=buffers MyCommand echo foo')
+ command('new')
+ eq({
+ cmd = 'MyCommand',
+ args = {},
+ bang = false,
+ line1 = 1,
+ line2 = 2,
+ addr = 'buf',
+ magic = {
+ file = false,
+ bar = false
+ },
+ nargs = '0',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = 0
+ }
+ }, meths.parse_cmd('MyCommand', {}))
+ end)
+ it('errors for invalid command', function()
+ eq('Error while parsing command line', pcall_err(meths.parse_cmd, 'Fubar', {}))
+ command('command! Fubar echo foo')
+ eq('Error while parsing command line', pcall_err(meths.parse_cmd, 'Fubar!', {}))
+ eq('Error while parsing command line', pcall_err(meths.parse_cmd, '4,6Fubar', {}))
+ end)
+ end)
end)
diff --git a/test/functional/editor/langmap_spec.lua b/test/functional/editor/langmap_spec.lua
index af19f97a68..b1070ecddc 100644
--- a/test/functional/editor/langmap_spec.lua
+++ b/test/functional/editor/langmap_spec.lua
@@ -213,11 +213,11 @@ describe("'langmap'", function()
iii]])
end)
- local function testrecording(command_string, expect_string, setup_function)
+ local function testrecording(command_string, expect_string, setup_function, expect_macro)
if setup_function then setup_function() end
feed('qa' .. command_string .. 'q')
expect(expect_string)
- eq(helpers.funcs.nvim_replace_termcodes(command_string, true, true, true),
+ eq(expect_macro or helpers.funcs.nvim_replace_termcodes(command_string, true, true, true),
eval('@a'))
if setup_function then setup_function() end
-- n.b. may need nvim_replace_termcodes() here.
@@ -273,8 +273,8 @@ describe("'langmap'", function()
it('treats control modified keys as characters', function()
command('nnoremap <C-w> iw<esc>')
command('nnoremap <C-i> ii<esc>')
- testrecording('<C-w>', 'whello', local_setup)
- testrecording('<C-i>', 'ihello', local_setup)
+ testrecording('<C-w>', 'whello', local_setup, eval([["\<*C-w>"]]))
+ testrecording('<C-i>', 'ihello', local_setup, eval([["\<*C-i>"]]))
end)
end)
diff --git a/test/functional/editor/mode_cmdline_spec.lua b/test/functional/editor/mode_cmdline_spec.lua
index 0f7d821bb5..50cc5e17ee 100644
--- a/test/functional/editor/mode_cmdline_spec.lua
+++ b/test/functional/editor/mode_cmdline_spec.lua
@@ -3,67 +3,74 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, insert, funcs, eq, feed =
helpers.clear, helpers.insert, helpers.funcs, helpers.eq, helpers.feed
+local eval = helpers.eval
local meths = helpers.meths
-describe('cmdline CTRL-R', function()
+describe('cmdline', function()
before_each(clear)
- it('pasting non-special register inserts <CR> *between* lines', function()
- insert([[
- line1abc
- line2somemoretext
- ]])
- -- Yank 2 lines linewise, then paste to cmdline.
- feed([[<C-\><C-N>gg0yj:<C-R>0]])
- -- <CR> inserted between lines, NOT after the final line.
- eq('line1abc\rline2somemoretext', funcs.getcmdline())
+ describe('Ctrl-R', function()
+ it('pasting non-special register inserts <CR> *between* lines', function()
+ insert([[
+ line1abc
+ line2somemoretext
+ ]])
+ -- Yank 2 lines linewise, then paste to cmdline.
+ feed([[<C-\><C-N>gg0yj:<C-R>0]])
+ -- <CR> inserted between lines, NOT after the final line.
+ eq('line1abc\rline2somemoretext', funcs.getcmdline())
- -- Yank 2 lines charwise, then paste to cmdline.
- feed([[<C-\><C-N>gg05lyvj:<C-R>0]])
- -- <CR> inserted between lines, NOT after the final line.
- eq('abc\rline2', funcs.getcmdline())
+ -- Yank 2 lines charwise, then paste to cmdline.
+ feed([[<C-\><C-N>gg05lyvj:<C-R>0]])
+ -- <CR> inserted between lines, NOT after the final line.
+ eq('abc\rline2', funcs.getcmdline())
- -- Yank 1 line linewise, then paste to cmdline.
- feed([[<C-\><C-N>ggyy:<C-R>0]])
- -- No <CR> inserted.
- eq('line1abc', funcs.getcmdline())
- end)
+ -- Yank 1 line linewise, then paste to cmdline.
+ feed([[<C-\><C-N>ggyy:<C-R>0]])
+ -- No <CR> inserted.
+ eq('line1abc', funcs.getcmdline())
+ end)
- it('pasting special register inserts <CR>, <NL>', function()
- feed([[:<C-R>="foo\nbar\rbaz"<CR>]])
- eq('foo\nbar\rbaz', funcs.getcmdline())
+ it('pasting special register inserts <CR>, <NL>', function()
+ feed([[:<C-R>="foo\nbar\rbaz"<CR>]])
+ eq('foo\nbar\rbaz', funcs.getcmdline())
+ end)
end)
-end)
-describe('cmdline history', function()
- before_each(clear)
+ it('Ctrl-Shift-V supports entering unsimplified key notations', function()
+ feed(':"<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><CR>')
- it('correctly clears start of the history', function()
- -- Regression test: check absence of the memory leak when clearing start of
- -- the history using ex_getln.c/clr_history().
- eq(1, funcs.histadd(':', 'foo'))
- eq(1, funcs.histdel(':'))
- eq('', funcs.histget(':', -1))
+ eq('"<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>', eval('@:'))
end)
- it('correctly clears end of the history', function()
- -- Regression test: check absence of the memory leak when clearing end of
- -- the history using ex_getln.c/clr_history().
- meths.set_option('history', 1)
- eq(1, funcs.histadd(':', 'foo'))
- eq(1, funcs.histdel(':'))
- eq('', funcs.histget(':', -1))
- end)
+ describe('history', function()
+ it('correctly clears start of the history', function()
+ -- Regression test: check absence of the memory leak when clearing start of
+ -- the history using ex_getln.c/clr_history().
+ eq(1, funcs.histadd(':', 'foo'))
+ eq(1, funcs.histdel(':'))
+ eq('', funcs.histget(':', -1))
+ end)
+
+ it('correctly clears end of the history', function()
+ -- Regression test: check absence of the memory leak when clearing end of
+ -- the history using ex_getln.c/clr_history().
+ meths.set_option('history', 1)
+ eq(1, funcs.histadd(':', 'foo'))
+ eq(1, funcs.histdel(':'))
+ eq('', funcs.histget(':', -1))
+ end)
- it('correctly removes item from history', function()
- -- Regression test: check that ex_getln.c/del_history_idx() correctly clears
- -- history index after removing history entry. If it does not then deleting
- -- history will result in a double free.
- eq(1, funcs.histadd(':', 'foo'))
- eq(1, funcs.histadd(':', 'bar'))
- eq(1, funcs.histadd(':', 'baz'))
- eq(1, funcs.histdel(':', -2))
- eq(1, funcs.histdel(':'))
- eq('', funcs.histget(':', -1))
+ it('correctly removes item from history', function()
+ -- Regression test: check that ex_getln.c/del_history_idx() correctly clears
+ -- history index after removing history entry. If it does not then deleting
+ -- history will result in a double free.
+ eq(1, funcs.histadd(':', 'foo'))
+ eq(1, funcs.histadd(':', 'bar'))
+ eq(1, funcs.histadd(':', 'baz'))
+ eq(1, funcs.histdel(':', -2))
+ eq(1, funcs.histdel(':'))
+ eq('', funcs.histget(':', -1))
+ end)
end)
end)
diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua
index c38acbe96a..684dee69db 100644
--- a/test/functional/editor/mode_insert_spec.lua
+++ b/test/functional/editor/mode_insert_spec.lua
@@ -131,6 +131,11 @@ describe('insert-mode', function()
end)
end)
+ it('Ctrl-Shift-V supports entering unsimplified key notations', function()
+ feed('i<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><Esc>')
+ expect('<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>')
+ end)
+
describe([[With 'insertmode', Insert mode is not re-entered immediately after <C-L>]], function()
before_each(function()
command('set insertmode')
diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua
index 64cf53dfa9..42e97757db 100644
--- a/test/functional/ex_cmds/cmd_map_spec.lua
+++ b/test/functional/ex_cmds/cmd_map_spec.lua
@@ -93,7 +93,7 @@ describe('mappings with <Cmd>', function()
{2:E5521: <Cmd> mapping must end with <CR> before second <Cmd>} |
]])
- command('noremap <F3> <Cmd><F3>let x = 2<cr>')
+ command('noremap <F3> <Cmd>let x = 3')
feed('<F3>')
screen:expect([[
^some short lines |
@@ -103,22 +103,43 @@ describe('mappings with <Cmd>', function()
{1:~ }|
{1:~ }|
{1:~ }|
- {2:E5522: <Cmd> mapping must not include <F3> key} |
+ {2:E5520: <Cmd> mapping must end with <CR>} |
]])
+ eq(0, eval('x'))
+ end)
- command('noremap <F3> <Cmd>let x = 3')
+ it('allows special keys and modifiers', function()
+ command('noremap <F3> <Cmd>normal! <Down><CR>')
feed('<F3>')
screen:expect([[
- ^some short lines |
- of test text |
+ some short lines |
+ ^of test text |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
- {2:E5520: <Cmd> mapping must end with <CR>} |
+ |
]])
- eq(0, eval('x'))
+
+ command('noremap <F3> <Cmd>normal! <C-Right><CR>')
+ feed('<F3>')
+ screen:expect([[
+ some short lines |
+ of ^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('handles character containing K_SPECIAL (0x80) byte correctly', function()
+ command([[noremap <F3> <Cmd>let g:str = '‥'<CR>]])
+ feed('<F3>')
+ eq('‥', eval('g:str'))
end)
it('works in various modes and sees correct `mode()` value', function()
diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/ex_cmds/ctrl_c_spec.lua
index f19fab5550..c2e4bf0fb7 100644
--- a/test/functional/ex_cmds/ctrl_c_spec.lua
+++ b/test/functional/ex_cmds/ctrl_c_spec.lua
@@ -2,10 +2,15 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, source = helpers.clear, helpers.feed, helpers.source
local command = helpers.command
+local sleep = helpers.sleep
describe("CTRL-C (mapped)", function()
+ local screen
+
before_each(function()
clear()
+ screen = Screen.new(52, 6)
+ screen:attach()
end)
it("interrupts :global", function()
@@ -20,14 +25,6 @@ describe("CTRL-C (mapped)", function()
]])
command("silent edit! test/functional/fixtures/bigfile.txt")
- local screen = Screen.new(52, 6)
- screen:attach()
- screen:set_default_attr_ids({
- [0] = {foreground = Screen.colors.White,
- background = Screen.colors.Red},
- [1] = {bold = true,
- foreground = Screen.colors.SeaGreen}
- })
screen:expect([[
^0000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
@@ -56,4 +53,23 @@ describe("CTRL-C (mapped)", function()
end
end
end)
+
+ it('interrupts :sleep', function()
+ command('nnoremap <C-C> <Nop>')
+ feed(':sleep 100<CR>')
+ -- wait for :sleep to start
+ sleep(10)
+ feed('foo<C-C>')
+ -- wait for input buffer to be flushed
+ sleep(10)
+ feed('i')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]])
+ end)
end)
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index 7ff3713d41..79a29cd8d8 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -28,7 +28,10 @@ local function assert_eq(a, b, ...)
if not vim.deep_equal(a, b) then
error(message_parts(": ",
..., "assert_eq failed",
- string.format("left == %q, right == %q", vim.inspect(a), vim.inspect(b))
+ string.format("left == %q, right == %q",
+ table.concat(vim.split(vim.inspect(a), "\n"), ""),
+ table.concat(vim.split(vim.inspect(b), "\n"), "")
+ )
))
end
end
@@ -100,8 +103,12 @@ local tests = {}
function tests.basic_init()
skeleton {
- on_init = function(_params)
- return { capabilities = {} }
+ on_init = function(_)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.None;
+ }
+ }
end;
body = function()
notify('test')
@@ -132,8 +139,11 @@ function tests.prepare_rename_nil()
skeleton {
on_init = function()
return { capabilities = {
- renameProvider = true,
- } }
+ renameProvider = {
+ prepareProvider = true
+ }
+ }
+ }
end;
body = function()
notify('start')
@@ -149,8 +159,11 @@ function tests.prepare_rename_placeholder()
skeleton {
on_init = function()
return { capabilities = {
- renameProvider = true,
- } }
+ renameProvider = {
+ prepareProvider = true
+ }
+ }
+ }
end;
body = function()
notify('start')
@@ -170,8 +183,11 @@ function tests.prepare_rename_range()
skeleton {
on_init = function()
return { capabilities = {
- renameProvider = true,
- } }
+ renameProvider = {
+ prepareProvider = true
+ }
+ }
+ }
end;
body = function()
notify('start')
@@ -193,9 +209,13 @@ end
function tests.prepare_rename_error()
skeleton {
on_init = function()
- return { capabilities = {
- renameProvider = true,
- } }
+ return {
+ capabilities = {
+ renameProvider = {
+ prepareProvider = true
+ },
+ }
+ }
end;
body = function()
notify('start')
@@ -219,10 +239,56 @@ function tests.basic_check_capabilities()
return {
capabilities = {
textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ codeLensProvider = false
+ }
+ }
+ end;
+ body = function()
+ end;
+ }
+end
+
+function tests.text_document_sync_save_bool()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ textDocumentSync = {
+ save = true
+ }
}
}
end;
body = function()
+ notify('start')
+ expect_notification('textDocument/didSave', {textDocument = { uri = "file://" }})
+ notify('shutdown')
+ end;
+ }
+end
+
+function tests.text_document_sync_save_includeText()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ textDocumentSync = {
+ save = {
+ includeText = true
+ }
+ }
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didSave', {
+ textDocument = {
+ uri = "file://"
+ },
+ text = "help me\n"
+ })
+ notify('shutdown')
end;
}
end
@@ -237,6 +303,7 @@ function tests.capabilities_for_client_supports_method()
textDocumentSync = protocol.TextDocumentSyncKind.Full;
completionProvider = true;
hoverProvider = true;
+ renameProvider = false;
definitionProvider = false;
referencesProvider = false;
codeLensProvider = { resolveProvider = true; };
@@ -544,7 +611,15 @@ function tests.basic_check_buffer_open_and_change_incremental()
assert_eq(params.capabilities, expected_capabilities)
return {
capabilities = {
- textDocumentSync = protocol.TextDocumentSyncKind.Incremental;
+ textDocumentSync = {
+ openClose = true,
+ change = protocol.TextDocumentSyncKind.Incremental,
+ willSave = true,
+ willSaveWaitUntil = true,
+ save = {
+ includeText = true,
+ }
+ }
}
}
end;
@@ -673,6 +748,36 @@ function tests.code_action_with_resolve()
}
end
+function tests.code_action_filter()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ codeActionProvider = {
+ resolveProvider = false
+ }
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ local action = {
+ title = 'Action 1',
+ command = 'command'
+ }
+ local preferred_action = {
+ title = 'Action 2',
+ isPreferred = true,
+ command = 'preferred_command',
+ }
+ expect_request('textDocument/codeAction', function()
+ return nil, { action, preferred_action, }
+ end)
+ notify('shutdown')
+ end;
+ }
+end
+
function tests.clientside_commands()
skeleton {
on_init = function()
@@ -687,6 +792,26 @@ function tests.clientside_commands()
}
end
+
+function tests.basic_formatting()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ documentFormattingProvider = true,
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_request('textDocument/formatting', function()
+ return nil, {}
+ end)
+ notify('shutdown')
+ end;
+ }
+end
+
-- Tests will be indexed by TEST_NAME
local kill_timer = vim.loop.new_timer()
diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua
index d3c0b4b938..05d853622e 100644
--- a/test/functional/legacy/eval_spec.lua
+++ b/test/functional/legacy/eval_spec.lua
@@ -46,7 +46,7 @@ describe('eval', function()
command('AR "')
command([[let @" = "abc\n"]])
source('AR "')
- command([[let @" = "abc\r"]])
+ command([[let @" = "abc\<C-m>"]])
command('AR "')
command([[let @= = '"abc"']])
command('AR =')
diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua
index 0f65d5eb65..552c26e7f2 100644
--- a/test/functional/legacy/mapping_spec.lua
+++ b/test/functional/legacy/mapping_spec.lua
@@ -2,7 +2,7 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local feed_command, expect, poke_eventloop = helpers.feed_command, helpers.expect, helpers.poke_eventloop
+local expect, poke_eventloop = helpers.expect, helpers.poke_eventloop
local command, eq, eval, meths = helpers.command, helpers.eq, helpers.eval, helpers.meths
local sleep = helpers.sleep
@@ -15,7 +15,7 @@ describe('mapping', function()
]])
-- Abbreviations with р (0x80) should work.
- feed_command('inoreab чкпр vim')
+ command('inoreab чкпр vim')
feed('GAчкпр <esc>')
expect([[
@@ -25,17 +25,15 @@ describe('mapping', function()
it('Ctrl-c works in Insert mode', function()
-- Mapping of ctrl-c in insert mode
- feed_command('set cpo-=< cpo-=k')
- feed_command('inoremap <c-c> <ctrl-c>')
- feed_command('cnoremap <c-c> dummy')
- feed_command('cunmap <c-c>')
+ command('set cpo-=< cpo-=k')
+ command('inoremap <c-c> <ctrl-c>')
+ command('cnoremap <c-c> dummy')
+ command('cunmap <c-c>')
feed('GA<cr>')
- feed('TEST2: CTRL-C |')
+ -- XXX: editor must be in Insert mode before <C-C> is put into input buffer
poke_eventloop()
- feed('<c-c>A|<cr><esc>')
- poke_eventloop()
- feed_command('unmap <c-c>')
- feed_command('unmap! <c-c>')
+ feed('TEST2: CTRL-C |<c-c>A|<cr><esc>')
+ command('unmap! <c-c>')
expect([[
@@ -44,13 +42,12 @@ describe('mapping', function()
end)
it('Ctrl-c works in Visual mode', function()
- feed_command([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]])
+ command([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]])
feed('GV')
- -- XXX: For some reason the mapping is only triggered
- -- when <C-c> is in a separate feed command.
+ -- XXX: editor must be in Visual mode before <C-C> is put into input buffer
poke_eventloop()
- feed('<c-c>')
- feed_command('vunmap <c-c>')
+ feed('vV<c-c>')
+ command('vunmap <c-c>')
expect([[
@@ -59,23 +56,23 @@ describe('mapping', function()
it('langmap', function()
-- langmap should not get remapped in insert mode.
- feed_command('inoremap { FAIL_ilangmap')
- feed_command('set langmap=+{ langnoremap')
+ command('inoremap { FAIL_ilangmap')
+ command('set langmap=+{ langnoremap')
feed('o+<esc>')
-- Insert mode expr mapping with langmap.
- feed_command('inoremap <expr> { "FAIL_iexplangmap"')
+ command('inoremap <expr> { "FAIL_iexplangmap"')
feed('o+<esc>')
-- langmap should not get remapped in cmdline mode.
- feed_command('cnoremap { FAIL_clangmap')
+ command('cnoremap { FAIL_clangmap')
feed('o+<esc>')
- feed_command('cunmap {')
+ command('cunmap {')
-- cmdline mode expr mapping with langmap.
- feed_command('cnoremap <expr> { "FAIL_cexplangmap"')
+ command('cnoremap <expr> { "FAIL_cexplangmap"')
feed('o+<esc>')
- feed_command('cunmap {')
+ command('cunmap {')
-- Assert buffer contents.
expect([[
@@ -93,10 +90,10 @@ describe('mapping', function()
]])
-- Vim's issue #212 (feedkeys insert mapping at current position)
- feed_command('nnoremap . :call feedkeys(".", "in")<cr>')
+ command('nnoremap . :call feedkeys(".", "in")<cr>')
feed('/^a b<cr>')
feed('0qqdw.ifoo<esc>qj0@q<esc>')
- feed_command('unmap .')
+ command('unmap .')
expect([[
fooc d
fooc d
@@ -105,15 +102,15 @@ describe('mapping', function()
it('i_CTRL-G_U', function()
-- <c-g>U<cursor> works only within a single line
- feed_command('imapclear')
- feed_command('imap ( ()<c-g>U<left>')
+ command('imapclear')
+ command('imap ( ()<c-g>U<left>')
feed('G2o<esc>ki<cr>Test1: text with a (here some more text<esc>k.')
-- test undo
feed('G2o<esc>ki<cr>Test2: text wit a (here some more text [und undo]<c-g>u<esc>k.u')
- feed_command('imapclear')
- feed_command('set whichwrap=<,>,[,]')
+ command('imapclear')
+ command('set whichwrap=<,>,[,]')
feed('G3o<esc>2k')
- feed_command([[:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."]])
+ command([[:exe ":norm! iTest3: text with a (parenthesis here\<C-G>U\<Right>new line here\<esc>\<up>\<up>."]])
expect([[
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index d9a8dfd2e8..73e4d7ca79 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -492,6 +492,10 @@ describe('lua stdlib', function()
it('vim.tbl_get', function()
eq(true, exec_lua("return vim.tbl_get({ test = { nested_test = true }}, 'test', 'nested_test')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = true }, 'unindexable', 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = 1 }, 'unindexable', 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = coroutine.create(function () end) }, 'unindexable', 'missing_key')"))
+ eq(NIL, exec_lua("return vim.tbl_get({ unindexable = function () end }, 'unindexable', 'missing_key')"))
eq(NIL, exec_lua("return vim.tbl_get({}, 'missing_key')"))
eq(NIL, exec_lua("return vim.tbl_get({})"))
end)
diff --git a/test/functional/options/pastetoggle_spec.lua b/test/functional/options/pastetoggle_spec.lua
index a1f86f73ff..40c14fa187 100644
--- a/test/functional/options/pastetoggle_spec.lua
+++ b/test/functional/options/pastetoggle_spec.lua
@@ -4,16 +4,14 @@ local clear = helpers.clear
local feed = helpers.feed
local command = helpers.command
local eq = helpers.eq
+local expect = helpers.expect
local eval = helpers.eval
+local insert = helpers.insert
+local meths = helpers.meths
local sleep = helpers.sleep
-local expect = helpers.expect
describe("'pastetoggle' option", function()
- before_each(function()
- clear()
- command('set nopaste')
- end)
-
+ before_each(clear)
it("toggles 'paste'", function()
command('set pastetoggle=a')
eq(0, eval('&paste'))
@@ -22,19 +20,71 @@ describe("'pastetoggle' option", function()
feed('j')
eq(1, eval('&paste'))
end)
+ describe("multiple key 'pastetoggle'", function()
+ before_each(function()
+ eq(0, eval('&paste'))
+ command('set timeoutlen=1 ttimeoutlen=10000')
+ end)
+ it('is waited for when chars are typed', function()
+ local pastetoggle = 'lllll'
+ command('set pastetoggle=' .. pastetoggle)
+ feed(pastetoggle:sub(0, 2))
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed(pastetoggle:sub(3, -1))
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ eq(1, eval('&paste'))
+ end)
+ it('is not waited for when there are no typed chars after mapped chars', function()
+ command('set pastetoggle=abc')
+ command('imap d a')
+ meths.feedkeys('id', 't', true)
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed('bc')
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ -- 'ttimeoutlen' should NOT apply
+ eq(0, eval('&paste'))
+ end)
- it('does not wait for timeout', function()
- command('set pastetoggle=abc')
- command('set ttimeoutlen=9999999')
- eq(0, eval('&paste'))
- -- n.b. need <esc> to return from vgetorpeek()
- feed('abc<esc>')
- eq(1, eval('&paste'))
- feed('ab')
- sleep(10)
- feed('c<esc>')
- expect('bc')
- eq(1, eval('&paste'))
+ it('is waited for when there are typed chars after mapped chars', function()
+ command('set pastetoggle=abc')
+ command('imap d a')
+ meths.feedkeys('idb', 't', true)
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed('c')
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ -- 'ttimeoutlen' should apply
+ eq(1, eval('&paste'))
+ end)
+
+ it('is waited for when there are typed chars after noremapped chars', function()
+ command('set pastetoggle=abc')
+ command('inoremap d a')
+ meths.feedkeys('idb', 't', true)
+ -- sleep() for long enough that vgetorpeek() is gotten into, but short
+ -- enough that ttimeoutlen is not reached.
+ sleep(200)
+ feed('c')
+ -- Need another key so that the vgetorpeek() function returns.
+ feed('j')
+ -- 'ttimeoutlen' should apply
+ eq(1, eval('&paste'))
+ end)
+ end)
+ it('does not interfere with character-find', function()
+ insert('foo,bar')
+ feed('0')
+ command('set pastetoggle=,sp')
+ feed('dt,')
+ expect(',bar')
end)
end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 436b431e38..3ee293db66 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -289,7 +289,7 @@ describe('LSP', function()
test_rpc_server {
test_name = "basic_init";
on_init = function(client)
- eq(0, client.resolved_capabilities().text_document_did_change)
+ eq(0, client.server_capabilities().textDocumentSync.change)
client.request('shutdown')
client.notify('exit')
client.stop()
@@ -407,10 +407,9 @@ describe('LSP', function()
on_init = function(client)
client.stop()
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_save)
- eq(false, client.resolved_capabilities().code_lens)
- eq(false, client.resolved_capabilities().code_lens_resolve)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq({includeText = false}, client.server_capabilities().textDocumentSync.save)
+ eq(false, client.server_capabilities().codeLensProvider)
end;
on_exit = function(code, signal)
eq(0, code, "exit code", fake_lsp_logfile)
@@ -422,6 +421,67 @@ describe('LSP', function()
}
end)
+ it('_text_document_did_save_handler sends didSave with bool textDocumentSync.save', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "text_document_sync_save_bool";
+ on_init = function(c)
+ client = c
+ 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}, "expected handler")
+ if ctx.method == "start" then
+ exec_lua([=[
+ BUFFER = vim.api.nvim_get_current_buf()
+ lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
+ lsp._text_document_did_save_handler(BUFFER)
+ ]=])
+ else
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ it('_text_document_did_save_handler sends didSave including text if server capability is set', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "text_document_sync_save_includeText";
+ on_init = function(c)
+ client = c
+ 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}, "expected handler")
+ if ctx.method == "start" then
+ exec_lua([=[
+ BUFFER = vim.api.nvim_get_current_buf()
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, true, {"help me"})
+ lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
+ lsp._text_document_did_save_handler(BUFFER)
+ ]=])
+ else
+ client.stop()
+ end
+ end;
+ }
+ end)
+
it('client.supports_methods() should validate capabilities', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
@@ -430,14 +490,19 @@ describe('LSP', function()
test_name = "capabilities_for_client_supports_method";
on_init = function(client)
client.stop()
- local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().completion)
- eq(true, client.resolved_capabilities().hover)
- eq(false, client.resolved_capabilities().goto_definition)
- eq(false, client.resolved_capabilities().rename)
- eq(true, client.resolved_capabilities().code_lens)
- eq(true, client.resolved_capabilities().code_lens_resolve)
+ local expected_sync_capabilities = {
+ change = 1,
+ openClose = true,
+ save = { includeText = false },
+ willSave = false,
+ willSaveWaitUntil = false,
+ }
+ eq(expected_sync_capabilities, client.server_capabilities().textDocumentSync)
+ eq(true, client.server_capabilities().completionProvider)
+ eq(true, client.server_capabilities().hoverProvider)
+ eq(false, client.server_capabilities().definitionProvider)
+ eq(false, client.server_capabilities().renameProvider)
+ eq(true, client.server_capabilities().codeLensProvider.resolveProvider)
-- known methods for resolved capabilities
eq(true, client.supports_method("textDocument/hover"))
@@ -720,8 +785,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
client.notify('finish')
end;
on_exit = function(code, signal)
@@ -761,8 +826,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(not lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID), "Shouldn't attach twice")
]]
@@ -804,8 +869,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -847,8 +912,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -896,8 +961,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(full_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(full_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -947,8 +1012,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental")
- eq(sync_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -998,8 +1063,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental")
- eq(sync_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -1047,8 +1112,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental")
- eq(sync_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -1091,8 +1156,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(sync_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -1142,8 +1207,8 @@ describe('LSP', function()
on_init = function(_client)
client = _client
local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
- eq(sync_kind, client.resolved_capabilities().text_document_did_change)
- eq(true, client.resolved_capabilities().text_document_open_close)
+ eq(sync_kind, client.server_capabilities().textDocumentSync.change)
+ eq(true, client.server_capabilities().textDocumentSync.openClose)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
@@ -2583,7 +2648,7 @@ describe('LSP', function()
test_name = test.name;
on_init = function(_client)
client = _client
- eq(true, client.resolved_capabilities().rename)
+ eq(true, client.server_capabilities().renameProvider.prepareProvider)
end;
on_setup = function()
exec_lua([=[
@@ -2665,6 +2730,42 @@ describe('LSP', function()
end
}
end)
+ it('Filters and automatically applies action if requested', 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_filter',
+ 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['preferred_command'] = function(cmd)
+ vim.lsp.commands['executed_preferred'] = function()
+ end
+ end
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ vim.lsp.buf.code_action({ filter = function(a) return a.isPreferred end, apply = true, })
+ ]])
+ elseif ctx.method == 'shutdown' then
+ eq('function', exec_lua[[return type(vim.lsp.commands['executed_preferred'])]])
+ client.stop()
+ end
+ end
+ }
+ end)
end)
describe('vim.lsp.commands', function()
it('Accepts only string keys', function()
@@ -2733,4 +2834,109 @@ describe('LSP', function()
}
end)
end)
+
+ describe("vim.lsp.buf.format", function()
+ it("Aborts with notify if no client matches filter", function()
+ local client
+ test_rpc_server {
+ test_name = "basic_init",
+ on_init = function(c)
+ client = c
+ end,
+ on_handler = function()
+ local notify_msg = exec_lua([[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ local notify_msg
+ local notify = vim.notify
+ vim.notify = function(msg, log_level)
+ notify_msg = msg
+ end
+ vim.lsp.buf.format({ name = 'does-not-exist' })
+ vim.notify = notify
+ return notify_msg
+ ]])
+ eq("[LSP] Format request failed, no matching language servers.", notify_msg)
+ client.stop()
+ end,
+ }
+ end)
+ it("Sends textDocument/formatting request to format buffer", function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_formatting",
+ on_init = function(c)
+ client = c
+ end,
+ on_handler = function(_, _, ctx)
+ table.remove(expected_handlers)
+ if ctx.method == "start" then
+ local notify_msg = exec_lua([[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ local notify_msg
+ local notify = vim.notify
+ vim.notify = function(msg, log_level)
+ notify_msg = msg
+ end
+ vim.lsp.buf.format({ bufnr = bufnr })
+ vim.notify = notify
+ return notify_msg
+ ]])
+ eq(NIL, notify_msg)
+ elseif ctx.method == "shutdown" then
+ client.stop()
+ end
+ end,
+ }
+ end)
+ it('Can format async', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_formatting",
+ on_init = function(c)
+ client = c
+ end,
+ on_handler = function(_, _, ctx)
+ table.remove(expected_handlers)
+ if ctx.method == "start" then
+ local result = exec_lua([[
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+
+ local notify_msg
+ local notify = vim.notify
+ vim.notify = function(msg, log_level)
+ notify_msg = msg
+ end
+
+ local handler = vim.lsp.handlers['textDocument/formatting']
+ local handler_called = false
+ vim.lsp.handlers['textDocument/formatting'] = function(...)
+ handler_called = true
+ end
+
+ vim.lsp.buf.format({ bufnr = bufnr, async = true })
+ vim.wait(1000, function() return handler_called end)
+
+ vim.notify = notify
+ vim.lsp.handlers['textDocument/formatting'] = handler
+ return {notify = notify_msg, handler_called = handler_called}
+ ]])
+ eq({handler_called=true}, result)
+ elseif ctx.method == "shutdown" then
+ client.stop()
+ end
+ end,
+ }
+ end)
+ end)
end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 8925dda730..07582ba602 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -8,6 +8,7 @@ local meths = helpers.meths
local exec_lua = helpers.exec_lua
local write_file = helpers.write_file
local funcs = helpers.funcs
+local eval = helpers.eval
local Screen = require('test.functional.ui.screen')
before_each(clear)
@@ -172,11 +173,20 @@ describe('input pairs', function()
eq('\t\t', curbuf_contents())
end)
- it('can be mapped', function()
- command('inoremap <tab> TAB!')
- command('inoremap <c-i> CTRL-I!')
- feed('i<tab><c-i><esc>')
- eq('TAB!CTRL-I!', curbuf_contents())
+ describe('can be mapped separately', function()
+ it('if <tab> is mapped after <c-i>', function()
+ command('inoremap <c-i> CTRL-I!')
+ command('inoremap <tab> TAB!')
+ feed('i<tab><c-i><esc>')
+ eq('TAB!CTRL-I!', curbuf_contents())
+ end)
+
+ it('if <tab> is mapped before <c-i>', function()
+ command('inoremap <tab> TAB!')
+ command('inoremap <c-i> CTRL-I!')
+ feed('i<tab><c-i><esc>')
+ eq('TAB!CTRL-I!', curbuf_contents())
+ end)
end)
end)
@@ -186,11 +196,20 @@ describe('input pairs', function()
eq('unos\ndos\ntres', curbuf_contents())
end)
- it('can be mapped', function()
- command('inoremap <c-m> SNIPPET!')
- command('inoremap <cr> , and then<cr>')
- feed('iunos<c-m>dos<cr>tres<esc>')
- eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
+ describe('can be mapped separately', function()
+ it('if <cr> is mapped after <c-m>', function()
+ command('inoremap <c-m> SNIPPET!')
+ command('inoremap <cr> , and then<cr>')
+ feed('iunos<c-m>dos<cr>tres<esc>')
+ eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
+ end)
+
+ it('if <cr> is mapped before <c-m>', function()
+ command('inoremap <cr> , and then<cr>')
+ command('inoremap <c-m> SNIPPET!')
+ feed('iunos<c-m>dos<cr>tres<esc>')
+ eq('unosSNIPPET!dos, and then\ntres', curbuf_contents())
+ end)
end)
end)
@@ -200,11 +219,20 @@ describe('input pairs', function()
eq('doubledoublesingle', curbuf_contents())
end)
- it('can be mapped', function()
- command('inoremap <c-[> HALLOJ!')
- command('inoremap <esc> ,<esc>')
- feed('2adubbel<c-[>upp<esc>')
- eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
+ describe('can be mapped separately', function()
+ it('if <esc> is mapped after <c-[>', function()
+ command('inoremap <c-[> HALLOJ!')
+ command('inoremap <esc> ,<esc>')
+ feed('2adubbel<c-[>upp<esc>')
+ eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
+ end)
+
+ it('if <esc> is mapped before <c-[>', function()
+ command('inoremap <esc> ,<esc>')
+ command('inoremap <c-[> HALLOJ!')
+ feed('2adubbel<c-[>upp<esc>')
+ eq('dubbelHALLOJ!upp,dubbelHALLOJ!upp,', curbuf_contents())
+ end)
end)
end)
end)
@@ -216,6 +244,80 @@ it('Ctrl-6 is Ctrl-^ vim-patch:8.1.2333', function()
eq('aaa', funcs.bufname())
end)
+it('c_CTRL-R_CTRL-R, i_CTRL-R_CTRL-R, i_CTRL-G_CTRL-K work properly vim-patch:8.1.2346', function()
+ command('set timeoutlen=10')
+
+ command([[let @a = 'aaa']])
+ feed([[:let x = '<C-R><C-R>a'<CR>]])
+ eq([[let x = 'aaa']], eval('@:'))
+
+ feed('a<C-R><C-R>a<Esc>')
+ expect('aaa')
+ command('bwipe!')
+
+ feed('axx<CR>yy<C-G><C-K>a<Esc>')
+ expect([[
+ axx
+ yy]])
+end)
+
+it('typing a simplifiable key at hit-enter prompt triggers mapping vim-patch:8.2.0839', function()
+ local screen = Screen.new(60,8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {bold = true, reverse = true}, -- MsgSeparator
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
+ })
+ screen:attach()
+ command([[nnoremap <C-6> <Cmd>echo 'hit ctrl-6'<CR>]])
+ feed_command('ls')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<C-6>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ hit ctrl-6 |
+ ]])
+end)
+
+it('mixing simplified and unsimplified keys can trigger mapping vim-patch:8.2.0916', function()
+ command('set timeoutlen=10')
+ command([[imap ' <C-W>]])
+ command('imap <C-W><C-A> c-a')
+ feed([[a'<C-A>]])
+ expect('c-a')
+end)
+
+it('unsimplified mapping works when there was a partial match vim-patch:8.2.4504', function()
+ command('set timeoutlen=10')
+ command('nnoremap <C-J> a')
+ command('nnoremap <NL> x')
+ command('nnoremap <C-J>x <Nop>')
+ funcs.setline(1, 'x')
+ -- CTRL-J b should have trigger the <C-J> mapping and then insert "b"
+ feed('<C-J>b<Esc>')
+ expect('xb')
+end)
+
+it('rhs of a mapping is not simplified', function()
+ command('nnoremap <Plug>foo <C-J>')
+ eq('<C-J>', funcs.maparg('<Plug>foo'))
+end)
+
describe('input non-printable chars', function()
after_each(function()
os.remove('Xtest-overwrite')
diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua
index bedf7e5498..9cc6424d31 100644
--- a/test/functional/vimscript/system_spec.lua
+++ b/test/functional/vimscript/system_spec.lua
@@ -268,7 +268,7 @@ describe('system()', function()
:call system("for /L %I in (1,0,2) do @echo y") |]]
or [[
:call system("yes") |]]))
- feed('<c-c>')
+ feed('foo<c-c>')
screen:expect([[
^ |
~ |
@@ -286,6 +286,49 @@ describe('system()', function()
Type :qa and press <Enter> to exit Nvim |
]])
end)
+
+ it('`yes` interrupted with mapped CTRL-C', function()
+ command('nnoremap <C-C> i')
+ feed(':call system("' .. (iswin()
+ and 'for /L %I in (1,0,2) do @echo y'
+ or 'yes') .. '")<cr>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+]] .. (iswin()
+ and [[
+ :call system("for /L %I in (1,0,2) do @echo y") |]]
+ or [[
+ :call system("yes") |]]))
+ feed('foo<c-c>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ -- INSERT -- |
+ ]])
+ end)
end)
describe('passing no input', function()
diff --git a/test/unit/keymap_spec.lua b/test/unit/keymap_spec.lua
index 595a19eb17..1f1f32bb9e 100644
--- a/test/unit/keymap_spec.lua
+++ b/test/unit/keymap_spec.lua
@@ -5,7 +5,8 @@ local ffi = helpers.ffi
local eq = helpers.eq
local neq = helpers.neq
-local keymap = helpers.cimport("./src/nvim/keymap.h")
+local keymap = helpers.cimport('./src/nvim/keymap.h')
+local NULL = helpers.NULL
describe('keymap.c', function()
@@ -15,12 +16,12 @@ describe('keymap.c', function()
itp('no keycode', function()
srcp[0] = 'abc'
- eq(0, keymap.find_special_key(srcp, 3, modp, false, false, false))
+ eq(0, keymap.find_special_key(srcp, 3, modp, 0, NULL))
end)
itp('keycode with multiple modifiers', function()
srcp[0] = '<C-M-S-A>'
- neq(0, keymap.find_special_key(srcp, 9, modp, false, false, false))
+ neq(0, keymap.find_special_key(srcp, 9, modp, 0, NULL))
neq(0, modp[0])
end)
@@ -28,22 +29,22 @@ describe('keymap.c', function()
-- Compare other capitalizations to this.
srcp[0] = '<C-A>'
local all_caps_key =
- keymap.find_special_key(srcp, 5, modp, false, false, false)
+ keymap.find_special_key(srcp, 5, modp, 0, NULL)
local all_caps_mod = modp[0]
srcp[0] = '<C-a>'
eq(all_caps_key,
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0])
srcp[0] = '<c-A>'
eq(all_caps_key,
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0])
srcp[0] = '<c-a>'
eq(all_caps_key,
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
eq(all_caps_mod, modp[0])
end)
@@ -51,20 +52,20 @@ describe('keymap.c', function()
-- Unescaped with in_string=false
srcp[0] = '<C-">'
eq(string.byte('"'),
- keymap.find_special_key(srcp, 5, modp, false, false, false))
+ keymap.find_special_key(srcp, 5, modp, 0, NULL))
-- Unescaped with in_string=true
- eq(0, keymap.find_special_key(srcp, 5, modp, false, false, true))
+ eq(0, keymap.find_special_key(srcp, 5, modp, keymap.FSK_IN_STRING, NULL))
-- Escaped with in_string=false
srcp[0] = '<C-\\">'
-- Should fail because the key is invalid
-- (more than 1 non-modifier character).
- eq(0, keymap.find_special_key(srcp, 6, modp, false, false, false))
+ eq(0, keymap.find_special_key(srcp, 6, modp, 0, NULL))
-- Escaped with in_string=true
eq(string.byte('"'),
- keymap.find_special_key(srcp, 6, modp, false, false, true))
+ keymap.find_special_key(srcp, 6, modp, keymap.FSK_IN_STRING, NULL))
end)
end)