aboutsummaryrefslogtreecommitdiff
path: root/test/functional/api
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/api')
-rw-r--r--test/functional/api/autocmd_spec.lua777
-rw-r--r--test/functional/api/buffer_spec.lua35
-rw-r--r--test/functional/api/buffer_updates_spec.lua4
-rw-r--r--test/functional/api/command_spec.lua203
-rw-r--r--test/functional/api/extmark_spec.lua71
-rw-r--r--test/functional/api/highlight_spec.lua76
-rw-r--r--test/functional/api/keymap_spec.lua230
-rw-r--r--test/functional/api/server_requests_spec.lua6
-rw-r--r--test/functional/api/vim_spec.lua98
-rw-r--r--test/functional/api/window_spec.lua44
10 files changed, 1521 insertions, 23 deletions
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua
new file mode 100644
index 0000000000..e50e812b4b
--- /dev/null
+++ b/test/functional/api/autocmd_spec.lua
@@ -0,0 +1,777 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local neq = helpers.neq
+local exec_lua = helpers.exec_lua
+local matches = helpers.matches
+local meths = helpers.meths
+local source = helpers.source
+
+before_each(clear)
+
+describe('autocmd api', function()
+ describe('nvim_create_autocmd', function()
+ it('does not allow "command" and "callback" in the same autocmd', function()
+ local ok, _ = pcall(meths.create_autocmd, "BufReadPost", {
+ pattern = "*.py,*.pyi",
+ command = "echo 'Should Have Errored",
+ callback = "not allowed",
+ })
+
+ eq(false, ok)
+ end)
+
+ it('doesnt leak when you use ++once', function()
+ eq(1, exec_lua([[
+ local count = 0
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function() count = count + 1 end,
+ once = true
+ })
+
+ vim.cmd "set filetype=txt"
+ vim.cmd "set filetype=python"
+
+ return count
+ ]], {}))
+ end)
+
+ it('allows passing buffer by key', function()
+ meths.set_var('called', 0)
+
+ meths.create_autocmd("FileType", {
+ command = "let g:called = g:called + 1",
+ buffer = 0,
+ })
+
+ meths.command "set filetype=txt"
+ eq(1, meths.get_var('called'))
+
+ -- switch to a new buffer
+ meths.command "new"
+ meths.command "set filetype=python"
+
+ eq(1, meths.get_var('called'))
+ end)
+
+ it('does not allow passing buffer and patterns', function()
+ local ok = pcall(meths.create_autocmd, "Filetype", {
+ command = "let g:called = g:called + 1",
+ buffer = 0,
+ pattern = "*.py",
+ })
+
+ eq(false, ok)
+ end)
+
+ it('does not allow passing invalid buffers', function()
+ local ok, msg = pcall(meths.create_autocmd, "Filetype", {
+ command = "let g:called = g:called + 1",
+ buffer = -1,
+ })
+
+ eq(false, ok)
+ matches('Invalid buffer id', msg)
+ end)
+
+ it('errors on non-functions for cb', function()
+ eq(false, pcall(exec_lua, [[
+ vim.api.nvim_create_autocmd("BufReadPost", {
+ pattern = "*.py,*.pyi",
+ callback = 5,
+ })
+ ]]))
+ end)
+
+ it('allow passing pattern and <buffer> in same pattern', function()
+ local ok = pcall(meths.create_autocmd, "BufReadPost", {
+ pattern = "*.py,<buffer>",
+ command = "echo 'Should Not Error'"
+ })
+
+ eq(true, ok)
+ end)
+
+ it('should handle multiple values as comma separated list', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*.py,*.pyi",
+ command = "echo 'Should Not Have Errored'"
+ })
+
+ -- We should have one autocmd for *.py and one for *.pyi
+ eq(2, #meths.get_autocmds { event = "BufReadPost" })
+ end)
+
+ it('should handle multiple values as array', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = { "*.py", "*.pyi", },
+ command = "echo 'Should Not Have Errored'"
+ })
+
+ -- We should have one autocmd for *.py and one for *.pyi
+ eq(2, #meths.get_autocmds { event = "BufReadPost" })
+ end)
+
+ describe('desc', function()
+ it('can add description to one autocmd', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*.py",
+ command = "echo 'Should Not Have Errored'",
+ desc = "Can show description",
+ })
+
+ eq("Can show description", meths.get_autocmds { event = "BufReadPost" }[1].desc)
+ end)
+
+ it('can add description to multiple autocmd', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = {"*.py", "*.pyi"},
+ command = "echo 'Should Not Have Errored'",
+ desc = "Can show description",
+ })
+
+ local aus = meths.get_autocmds { event = "BufReadPost" }
+ eq(2, #aus)
+ eq("Can show description", aus[1].desc)
+ eq("Can show description", aus[2].desc)
+ end)
+ end)
+
+ pending('script and verbose settings', function()
+ it('marks API client', function()
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*.py",
+ command = "echo 'Should Not Have Errored'",
+ desc = "Can show description",
+ })
+
+ local aus = meths.get_autocmds { event = "BufReadPost" }
+ eq(1, #aus, aus)
+ end)
+ end)
+ end)
+
+ describe('nvim_get_autocmds', function()
+ describe('events', function()
+ it('should return one autocmd when there is only one for an event', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ eq(1, #aus)
+ end)
+
+ it('should return two autocmds when there are two for an event', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+ command [[au InsertEnter * :echo "2"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ eq(2, #aus)
+ end)
+
+ it('should return the same thing if you use string or list', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+ command [[au InsertEnter * :echo "2"]]
+
+ local string_aus = meths.get_autocmds { event = "InsertEnter" }
+ local array_aus = meths.get_autocmds { event = { "InsertEnter" } }
+ eq(string_aus, array_aus)
+ end)
+
+ it('should return two autocmds when there are two for an event', function()
+ command [[au! InsertEnter]]
+ command [[au! InsertLeave]]
+ command [[au InsertEnter * :echo "1"]]
+ command [[au InsertEnter * :echo "2"]]
+
+ local aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ eq(2, #aus)
+ end)
+
+ it('should return different IDs for different autocmds', function()
+ command [[au! InsertEnter]]
+ command [[au! InsertLeave]]
+ command [[au InsertEnter * :echo "1"]]
+ source [[
+ call nvim_create_autocmd("InsertLeave", #{
+ \ command: ":echo 2",
+ \ })
+ ]]
+
+ local aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ local first = aus[1]
+ eq(first.id, nil)
+
+ -- TODO: Maybe don't have this number, just assert it's not nil
+ local second = aus[2]
+ neq(second.id, nil)
+
+ meths.del_autocmd(second.id)
+ local new_aus = meths.get_autocmds { event = { "InsertEnter", "InsertLeave" } }
+ eq(1, #new_aus)
+ eq(first, new_aus[1])
+ end)
+
+ it('should return event name', function()
+ command [[au! InsertEnter]]
+ command [[au InsertEnter * :echo "1"]]
+
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ eq({ { buflocal = false, command = ':echo "1"', event = "InsertEnter", once = false, pattern = "*" } }, aus)
+ end)
+ end)
+
+ describe('groups', function()
+ before_each(function()
+ command [[au! InsertEnter]]
+
+ command [[au InsertEnter * :echo "No Group"]]
+
+ command [[augroup GroupOne]]
+ command [[ au InsertEnter * :echo "GroupOne:1"]]
+ command [[augroup END]]
+
+ command [[augroup GroupTwo]]
+ command [[ au InsertEnter * :echo "GroupTwo:2"]]
+ command [[ au InsertEnter * :echo "GroupTwo:3"]]
+ command [[augroup END]]
+ end)
+
+ it('should return all groups if no group is specified', function()
+ local aus = meths.get_autocmds { event = "InsertEnter" }
+ if #aus ~= 4 then
+ eq({}, aus)
+ end
+
+ eq(4, #aus)
+ end)
+
+ it('should return only the group specified', function()
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ group = "GroupOne",
+ }
+
+ eq(1, #aus)
+ eq([[:echo "GroupOne:1"]], aus[1].command)
+ end)
+
+ it('should return only the group specified, multiple values', function()
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ group = "GroupTwo",
+ }
+
+ eq(2, #aus)
+ eq([[:echo "GroupTwo:2"]], aus[1].command)
+ eq([[:echo "GroupTwo:3"]], aus[2].command)
+ end)
+ end)
+
+ describe('groups: 2', function()
+ it('raises error for undefined augroup', function()
+ local success, code = unpack(meths.exec_lua([[
+ return {pcall(function()
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ group = "NotDefined",
+ command = "echo 'hello'",
+ })
+ end)}
+ ]], {}))
+
+ eq(false, success)
+ matches('invalid augroup: NotDefined', code)
+ end)
+ end)
+
+ describe('patterns', function()
+ before_each(function()
+ command [[au! InsertEnter]]
+
+ command [[au InsertEnter * :echo "No Group"]]
+ command [[au InsertEnter *.one :echo "GroupOne:1"]]
+ command [[au InsertEnter *.two :echo "GroupTwo:2"]]
+ command [[au InsertEnter *.two :echo "GroupTwo:3"]]
+ command [[au InsertEnter <buffer> :echo "Buffer"]]
+ end)
+
+ it('should should return for literal match', function()
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "*"
+ }
+
+ eq(1, #aus)
+ eq([[:echo "No Group"]], aus[1].command)
+ end)
+
+ it('should return for multiple matches', function()
+ -- vim.api.nvim_get_autocmds
+ local aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = { "*.one", "*.two" },
+ }
+
+ eq(3, #aus)
+ eq([[:echo "GroupOne:1"]], aus[1].command)
+ eq([[:echo "GroupTwo:2"]], aus[2].command)
+ eq([[:echo "GroupTwo:3"]], aus[3].command)
+ end)
+
+ it('should work for buffer autocmds', function()
+ local normalized_aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "<buffer=1>",
+ }
+
+ local raw_aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "<buffer>",
+ }
+
+ local zero_aus = meths.get_autocmds {
+ event = "InsertEnter",
+ pattern = "<buffer=0>",
+ }
+
+ eq(normalized_aus, raw_aus)
+ eq(normalized_aus, zero_aus)
+ eq([[:echo "Buffer"]], normalized_aus[1].command)
+ end)
+ end)
+ end)
+
+ describe('nvim_do_autocmd', function()
+ it("can trigger builtin autocmds", function()
+ meths.set_var("autocmd_executed", false)
+
+ meths.create_autocmd("BufReadPost", {
+ pattern = "*",
+ command = "let g:autocmd_executed = v:true",
+ })
+
+ eq(false, meths.get_var("autocmd_executed"))
+ meths.do_autocmd("BufReadPost", {})
+ eq(true, meths.get_var("autocmd_executed"))
+ end)
+
+ it("can pass the buffer", function()
+ meths.set_var("buffer_executed", -1)
+ eq(-1, meths.get_var("buffer_executed"))
+
+ meths.create_autocmd("BufLeave", {
+ pattern = "*",
+ command = 'let g:buffer_executed = +expand("<abuf>")',
+ })
+
+ -- Doesn't execute for other non-matching events
+ meths.do_autocmd("CursorHold", { buffer = 1 })
+ eq(-1, meths.get_var("buffer_executed"))
+
+ meths.do_autocmd("BufLeave", { buffer = 1 })
+ eq(1, meths.get_var("buffer_executed"))
+ end)
+
+ it("can pass the filename, pattern match", function()
+ meths.set_var("filename_executed", 'none')
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.create_autocmd("BufEnter", {
+ pattern = "*.py",
+ command = 'let g:filename_executed = expand("<afile>")',
+ })
+
+ -- Doesn't execute for other non-matching events
+ meths.do_autocmd("CursorHold", { buffer = 1 })
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.command('edit __init__.py')
+ eq('__init__.py', meths.get_var("filename_executed"))
+ end)
+
+ it('cannot pass buf and fname', function()
+ local ok = pcall(meths.do_autocmd, "BufReadPre", { pattern = "literally_cannot_error.rs", buffer = 1 })
+ eq(false, ok)
+ end)
+
+ it("can pass the filename, exact match", function()
+ meths.set_var("filename_executed", 'none')
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.command('edit other_file.txt')
+ meths.command('edit __init__.py')
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.create_autocmd("CursorHoldI", {
+ pattern = "__init__.py",
+ command = 'let g:filename_executed = expand("<afile>")',
+ })
+
+ -- Doesn't execute for other non-matching events
+ meths.do_autocmd("CursorHoldI", { buffer = 1 })
+ eq('none', meths.get_var("filename_executed"))
+
+ meths.do_autocmd("CursorHoldI", { buffer = tonumber(meths.get_current_buf()) })
+ eq('__init__.py', meths.get_var("filename_executed"))
+
+ -- Reset filename
+ meths.set_var("filename_executed", 'none')
+
+ meths.do_autocmd("CursorHoldI", { pattern = '__init__.py' })
+ eq('__init__.py', meths.get_var("filename_executed"))
+ end)
+
+ it("works with user autocmds", function()
+ meths.set_var("matched", 'none')
+
+ meths.create_autocmd("User", {
+ pattern = "TestCommand",
+ command = 'let g:matched = "matched"'
+ })
+
+ meths.do_autocmd("User", { pattern = "OtherCommand" })
+ eq('none', meths.get_var('matched'))
+ meths.do_autocmd("User", { pattern = "TestCommand" })
+ eq('matched', meths.get_var('matched'))
+ end)
+ end)
+
+ describe('nvim_create_augroup', function()
+ before_each(function()
+ clear()
+
+ meths.set_var('executed', 0)
+ end)
+
+ local make_counting_autocmd = function(opts)
+ opts = opts or {}
+
+ local resulting = {
+ pattern = "*",
+ command = "let g:executed = g:executed + 1",
+ }
+
+ resulting.group = opts.group
+ resulting.once = opts.once
+
+ meths.create_autocmd("FileType", resulting)
+ end
+
+ local set_ft = function(ft)
+ ft = ft or "txt"
+ source(string.format("set filetype=%s", ft))
+ end
+
+ local get_executed_count = function()
+ return meths.get_var('executed')
+ end
+
+ it('can be added in a group', function()
+ local augroup = "TestGroup"
+ meths.create_augroup(augroup, { clear = true })
+ make_counting_autocmd { group = augroup }
+
+ set_ft("txt")
+ set_ft("python")
+
+ eq(get_executed_count(), 2)
+ end)
+
+ it('works getting called multiple times', function()
+ make_counting_autocmd()
+ set_ft()
+ set_ft()
+ set_ft()
+
+ eq(get_executed_count(), 3)
+ end)
+
+ it('handles ++once', function()
+ make_counting_autocmd {once = true}
+ set_ft('txt')
+ set_ft('help')
+ set_ft('txt')
+ set_ft('help')
+
+ eq(get_executed_count(), 1)
+ end)
+
+ it('errors on unexpected keys', function()
+ local success, code = pcall(meths.create_autocmd, "FileType", {
+ pattern = "*",
+ not_a_valid_key = "NotDefined",
+ })
+
+ eq(false, success)
+ matches('not_a_valid_key', code)
+ end)
+
+ it('can execute simple callback', function()
+ exec_lua([[
+ vim.g.executed = false
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function() vim.g.executed = true end,
+ })
+ ]], {})
+
+
+ eq(true, exec_lua([[
+ vim.cmd "set filetype=txt"
+ return vim.g.executed
+ ]], {}))
+ end)
+
+ it('calls multiple lua callbacks for the same autocmd execution', function()
+ eq(4, exec_lua([[
+ local count = 0
+ local counter = function()
+ count = count + 1
+ end
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = counter,
+ })
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = counter,
+ })
+
+ vim.cmd "set filetype=txt"
+ vim.cmd "set filetype=txt"
+
+ return count
+ ]], {}))
+ end)
+
+ it('properly releases functions with ++once', function()
+ exec_lua([[
+ WeakTable = setmetatable({}, { __mode = "k" })
+
+ OnceCount = 0
+
+ MyVal = {}
+ WeakTable[MyVal] = true
+
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function()
+ OnceCount = OnceCount + 1
+ MyVal = {}
+ end,
+ once = true
+ })
+ ]])
+
+ command [[set filetype=txt]]
+ eq(1, exec_lua([[return OnceCount]], {}))
+
+ exec_lua([[collectgarbage()]], {})
+
+ command [[set filetype=txt]]
+ eq(1, exec_lua([[return OnceCount]], {}))
+
+ eq(0, exec_lua([[
+ local count = 0
+ for _ in pairs(WeakTable) do
+ count = count + 1
+ end
+
+ return count
+ ]]), "Should have no keys remaining")
+ end)
+
+ it('groups can be cleared', function()
+ local augroup = "TestGroup"
+ meths.create_augroup(augroup, { clear = true })
+ meths.create_autocmd("FileType", {
+ group = augroup,
+ command = "let g:executed = g:executed + 1"
+ })
+
+ set_ft("txt")
+ set_ft("txt")
+ eq(2, get_executed_count(), "should only count twice")
+
+ meths.create_augroup(augroup, { clear = true })
+ eq({}, meths.get_autocmds { group = augroup })
+
+ set_ft("txt")
+ set_ft("txt")
+ eq(2, get_executed_count(), "No additional counts")
+ end)
+
+ it('groups work with once', function()
+ local augroup = "TestGroup"
+
+ meths.create_augroup(augroup, { clear = true })
+ make_counting_autocmd { group = augroup, once = true }
+
+ set_ft("txt")
+ set_ft("python")
+
+ eq(get_executed_count(), 1)
+ end)
+
+ it('autocmds can be registered multiple times.', function()
+ local augroup = "TestGroup"
+
+ meths.create_augroup(augroup, { clear = true })
+ make_counting_autocmd { group = augroup, once = false }
+ make_counting_autocmd { group = augroup, once = false }
+ make_counting_autocmd { group = augroup, once = false }
+
+ set_ft("txt")
+ set_ft("python")
+
+ eq(get_executed_count(), 3 * 2)
+ end)
+
+ it('can be deleted', function()
+ local augroup = "WillBeDeleted"
+
+ meths.create_augroup(augroup, { clear = true })
+ meths.create_autocmd({"Filetype"}, {
+ pattern = "*",
+ command = "echo 'does not matter'",
+ })
+
+ -- Clears the augroup from before, which erases the autocmd
+ meths.create_augroup(augroup, { clear = true })
+
+ local result = #meths.get_autocmds { group = augroup }
+
+ eq(0, result)
+ end)
+
+ it('can be used for buffer local autocmds', function()
+ local augroup = "WillBeDeleted"
+
+ meths.set_var("value_set", false)
+
+ meths.create_augroup(augroup, { clear = true })
+ meths.create_autocmd("Filetype", {
+ pattern = "<buffer>",
+ command = "let g:value_set = v:true",
+ })
+
+ command "new"
+ command "set filetype=python"
+
+ eq(false, meths.get_var("value_set"))
+ end)
+
+ it('can accept vimscript functions', function()
+ source [[
+ let g:vimscript_executed = 0
+
+ function! MyVimscriptFunction() abort
+ let g:vimscript_executed = g:vimscript_executed + 1
+ endfunction
+
+ call nvim_create_autocmd("FileType", #{
+ \ pattern: ["python", "javascript"],
+ \ callback: "MyVimscriptFunction",
+ \ })
+
+ set filetype=txt
+ set filetype=python
+ set filetype=txt
+ set filetype=javascript
+ set filetype=txt
+ ]]
+
+ eq(2, meths.get_var("vimscript_executed"))
+ end)
+ end)
+
+ describe('augroup!', function()
+ it('legacy: should clear and not return any autocmds for delete groups', function()
+ command('augroup TEMP_A')
+ command(' autocmd! BufReadPost *.py :echo "Hello"')
+ command('augroup END')
+
+ command('augroup! TEMP_A')
+
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_A' }))
+
+ -- For some reason, augroup! doesn't clear the autocmds themselves, which is just wild
+ -- but we managed to keep this behavior.
+ eq(1, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('legacy: remove augroups that have no autocmds', function()
+ command('augroup TEMP_AB')
+ command('augroup END')
+
+ command('augroup! TEMP_AB')
+
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_AB' }))
+ eq(0, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('legacy: multiple remove and add augroup', function()
+ command('augroup TEMP_ABC')
+ command(' au!')
+ command(' autocmd BufReadPost *.py echo "Hello"')
+ command('augroup END')
+
+ command('augroup! TEMP_ABC')
+
+ -- Should still have one autocmd :'(
+ local aus = meths.get_autocmds { event = 'BufReadPost' }
+ eq(1, #aus, aus)
+
+ command('augroup TEMP_ABC')
+ command(' au!')
+ command(' autocmd BufReadPost *.py echo "Hello"')
+ command('augroup END')
+
+ -- Should now have two autocmds :'(
+ aus = meths.get_autocmds { event = 'BufReadPost' }
+ eq(2, #aus, aus)
+
+ command('augroup! TEMP_ABC')
+
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABC' }))
+ eq(2, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('api: should clear and not return any autocmds for delete groups by id', function()
+ command('augroup TEMP_ABCD')
+ command('autocmd! BufReadPost *.py :echo "Hello"')
+ command('augroup END')
+
+ local augroup_id = meths.create_augroup("TEMP_ABCD", { clear = false })
+ meths.del_augroup_by_id(augroup_id)
+
+ -- For good reason, we kill all the autocmds from del_augroup,
+ -- so now this works as expected
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABCD' }))
+ eq(0, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+
+ it('api: should clear and not return any autocmds for delete groups by name', function()
+ command('augroup TEMP_ABCDE')
+ command('autocmd! BufReadPost *.py :echo "Hello"')
+ command('augroup END')
+
+ meths.del_augroup_by_name("TEMP_ABCDE")
+
+ -- For good reason, we kill all the autocmds from del_augroup,
+ -- so now this works as expected
+ eq(false, pcall(meths.get_autocmds, { group = 'TEMP_ABCDE' }))
+ eq(0, #meths.get_autocmds { event = 'BufReadPost' })
+ end)
+ end)
+end)
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua
index 688f3abba5..dd3af8c28f 100644
--- a/test/functional/api/buffer_spec.lua
+++ b/test/functional/api/buffer_spec.lua
@@ -423,6 +423,13 @@ describe('api/buf', function()
-- will join multiple lines if needed
set_text(0, 6, 3, 4, {'bar'})
eq({'hello bar'}, get_lines(0, 1, true))
+
+ -- can use negative line numbers
+ set_text(-2, 0, -2, 5, {'goodbye'})
+ eq({'goodbye bar', ''}, get_lines(0, -1, true))
+
+ set_text(-1, 0, -1, 0, {'text'})
+ eq({'goodbye bar', 'text'}, get_lines(0, 2, true))
end)
it('works with undo', function()
@@ -537,6 +544,34 @@ describe('api/buf', function()
end)
end)
+ describe('nvim_buf_get_text', function()
+ local get_text = curbufmeths.get_text
+
+ it('works', function()
+ insert([[
+ hello foo!
+ text]])
+
+ eq({'hello'}, get_text(0, 0, 0, 5, {}))
+ eq({'hello foo!'}, get_text(0, 0, 0, 42, {}))
+ eq({'foo!'}, get_text(0, 6, 0, 10, {}))
+ eq({'foo!', 'tex'}, get_text(0, 6, 1, 3, {}))
+ eq({'foo!', 'tex'}, get_text(-2, 6, -1, 3, {}))
+ eq({''}, get_text(0, 18, 0, 20, {}))
+ eq({'ext'}, get_text(-1, 1, -1, 4, {}))
+ end)
+
+ it('errors on out-of-range', function()
+ eq(false, pcall(get_text, 2, 0, 3, 0, {}))
+ eq(false, pcall(get_text, 0, 0, 4, 0, {}))
+ end)
+
+ it('errors when start is greater than end', function()
+ eq(false, pcall(get_text, 1, 0, 0, 0, {}))
+ eq(false, pcall(get_text, 0, 1, 0, 0, {}))
+ end)
+ end)
+
describe('nvim_buf_get_offset', function()
local get_offset = curbufmeths.get_offset
it('works', function()
diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua
index c9c9be5406..e9ad756947 100644
--- a/test/functional/api/buffer_updates_spec.lua
+++ b/test/functional/api/buffer_updates_spec.lua
@@ -7,6 +7,7 @@ local nvim_prog = helpers.nvim_prog
local pcall_err = helpers.pcall_err
local sleep = helpers.sleep
local write_file = helpers.write_file
+local iswin = helpers.iswin
local origlines = {"original line 1",
"original line 2",
@@ -823,7 +824,8 @@ describe('API: buffer events:', function()
end
msg = next_msg()
end
- assert(false, 'did not match/receive expected nvim_buf_lines_event lines')
+ -- FIXME: Windows
+ assert(iswin(), 'did not match/receive expected nvim_buf_lines_event lines')
end
it('when :terminal lines change', function()
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index 6f929ad1ca..b80004f67c 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -6,12 +6,18 @@ local command = helpers.command
local curbufmeths = helpers.curbufmeths
local eq = helpers.eq
local meths = helpers.meths
+local bufmeths = helpers.bufmeths
+local matches = helpers.matches
local source = helpers.source
local pcall_err = helpers.pcall_err
+local exec_lua = helpers.exec_lua
+local assert_alive = helpers.assert_alive
+local feed = helpers.feed
+local funcs = helpers.funcs
describe('nvim_get_commands', function()
- local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, }
- local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, script_id=0, }
+ local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, keepscript=false, script_id=0, }
+ local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, keepscript=false, script_id=0, }
before_each(clear)
it('gets empty list if no commands were defined', function()
@@ -53,11 +59,11 @@ describe('nvim_get_commands', function()
end)
it('gets various command attributes', function()
- local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, script_id=0, }
- local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, script_id=1, }
- local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, }
- local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, }
- local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, script_id=4, }
+ local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, keepscript=false, script_id=0, }
+ local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, keepscript=false, script_id=1, }
+ local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, keepscript=false, script_id=2, }
+ local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, keepscript=false, script_id=3, }
+ local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, keepscript=false, script_id=4, }
source([[
command -complete=custom,ListUsers -nargs=+ Finger !finger <args>
]])
@@ -78,3 +84,186 @@ describe('nvim_get_commands', function()
eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
end)
end)
+
+describe('nvim_add_user_command', function()
+ before_each(clear)
+
+ it('works with strings', function()
+ meths.add_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1})
+ meths.command('SomeCommand 42')
+ eq(42, meths.eval('g:command_fired'))
+ end)
+
+ it('works with Lua functions', function()
+ exec_lua [[
+ result = {}
+ vim.api.nvim_add_user_command('CommandWithLuaCallback', function(opts)
+ result = opts
+ end, {
+ nargs = "*",
+ bang = true,
+ count = 2,
+ })
+ ]]
+
+ eq({
+ args = [[hello my\ friend how\ are\ you?]],
+ fargs = {[[hello]], [[my\ friend]], [[how\ are\ you?]]},
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [=[
+ vim.api.nvim_command([[CommandWithLuaCallback hello my\ friend how\ are\ you?]])
+ return result
+ ]=])
+
+ eq({
+ args = 'h\tey',
+ fargs = {[[h]], [[ey]]},
+ bang = true,
+ line1 = 10,
+ line2 = 10,
+ mods = "botright",
+ range = 1,
+ count = 10,
+ reg = "",
+ }, exec_lua [=[
+ vim.api.nvim_command('botright 10CommandWithLuaCallback! h\tey')
+ return result
+ ]=])
+
+ eq({
+ args = "h",
+ fargs = {"h"},
+ bang = false,
+ line1 = 1,
+ line2 = 42,
+ mods = "",
+ range = 1,
+ count = 42,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('CommandWithLuaCallback 42 h')
+ return result
+ ]])
+
+ eq({
+ args = "",
+ fargs = {""}, -- fargs works without args
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('CommandWithLuaCallback')
+ return result
+ ]])
+
+ -- f-args doesn't split when command nargs is 1 or "?"
+ exec_lua [[
+ result = {}
+ vim.api.nvim_add_user_command('CommandWithOneArg', function(opts)
+ result = opts
+ end, {
+ nargs = "?",
+ bang = true,
+ count = 2,
+ })
+ ]]
+
+ eq({
+ args = "hello I'm one argmuent",
+ fargs = {"hello I'm one argmuent"}, -- Doesn't split args
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ mods = "",
+ range = 0,
+ count = 2,
+ reg = "",
+ }, exec_lua [[
+ vim.api.nvim_command('CommandWithOneArg hello I\'m one argmuent')
+ return result
+ ]])
+
+ end)
+
+ it('can define buffer-local commands', function()
+ local bufnr = meths.create_buf(false, false)
+ bufmeths.add_user_command(bufnr, "Hello", "", {})
+ matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
+ meths.set_current_buf(bufnr)
+ meths.command("Hello")
+ assert_alive()
+ end)
+
+ it('can use a Lua complete function', function()
+ exec_lua [[
+ vim.api.nvim_add_user_command('Test', '', {
+ nargs = "*",
+ complete = function(arg, cmdline, pos)
+ local options = {"aaa", "bbb", "ccc"}
+ local t = {}
+ for _, v in ipairs(options) do
+ if string.find(v, "^" .. arg) then
+ table.insert(t, v)
+ end
+ end
+ return t
+ end,
+ })
+ ]]
+
+ feed(':Test a<Tab>')
+ eq('Test aaa', funcs.getcmdline())
+ feed('<C-U>Test b<Tab>')
+ eq('Test bbb', funcs.getcmdline())
+ end)
+
+ it('does not allow invalid command names', function()
+ matches("'name' must begin with an uppercase letter", pcall_err(exec_lua, [[
+ vim.api.nvim_add_user_command('test', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_add_user_command('t@', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_add_user_command('T@st', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_add_user_command('Test!', 'echo "hi"', {})
+ ]]))
+
+ matches('Invalid command name', pcall_err(exec_lua, [[
+ vim.api.nvim_add_user_command('💩', 'echo "hi"', {})
+ ]]))
+ end)
+end)
+
+describe('nvim_del_user_command', function()
+ before_each(clear)
+
+ it('can delete global commands', function()
+ meths.add_user_command('Hello', 'echo "Hi"', {})
+ meths.command('Hello')
+ meths.del_user_command('Hello')
+ matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
+ end)
+
+ it('can delete buffer-local commands', function()
+ bufmeths.add_user_command(0, 'Hello', 'echo "Hi"', {})
+ meths.command('Hello')
+ bufmeths.del_user_command(0, 'Hello')
+ matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
+ end)
+end)
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index a8f538b951..3f96efd4ef 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -110,6 +110,22 @@ describe('API/extmarks', function()
pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }))
end)
+ it("can end extranges past final newline when strict mode is false", function()
+ set_extmark(ns, marks[1], 0, 0, {
+ end_col = 1,
+ end_row = 1,
+ strict = false,
+ })
+ end)
+
+ it("can end extranges past final column when strict mode is false", function()
+ set_extmark(ns, marks[1], 0, 0, {
+ end_col = 6,
+ end_row = 0,
+ strict = false,
+ })
+ end)
+
it('adds, updates and deletes marks', function()
local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
eq(marks[1], rv)
@@ -1430,7 +1446,60 @@ describe('API/extmarks', function()
end_col = 0,
end_line = 1
})
- eq({ {1, 0, 0, { end_col = 0, end_row = 1 }} }, get_extmarks(ns, 0, -1, {details=true}))
+ eq({ {1, 0, 0, {
+ end_col = 0,
+ end_row = 1,
+ right_gravity = true,
+ end_right_gravity = false,
+ }} }, get_extmarks(ns, 0, -1, {details=true}))
+ end)
+
+ it('can get details', function()
+ set_extmark(ns, marks[1], 0, 0, {
+ end_col = 0,
+ end_row = 1,
+ right_gravity = false,
+ end_right_gravity = true,
+ priority = 0,
+ hl_eol = true,
+ hl_mode = "blend",
+ hl_group = "String",
+ virt_text = { { "text", "Statement" } },
+ virt_text_pos = "right_align",
+ virt_text_hide = true,
+ virt_lines = { { { "lines", "Statement" } }},
+ virt_lines_above = true,
+ virt_lines_leftcol = true,
+ })
+ set_extmark(ns, marks[2], 0, 0, {
+ priority = 0,
+ virt_text = { { "text", "Statement" } },
+ virt_text_win_col = 1,
+ })
+ eq({0, 0, {
+ end_col = 0,
+ end_row = 1,
+ right_gravity = false,
+ end_right_gravity = true,
+ priority = 0,
+ hl_eol = true,
+ hl_mode = "blend",
+ hl_group = "String",
+ virt_text = { { "text", "Statement" } },
+ virt_text_pos = "right_align",
+ virt_text_hide = true,
+ virt_lines = { { { "lines", "Statement" } }},
+ virt_lines_above = true,
+ virt_lines_leftcol = true,
+ } }, get_extmark_by_id(ns, marks[1], { details = true }))
+ eq({0, 0, {
+ right_gravity = true,
+ priority = 0,
+ virt_text = { { "text", "Statement" } },
+ virt_text_hide = false,
+ virt_text_pos = "win_col",
+ virt_text_win_col = 1,
+ } }, get_extmark_by_id(ns, marks[2], { details = true }))
end)
end)
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index 21e3094f8e..443689754c 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -3,6 +3,7 @@ local clear, nvim = helpers.clear, helpers.nvim
local Screen = require('test.functional.ui.screen')
local eq, eval = helpers.eq, helpers.eval
local command = helpers.command
+local exec_capture = helpers.exec_capture
local meths = helpers.meths
local funcs = helpers.funcs
local pcall_err = helpers.pcall_err
@@ -194,10 +195,12 @@ describe("API: set highlight", function()
reverse = true,
undercurl = true,
underline = true,
+ strikethrough = true,
cterm = {
italic = true,
reverse = true,
undercurl = true,
+ strikethrough = true,
}
}
local highlight3_result_gui = {
@@ -208,6 +211,7 @@ describe("API: set highlight", function()
reverse = true,
undercurl = true,
underline = true,
+ strikethrough = true,
}
local highlight3_result_cterm = {
background = highlight_color.ctermbg,
@@ -215,6 +219,7 @@ describe("API: set highlight", function()
italic = true,
reverse = true,
undercurl = true,
+ strikethrough = true,
}
local function get_ns()
@@ -237,6 +242,12 @@ describe("API: set highlight", function()
eq(highlight2_result, meths.get_hl_by_name('Test_hl', false))
end)
+ it ("can set emtpy cterm attr", function()
+ local ns = get_ns()
+ meths.set_hl(ns, 'Test_hl', { cterm = {} })
+ eq({}, meths.get_hl_by_name('Test_hl', false))
+ end)
+
it ("cterm attr defaults to gui attr", function()
local ns = get_ns()
meths.set_hl(ns, 'Test_hl', highlight1)
@@ -252,4 +263,69 @@ describe("API: set highlight", function()
eq(highlight3_result_gui, meths.get_hl_by_name('Test_hl', true))
eq(highlight3_result_cterm, meths.get_hl_by_name('Test_hl', false))
end)
+
+ it ("can set a highlight in the global namespace", function()
+ meths.set_hl(0, 'Test_hl', highlight2_config)
+ eq('Test_hl xxx cterm=underline,reverse ctermfg=8 ctermbg=15 gui=underline,reverse',
+ exec_capture('highlight Test_hl'))
+
+ meths.set_hl(0, 'Test_hl', { background = highlight_color.bg })
+ eq('Test_hl xxx guibg=#0032aa',
+ exec_capture('highlight Test_hl'))
+
+ meths.set_hl(0, 'Test_hl2', highlight3_config)
+ eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough ctermfg=8 ctermbg=15 gui=bold,underline,undercurl,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa',
+ exec_capture('highlight Test_hl2'))
+
+ -- Colors are stored exactly as they are defined.
+ meths.set_hl(0, 'Test_hl3', { bg = 'reD', fg = 'bLue'})
+ eq('Test_hl3 xxx guifg=bLue guibg=reD',
+ exec_capture('highlight Test_hl3'))
+ end)
+
+ it ("can modify a highlight in the global namespace", function()
+ meths.set_hl(0, 'Test_hl3', { bg = 'red', fg = 'blue'})
+ eq('Test_hl3 xxx guifg=blue guibg=red',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { bg = 'red' })
+ eq('Test_hl3 xxx guibg=red',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { ctermbg = 9, ctermfg = 12})
+ eq('Test_hl3 xxx ctermfg=12 ctermbg=9',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { ctermbg = 'red' , ctermfg = 'blue'})
+ eq('Test_hl3 xxx ctermfg=12 ctermbg=9',
+ exec_capture('highlight Test_hl3'))
+
+ meths.set_hl(0, 'Test_hl3', { ctermbg = 9 })
+ eq('Test_hl3 xxx ctermbg=9',
+ exec_capture('highlight Test_hl3'))
+
+ eq("'redd' is not a valid color",
+ pcall_err(meths.set_hl, 0, 'Test_hl3', {fg='redd'}))
+
+ eq("'bleu' is not a valid color",
+ pcall_err(meths.set_hl, 0, 'Test_hl3', {ctermfg='bleu'}))
+
+ meths.set_hl(0, 'Test_hl3', {fg='#FF00FF'})
+ eq('Test_hl3 xxx guifg=#FF00FF',
+ exec_capture('highlight Test_hl3'))
+
+ eq("'#FF00FF' is not a valid color",
+ pcall_err(meths.set_hl, 0, 'Test_hl3', {ctermfg='#FF00FF'}))
+
+ for _, fg_val in ipairs{ nil, 'NONE', 'nOnE', '', -1 } do
+ meths.set_hl(0, 'Test_hl3', {fg = fg_val})
+ eq('Test_hl3 xxx cleared',
+ exec_capture('highlight Test_hl3'))
+ end
+
+ meths.set_hl(0, 'Test_hl3', {fg='#FF00FF', blend=50})
+ eq('Test_hl3 xxx guifg=#FF00FF blend=50',
+ exec_capture('highlight Test_hl3'))
+
+ end)
end)
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index dd8eef7ca0..c0edcde476 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -5,6 +5,7 @@ local clear = helpers.clear
local command = helpers.command
local curbufmeths = helpers.curbufmeths
local eq, neq = helpers.eq, helpers.neq
+local exec_lua = helpers.exec_lua
local feed = helpers.feed
local funcs = helpers.funcs
local meths = helpers.meths
@@ -14,6 +15,9 @@ local pcall_err = helpers.pcall_err
local shallowcopy = helpers.shallowcopy
local sleep = helpers.sleep
+local sid_api_client = -9
+local sid_lua = -8
+
describe('nvim_get_keymap', function()
before_each(clear)
@@ -316,6 +320,55 @@ describe('nvim_get_keymap', function()
command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>')
eq({space_table}, meths.get_keymap('n'))
end)
+
+ it('can handle lua keymaps', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+ eq(1, exec_lua[[return GlobalCount]])
+
+ eq(2, exec_lua[[
+ vim.api.nvim_get_keymap('n')[1].callback()
+ return GlobalCount
+ ]])
+ local mapargs = meths.get_keymap('n')
+ assert.Truthy(type(mapargs[1].callback) == 'number', 'callback is not luaref number')
+ mapargs[1].callback = nil
+ eq({
+ lhs='asdf',
+ script=0,
+ silent=0,
+ expr=0,
+ sid=sid_lua,
+ buffer=0,
+ nowait=0,
+ mode='n',
+ noremap=0,
+ lnum=0,
+ }, mapargs[1])
+ end)
+
+ it ('can handle map descriptions', function()
+ meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"})
+ eq({
+ lhs='lhs',
+ rhs='rhs',
+ script=0,
+ silent=0,
+ expr=0,
+ sid=sid_api_client,
+ buffer=0,
+ nowait=0,
+ mode='n',
+ noremap=0,
+ lnum=0,
+ desc='map description'
+ }, meths.get_keymap('n')[1])
+ end)
end)
describe('nvim_set_keymap, nvim_del_keymap', function()
@@ -350,9 +403,10 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
to_return.silent = not opts.silent and 0 or 1
to_return.nowait = not opts.nowait and 0 or 1
to_return.expr = not opts.expr and 0 or 1
- to_return.sid = not opts.sid and 0 or opts.sid
+ to_return.sid = not opts.sid and sid_api_client or opts.sid
to_return.buffer = not opts.buffer and 0 or opts.buffer
to_return.lnum = not opts.lnum and 0 or opts.lnum
+ to_return.desc = opts.desc
return to_return
end
@@ -574,7 +628,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
it('interprets control sequences in expr-quotes correctly when called '
..'inside vim', function()
command([[call nvim_set_keymap('i', "\<space>", "\<tab>", {})]])
- eq(generate_mapargs('i', '<Space>', '\t', {}),
+ eq(generate_mapargs('i', '<Space>', '\t', {sid=0}),
get_mapargs('i', '<Space>'))
feed('i ')
eq({'\t'}, curbufmeths.get_lines(0, -1, 0))
@@ -717,6 +771,115 @@ describe('nvim_set_keymap, nvim_del_keymap', function()
end)
end
end
+
+ it('can make lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ end)
+
+ it (':map command shows lua keymap correctly', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
+ ]]
+ assert.truthy(string.match(exec_lua[[return vim.api.nvim_exec(':nmap asdf', true)]],
+ "^\nn asdf <Lua function %d+>"))
+ end)
+
+ it ('mapcheck() returns lua keymap correctly', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
+ ]]
+ assert.truthy(string.match(funcs.mapcheck('asdf', 'n'),
+ "^<Lua function %d+>"))
+ end)
+
+ it ('maparg() returns lua keymap correctly', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() print('jkl;') end })
+ ]]
+ assert.truthy(string.match(funcs.maparg('asdf', 'n'),
+ "^<Lua function %d+>"))
+ local mapargs = funcs.maparg('asdf', 'n', false, true)
+ assert.Truthy(type(mapargs.callback) == 'number', 'callback is not luaref number')
+ mapargs.callback = nil
+ eq(generate_mapargs('n', 'asdf', nil, {sid=sid_lua}), mapargs)
+ end)
+
+ it('can make lua expr mappings', function()
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true })
+ ]]
+
+ feed('aa')
+
+ eq(99, exec_lua[[return SomeValue]])
+ end)
+
+ it('does not reset pum in lua mapping', function()
+ eq(0, exec_lua [[
+ VisibleCount = 0
+ vim.api.nvim_set_keymap ('i', '<F2>', '', {callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end})
+ return VisibleCount
+ ]])
+ feed('i<C-X><C-V><F2><F2><esc>')
+ eq(2, exec_lua[[return VisibleCount]])
+ end)
+
+ it('can overwrite lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
+ ]]
+
+ feed('asdf\n')
+
+ eq(0, exec_lua[[return GlobalCount]])
+ end)
+
+ it('can unmap lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_set_keymap ('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_del_keymap('n', 'asdf' )
+ ]]
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
+ 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'))
+ eq("\nn lhs rhs\n map description",
+ helpers.exec_capture("nmap lhs"))
+ end)
end)
describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
@@ -814,4 +977,67 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function()
pcall_err(bufmeths.set_keymap, 100, '', 'lsh', 'irhs<Esc>', {})
helpers.assert_alive()
end)
+
+ it('can make lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ end)
+
+ it('can make lua expr mappings', function()
+ exec_lua [[
+ vim.api.nvim_buf_set_keymap (0, 'n', 'aa', '', {callback = function() return vim.api.nvim_replace_termcodes(':lua SomeValue = 99<cr>', true, false, true) end, expr = true })
+ ]]
+
+ feed('aa')
+
+ eq(99, exec_lua[[return SomeValue ]])
+ end)
+
+ it('can overwrite lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end })
+ ]]
+
+ feed('asdf\n')
+
+ eq(0, exec_lua[[return GlobalCount]])
+ end)
+
+ it('can unmap lua mappings', function()
+ eq(0, exec_lua [[
+ GlobalCount = 0
+ vim.api.nvim_buf_set_keymap (0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
+ return GlobalCount
+ ]])
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+
+ exec_lua [[
+ vim.api.nvim_buf_del_keymap(0, 'n', 'asdf' )
+ ]]
+
+ feed('asdf\n')
+
+ eq(1, exec_lua[[return GlobalCount]])
+ eq('\nNo mapping found', helpers.exec_capture('nmap asdf'))
+ end)
end)
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 309d9084c8..cdcf08c348 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -181,12 +181,6 @@ describe('server -> client', function()
end)
describe('recursive (child) nvim client', function()
- if helpers.isCI('travis') and helpers.is_os('mac') then
- -- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86.
- pending("[Hangs on Travis macOS. #5002]", function() end)
- return
- end
-
before_each(function()
command("let vim = rpcstart('"..nvim_prog.."', ['-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--embed', '--headless'])")
neq(0, eval('vim'))
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 22201e21a2..71cd055e08 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -23,6 +23,7 @@ local next_msg = helpers.next_msg
local tmpname = helpers.tmpname
local write_file = helpers.write_file
local exec_lua = helpers.exec_lua
+local exc_exec = helpers.exc_exec
local pcall_err = helpers.pcall_err
local format_string = helpers.format_string
@@ -333,6 +334,7 @@ describe('API', function()
describe('nvim_command_output', function()
it('does not induce hit-enter prompt', function()
+ nvim("ui_attach", 80, 20, {})
-- Induce a hit-enter prompt use nvim_input (non-blocking).
nvim('command', 'set cmdheight=1')
nvim('input', [[:echo "hi\nhi2"<CR>]])
@@ -535,6 +537,31 @@ describe('API', function()
end)
end)
+ describe('nvim_set_current_dir', function()
+ local start_dir
+
+ before_each(function()
+ clear()
+ funcs.mkdir("Xtestdir")
+ start_dir = funcs.getcwd()
+ end)
+
+ after_each(function()
+ helpers.rmdir("Xtestdir")
+ end)
+
+ it('works', function()
+ meths.set_current_dir("Xtestdir")
+ eq(funcs.getcwd(), start_dir .. helpers.get_pathsep() .. "Xtestdir")
+ end)
+
+ it('sets previous directory', function()
+ meths.set_current_dir("Xtestdir")
+ meths.exec('cd -', false)
+ eq(funcs.getcwd(), start_dir)
+ end)
+ end)
+
describe('nvim_exec_lua', function()
it('works', function()
meths.exec_lua('vim.api.nvim_set_var("test", 3)', {})
@@ -872,6 +899,19 @@ describe('API', function()
command('lockvar lua')
eq('Key is locked: lua', pcall_err(meths.del_var, 'lua'))
eq('Key is locked: lua', pcall_err(meths.set_var, 'lua', 1))
+
+ -- Check if autoload works properly
+ local pathsep = helpers.get_pathsep()
+ local xconfig = 'Xhome' .. pathsep .. 'Xconfig'
+ local xdata = 'Xhome' .. pathsep .. 'Xdata'
+ local autoload_folder = table.concat({xconfig, 'nvim', 'autoload'}, pathsep)
+ local autoload_file = table.concat({autoload_folder , 'testload.vim'}, pathsep)
+ mkdir_p(autoload_folder)
+ write_file(autoload_file , [[let testload#value = 2]])
+
+ clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } }
+ eq(2, meths.get_var('testload#value'))
+ rmdir('Xhome')
end)
it('nvim_get_vvar, nvim_set_vvar', function()
@@ -1093,7 +1133,20 @@ describe('API', function()
eq({mode='n', blocking=false}, nvim("get_mode"))
end)
+ it("during press-enter prompt without UI returns blocking=false", function()
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ command("echom 'msg1'")
+ command("echom 'msg2'")
+ command("echom 'msg3'")
+ command("echom 'msg4'")
+ command("echom 'msg5'")
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ nvim("input", ":messages<CR>")
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
it("during press-enter prompt returns blocking=true", function()
+ nvim("ui_attach", 80, 20, {})
eq({mode='n', blocking=false}, nvim("get_mode"))
command("echom 'msg1'")
command("echom 'msg2'")
@@ -1117,6 +1170,7 @@ describe('API', function()
-- TODO: bug #6247#issuecomment-286403810
it("batched with input", function()
+ nvim("ui_attach", 80, 20, {})
eq({mode='n', blocking=false}, nvim("get_mode"))
command("echom 'msg1'")
command("echom 'msg2'")
@@ -1367,18 +1421,18 @@ describe('API', function()
end)
describe('nvim_feedkeys', function()
- it('CSI escaping', function()
+ it('K_SPECIAL escaping', function()
local function on_setup()
-- notice the special char(…) \xe2\80\xa6
nvim('feedkeys', ':let x1="…"\n', '', true)
-- Both nvim_replace_termcodes and nvim_feedkeys escape \x80
local inp = helpers.nvim('replace_termcodes', ':let x2="…"<CR>', true, true, true)
- nvim('feedkeys', inp, '', true) -- escape_csi=true
+ nvim('feedkeys', inp, '', true) -- escape_ks=true
- -- nvim_feedkeys with CSI escaping disabled
+ -- nvim_feedkeys with K_SPECIAL escaping disabled
inp = helpers.nvim('replace_termcodes', ':let x3="…"<CR>', true, true, true)
- nvim('feedkeys', inp, '', false) -- escape_csi=false
+ nvim('feedkeys', inp, '', false) -- escape_ks=false
helpers.stop()
end
@@ -2187,6 +2241,14 @@ describe('API', function()
eq({}, meths.get_runtime_file("foobarlang/", true))
end)
+ it('can handle bad patterns', function()
+ if helpers.pending_win32(pending) then return end
+
+ eq("Vim:E220: Missing }.", pcall_err(meths.get_runtime_file, "{", false))
+
+ eq('Vim(echo):E5555: API call: Vim:E220: Missing }.',
+ exc_exec("echo nvim_get_runtime_file('{', v:false)"))
+ end)
end)
describe('nvim_get_all_options_info', function()
@@ -2551,6 +2613,34 @@ describe('API', function()
'Should be truncated%<',
{ maxwidth = 15 }))
end)
+ it('supports ASCII fillchar', function()
+ eq({ str = 'a~~~b', width = 5 },
+ meths.eval_statusline('a%=b', { fillchar = '~', maxwidth = 5 }))
+ end)
+ it('supports single-width multibyte fillchar', function()
+ eq({ str = 'a━━━b', width = 5 },
+ meths.eval_statusline('a%=b', { fillchar = '━', maxwidth = 5 }))
+ end)
+ it('rejects double-width fillchar', function()
+ eq('fillchar must be a single-width character',
+ pcall_err(meths.eval_statusline, '', { fillchar = '哦' }))
+ end)
+ it('rejects control character fillchar', function()
+ eq('fillchar must be a single-width character',
+ pcall_err(meths.eval_statusline, '', { fillchar = '\a' }))
+ end)
+ it('rejects multiple-character fillchar', function()
+ eq('fillchar must be a single-width character',
+ pcall_err(meths.eval_statusline, '', { fillchar = 'aa' }))
+ end)
+ it('rejects empty string fillchar', function()
+ eq('fillchar must be a single-width character',
+ pcall_err(meths.eval_statusline, '', { fillchar = '' }))
+ end)
+ it('rejects non-string fillchar', function()
+ eq('fillchar must be a single-width character',
+ pcall_err(meths.eval_statusline, '', { fillchar = 1 }))
+ end)
describe('highlight parsing', function()
it('works', function()
eq({
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 4d2ffa316a..c31ab2060a 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -1,4 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq,
ok, feed, insert, eval, tabpage = helpers.clear, helpers.nvim, helpers.curbuf,
helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq,
@@ -73,8 +74,7 @@ describe('API/win', function()
eq('typing\n some dumb text', curbuf_contents())
end)
- it('does not leak memory when using invalid window ID with invalid pos',
- function()
+ it('does not leak memory when using invalid window ID with invalid pos', function()
eq('Invalid window id: 1', pcall_err(meths.win_set_cursor, 1, {"b\na"}))
end)
@@ -147,6 +147,46 @@ describe('API/win', function()
eq({2, 5}, window('get_cursor', win))
end)
+ it('updates cursorline and statusline ruler in non-current window', function()
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
+ [2] = {background = Screen.colors.Grey90}, -- CursorLine
+ [3] = {bold = true, reverse = true}, -- StatusLine
+ [4] = {reverse = true}, -- VertSplit, StatusLineNC
+ })
+ screen:attach()
+ command('set ruler')
+ command('set cursorline')
+ insert([[
+ aaa
+ bbb
+ ccc
+ ddd]])
+ local oldwin = curwin()
+ command('vsplit')
+ screen:expect([[
+ aaa {4:│}aaa |
+ bbb {4:│}bbb |
+ ccc {4:│}ccc |
+ {2:dd^d }{4:│}{2:ddd }|
+ {1:~ }{4:│}{1:~ }|
+ {1:~ }{4:│}{1:~ }|
+ {3:[No Name] [+] 4,3 All }{4:[No Name] [+] 4,3 All}|
+ |
+ ]])
+ window('set_cursor', oldwin, {1, 0})
+ screen:expect([[
+ aaa {4:│}{2:aaa }|
+ bbb {4:│}bbb |
+ ccc {4:│}ccc |
+ {2:dd^d }{4:│}ddd |
+ {1:~ }{4:│}{1:~ }|
+ {1:~ }{4:│}{1:~ }|
+ {3:[No Name] [+] 4,3 All }{4:[No Name] [+] 1,1 All}|
+ |
+ ]])
+ end)
end)
describe('{get,set}_height', function()