diff options
Diffstat (limited to 'test/functional/api')
| -rw-r--r-- | test/functional/api/autocmd_spec.lua | 191 | ||||
| -rw-r--r-- | test/functional/api/buffer_spec.lua | 1276 | ||||
| -rw-r--r-- | test/functional/api/buffer_updates_spec.lua | 4 | ||||
| -rw-r--r-- | test/functional/api/command_spec.lua | 16 | ||||
| -rw-r--r-- | test/functional/api/extmark_spec.lua | 240 | ||||
| -rw-r--r-- | test/functional/api/highlight_spec.lua | 375 | ||||
| -rw-r--r-- | test/functional/api/keymap_spec.lua | 214 | ||||
| -rw-r--r-- | test/functional/api/proc_spec.lua | 12 | ||||
| -rw-r--r-- | test/functional/api/rpc_fixture.lua | 7 | ||||
| -rw-r--r-- | test/functional/api/server_notifications_spec.lua | 3 | ||||
| -rw-r--r-- | test/functional/api/server_requests_spec.lua | 19 | ||||
| -rw-r--r-- | test/functional/api/ui_spec.lua | 84 | ||||
| -rw-r--r-- | test/functional/api/version_spec.lua | 2 | ||||
| -rw-r--r-- | test/functional/api/vim_spec.lua | 876 | ||||
| -rw-r--r-- | test/functional/api/window_spec.lua | 354 |
15 files changed, 3206 insertions, 467 deletions
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index 22a1311ee9..fd46a1dcfa 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -14,14 +14,40 @@ before_each(clear) describe('autocmd api', function() describe('nvim_create_autocmd', function() - it('"command" and "callback" are mutually exclusive', function() - local rv = pcall_err(meths.create_autocmd, "BufReadPost", { - pattern = "*.py,*.pyi", + it('validation', function() + eq("Cannot use both 'callback' and 'command'", pcall_err(meths.create_autocmd, 'BufReadPost', { + pattern = '*.py,*.pyi', command = "echo 'Should Have Errored", - callback = "NotAllowed", - }) - - eq("specify either 'callback' or 'command', not both", rv) + callback = 'NotAllowed', + })) + eq("Cannot use both 'pattern' and 'buffer' for the same autocmd", pcall_err(meths.create_autocmd, 'FileType', { + command = 'let g:called = g:called + 1', + buffer = 0, + pattern = '*.py', + })) + eq("Required: 'event'", pcall_err(meths.create_autocmd, {}, { + command = 'ls', + })) + eq("Required: 'command' or 'callback'", pcall_err(meths.create_autocmd, 'FileType', { + })) + eq("Invalid 'desc': expected String, got Integer", pcall_err(meths.create_autocmd, 'FileType', { + command = 'ls', + desc = 42, + })) + eq("Invalid 'callback': expected Lua function or Vim function name, got Integer", pcall_err(meths.create_autocmd, 'FileType', { + callback = 0, + })) + eq("Invalid 'event' item: expected String, got Array", pcall_err(meths.create_autocmd, + {'FileType', {}}, {})) + eq("Invalid 'group': 0", pcall_err(meths.create_autocmd, 'FileType', { + group = 0, + command = 'ls', + })) + + eq("Invalid 'event': 'foo'", pcall_err(meths.create_autocmd, 'foo', { command = '' })) + eq("Invalid 'event': 'VimEnter '", pcall_err(meths.create_autocmd, 'VimEnter ', { command = '' })) + eq("Invalid 'event': 'VimEnter foo'", pcall_err(meths.create_autocmd, 'VimEnter foo', { command = '' })) + eq("Invalid 'event': 'BufAdd,BufDelete'", pcall_err(meths.create_autocmd, 'BufAdd,BufDelete', { command = '' })) end) it('doesnt leak when you use ++once', function() @@ -59,18 +85,8 @@ describe('autocmd api', function() eq(1, meths.get_var('called')) end) - it('does not allow passing buffer and patterns', function() - local rv = pcall_err(meths.create_autocmd, "Filetype", { - command = "let g:called = g:called + 1", - buffer = 0, - pattern = "*.py", - }) - - eq("cannot pass both: 'pattern' and 'buffer' for the same autocmd", rv) - end) - it('does not allow passing invalid buffers', function() - local ok, msg = pcall(meths.create_autocmd, "Filetype", { + local ok, msg = pcall(meths.create_autocmd, 'FileType', { command = "let g:called = g:called + 1", buffer = -1, }) @@ -209,7 +225,7 @@ describe('autocmd api', function() local aus = meths.get_autocmds({ event = 'User', pattern = 'Test' }) local first = aus[1] - eq(first.id, 1) + eq(true, first.id > 0) meths.set_var("some_condition", true) meths.exec_autocmds("User", {pattern = "Test"}) @@ -217,23 +233,28 @@ describe('autocmd api', function() end) it('receives an args table', function() - local res = exec_lua [[ - local group_id = vim.api.nvim_create_augroup("TestGroup", {}) - local autocmd_id = vim.api.nvim_create_autocmd("User", { + local group_id = meths.create_augroup("TestGroup", {}) + -- Having an existing autocmd calling expand("<afile>") shouldn't change args #18964 + meths.create_autocmd('User', { + group = 'TestGroup', + pattern = 'Te*', + command = 'call expand("<afile>")', + }) + + local autocmd_id = exec_lua [[ + return vim.api.nvim_create_autocmd("User", { group = "TestGroup", pattern = "Te*", callback = function(args) vim.g.autocmd_args = args end, }) - - return {group_id, autocmd_id} ]] meths.exec_autocmds("User", {pattern = "Test pattern"}) eq({ - id = res[2], - group = res[1], + id = autocmd_id, + group = group_id, event = "User", match = "Test pattern", file = "Test pattern", @@ -241,27 +262,24 @@ describe('autocmd api', function() }, meths.get_var("autocmd_args")) -- Test without a group - res = exec_lua [[ - local autocmd_id = vim.api.nvim_create_autocmd("User", { + autocmd_id = exec_lua [[ + return vim.api.nvim_create_autocmd("User", { pattern = "*", callback = function(args) vim.g.autocmd_args = args end, }) - - return {autocmd_id} ]] meths.exec_autocmds("User", {pattern = "some_pat"}) eq({ - id = res[1], + id = autocmd_id, group = nil, event = "User", match = "some_pat", file = "some_pat", buf = 1, }, meths.get_var("autocmd_args")) - end) it('can receive arbitrary data', function() @@ -294,8 +312,32 @@ describe('autocmd api', function() end) describe('nvim_get_autocmds', function() + it('validation', function() + eq("Invalid 'group': 9997999", pcall_err(meths.get_autocmds, { + group = 9997999, + })) + eq("Invalid 'group': 'bogus'", pcall_err(meths.get_autocmds, { + group = 'bogus', + })) + eq("Invalid 'group': 0", pcall_err(meths.get_autocmds, { + group = 0, + })) + eq("Invalid 'group': expected String or Integer, got Array", pcall_err(meths.get_autocmds, { + group = {}, + })) + eq("Invalid 'buffer': expected Integer or Array, got Boolean", pcall_err(meths.get_autocmds, { + buffer = true, + })) + eq("Invalid 'event': expected String or Array", pcall_err(meths.get_autocmds, { + event = true, + })) + eq("Invalid 'pattern': expected String or Array, got Boolean", pcall_err(meths.get_autocmds, { + pattern = true, + })) + end) + describe('events', function() - it('should return one autocmd when there is only one for an event', function() + it('returns one autocmd when there is only one for an event', function() command [[au! InsertEnter]] command [[au InsertEnter * :echo "1"]] @@ -303,7 +345,7 @@ describe('autocmd api', function() eq(1, #aus) end) - it('should return two autocmds when there are two for an event', function() + it('returns two autocmds when there are two for an event', function() command [[au! InsertEnter]] command [[au InsertEnter * :echo "1"]] command [[au InsertEnter * :echo "2"]] @@ -312,7 +354,7 @@ describe('autocmd api', function() eq(2, #aus) end) - it('should return the same thing if you use string or list', function() + it('returns the same thing if you use string or list', function() command [[au! InsertEnter]] command [[au InsertEnter * :echo "1"]] command [[au InsertEnter * :echo "2"]] @@ -322,7 +364,7 @@ describe('autocmd api', function() eq(string_aus, array_aus) end) - it('should return two autocmds when there are two for an event', function() + it('returns two autocmds when there are two for an event', function() command [[au! InsertEnter]] command [[au! InsertLeave]] command [[au InsertEnter * :echo "1"]] @@ -332,7 +374,7 @@ describe('autocmd api', function() eq(2, #aus) end) - it('should return different IDs for different autocmds', function() + it('returns different IDs for different autocmds', function() command [[au! InsertEnter]] command [[au! InsertLeave]] command [[au InsertEnter * :echo "1"]] @@ -356,7 +398,7 @@ describe('autocmd api', function() eq(first, new_aus[1]) end) - it('should return event name', function() + it('returns event name', function() command [[au! InsertEnter]] command [[au InsertEnter * :echo "1"]] @@ -364,7 +406,7 @@ describe('autocmd api', function() eq({ { buflocal = false, command = ':echo "1"', event = "InsertEnter", once = false, pattern = "*" } }, aus) end) - it('should work with buffer numbers', function() + it('works with buffer numbers', function() command [[new]] command [[au! InsertEnter]] command [[au InsertEnter <buffer=1> :echo "1"]] @@ -407,8 +449,8 @@ describe('autocmd api', function() pattern = "<buffer=2>", }}, aus) - eq("Invalid value for 'buffer': must be an integer or array of integers", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" })) - eq("Invalid value for 'buffer': must be an integer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } })) + eq("Invalid 'buffer': expected Integer or Array, got String", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" })) + eq("Invalid 'buffer': expected Integer, got String", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } })) eq("Invalid buffer id: 42", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { 42 } })) local bufs = {} @@ -416,10 +458,10 @@ describe('autocmd api', function() table.insert(bufs, meths.create_buf(true, false)) end - eq("Too many buffers. Please limit yourself to 256 or fewer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs })) + eq("Too many buffers (maximum of 256)", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs })) end) - it('should return autocmds when group is specified by id', function() + it('returns autocmds when group is specified by id', function() local auid = meths.create_augroup("nvim_test_augroup", { clear = true }) meths.create_autocmd("FileType", { group = auid, command = 'echo "1"' }) meths.create_autocmd("FileType", { group = auid, command = 'echo "2"' }) @@ -431,7 +473,7 @@ describe('autocmd api', function() eq(0, #aus2) end) - it('should return autocmds when group is specified by name', function() + it('returns autocmds when group is specified by name', function() local auname = "nvim_test_augroup" meths.create_augroup(auname, { clear = true }) meths.create_autocmd("FileType", { group = auname, command = 'echo "1"' }) @@ -531,7 +573,7 @@ describe('autocmd api', function() command [[augroup END]] end) - it('should return all groups if no group is specified', function() + it('returns all groups if no group is specified', function() local aus = meths.get_autocmds { event = "InsertEnter" } if #aus ~= 4 then eq({}, aus) @@ -540,7 +582,7 @@ describe('autocmd api', function() eq(4, #aus) end) - it('should return only the group specified', function() + it('returns only the group specified', function() local aus = meths.get_autocmds { event = "InsertEnter", group = "GroupOne", @@ -551,7 +593,7 @@ describe('autocmd api', function() eq("GroupOne", aus[1].group_name) end) - it('should return only the group specified, multiple values', function() + it('returns only the group specified, multiple values', function() local aus = meths.get_autocmds { event = "InsertEnter", group = "GroupTwo", @@ -578,7 +620,7 @@ describe('autocmd api', function() ]], {})) eq(false, success) - matches('invalid augroup: NotDefined', code) + matches("Invalid 'group': 'NotDefined'", code) end) it('raises error for undefined augroup id', function() @@ -596,7 +638,7 @@ describe('autocmd api', function() ]], {})) eq(false, success) - matches('invalid augroup: 1', code) + matches("Invalid 'group': 1", code) end) it('raises error for invalid group type', function() @@ -611,7 +653,7 @@ describe('autocmd api', function() ]], {})) eq(false, success) - matches("'group' must be a string or an integer", code) + matches("Invalid 'group': expected String or Integer, got Boolean", code) end) it('raises error for invalid pattern array', function() @@ -625,7 +667,7 @@ describe('autocmd api', function() ]], {})) eq(false, success) - matches("All entries in 'pattern' must be strings", code) + matches("Invalid 'pattern' item: expected String, got Array", code) end) end) @@ -640,7 +682,7 @@ describe('autocmd api', function() command [[au InsertEnter <buffer> :echo "Buffer"]] end) - it('should should return for literal match', function() + it('returns for literal match', function() local aus = meths.get_autocmds { event = "InsertEnter", pattern = "*" @@ -650,7 +692,7 @@ describe('autocmd api', function() eq([[:echo "No Group"]], aus[1].command) end) - it('should return for multiple matches', function() + it('returns for multiple matches', function() -- vim.api.nvim_get_autocmds local aus = meths.get_autocmds { event = "InsertEnter", @@ -687,6 +729,26 @@ describe('autocmd api', function() end) describe('nvim_exec_autocmds', function() + it('validation', function() + eq("Invalid 'group': 9997999", pcall_err(meths.exec_autocmds, 'FileType', { + group = 9997999, + })) + eq("Invalid 'group': 'bogus'", pcall_err(meths.exec_autocmds, 'FileType', { + group = 'bogus', + })) + eq("Invalid 'group': expected String or Integer, got Array", pcall_err(meths.exec_autocmds, 'FileType', { + group = {}, + })) + eq("Invalid 'group': 0", pcall_err(meths.exec_autocmds, 'FileType', { + group = 0, + })) + eq("Invalid 'buffer': expected Buffer, got Array", pcall_err(meths.exec_autocmds, 'FileType', { + buffer = {}, + })) + eq("Invalid 'event' item: expected String, got Array", pcall_err(meths.exec_autocmds, + {'FileType', {}}, {})) + end) + it("can trigger builtin autocmds", function() meths.set_var("autocmd_executed", false) @@ -1004,6 +1066,12 @@ describe('autocmd api', function() eq(false, exec_lua[[return pcall(vim.api.nvim_del_augroup_by_id, -12342)]]) eq('Vim:E367: No such group: "--Deleted--"', pcall_err(meths.del_augroup_by_id, -12312)) + + eq(false, exec_lua[[return pcall(vim.api.nvim_del_augroup_by_id, 0)]]) + eq('Vim:E367: No such group: "[NULL]"', pcall_err(meths.del_augroup_by_id, 0)) + + eq(false, exec_lua[[return pcall(vim.api.nvim_del_augroup_by_id, 12342)]]) + eq('Vim:E367: No such group: "[NULL]"', pcall_err(meths.del_augroup_by_id, 12312)) end) it('groups work with once', function() @@ -1036,7 +1104,7 @@ describe('autocmd api', function() local augroup = "WillBeDeleted" meths.create_augroup(augroup, { clear = true }) - meths.create_autocmd({"Filetype"}, { + meths.create_autocmd({"FileType"}, { pattern = "*", command = "echo 'does not matter'", }) @@ -1055,7 +1123,7 @@ describe('autocmd api', function() meths.set_var("value_set", false) meths.create_augroup(augroup, { clear = true }) - meths.create_autocmd("Filetype", { + meths.create_autocmd("FileType", { pattern = "<buffer>", command = "let g:value_set = v:true", }) @@ -1171,6 +1239,17 @@ describe('autocmd api', function() end) describe('nvim_clear_autocmds', function() + it('validation', function() + eq("Cannot use both 'pattern' and 'buffer'", pcall_err(meths.clear_autocmds, { + pattern = '*', + buffer = 42, + })) + eq("Invalid 'event' item: expected String, got Array", pcall_err(meths.clear_autocmds, { + event = {'FileType', {}} + })) + eq("Invalid 'group': 0", pcall_err(meths.clear_autocmds, {group = 0})) + end) + it('should clear based on event + pattern', function() command('autocmd InsertEnter *.py :echo "Python can be cool sometimes"') command('autocmd InsertEnter *.txt :echo "Text Files Are Cool"') @@ -1254,7 +1333,7 @@ describe('autocmd api', function() local without_group = meths.get_autocmds(search) eq(2, #without_group) - -- Doest clear with passing group. + -- Doesn't clear with passing group. meths.clear_autocmds { buffer = 0, group = search.group } local with_group = meths.get_autocmds(search) eq(1, #with_group) diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 6b13729994..6ed9aa574a 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -16,6 +16,7 @@ local command = helpers.command local bufmeths = helpers.bufmeths local feed = helpers.feed local pcall_err = helpers.pcall_err +local assert_alive = helpers.assert_alive describe('api/buf', function() before_each(clear) @@ -41,6 +42,14 @@ describe('api/buf', function() eq(1, curbuf_depr('line_count')) end) + it("doesn't crash just after set undolevels=1 #24894", function() + local buf = meths.create_buf(false, true) + meths.buf_set_option(buf, 'undolevels', -1) + meths.buf_set_lines(buf, 0, 1, false, { }) + + assert_alive() + end) + it('cursor position is maintained after lines are inserted #9961', function() -- replace the buffer contents with these three lines. request('nvim_buf_set_lines', 0, 0, -1, 1, {"line1", "line2", "line3", "line4"}) @@ -76,6 +85,38 @@ describe('api/buf', function() eq({4, 2}, curwin('get_cursor')) end) + it('cursor position is maintained in non-current window', function() + meths.buf_set_lines(0, 0, -1, 1, {"line1", "line2", "line3", "line4"}) + meths.win_set_cursor(0, {3, 2}) + local win = meths.get_current_win() + local buf = meths.get_current_buf() + + command('new') + + meths.buf_set_lines(buf, 1, 2, 1, {"line5", "line6"}) + eq({"line1", "line5", "line6", "line3", "line4"}, meths.buf_get_lines(buf, 0, -1, true)) + eq({4, 2}, meths.win_get_cursor(win)) + end) + + it('cursor position is maintained in TWO non-current windows', function() + meths.buf_set_lines(0, 0, -1, 1, {"line1", "line2", "line3", "line4"}) + meths.win_set_cursor(0, {3, 2}) + local win = meths.get_current_win() + local buf = meths.get_current_buf() + + command('split') + meths.win_set_cursor(0, {4, 2}) + local win2 = meths.get_current_win() + + -- set current window to third one with another buffer + command("new") + + meths.buf_set_lines(buf, 1, 2, 1, {"line5", "line6"}) + eq({"line1", "line5", "line6", "line3", "line4"}, meths.buf_get_lines(buf, 0, -1, true)) + eq({4, 2}, meths.win_get_cursor(win)) + eq({5, 2}, meths.win_get_cursor(win2)) + end) + it('line_count has defined behaviour for unloaded buffers', function() -- we'll need to know our bufnr for when it gets unloaded local bufnr = curbuf('get_number') @@ -105,6 +146,285 @@ describe('api/buf', function() -- it's impossible to get out-of-bounds errors for an unloaded buffer eq({}, buffer('get_lines', bufnr, 8888, 9999, 1)) end) + + describe('handles topline', function() + local screen + before_each(function() + screen = Screen.new(20, 12) + screen:set_default_attr_ids { + [1] = {bold = true, foreground = Screen.colors.Blue1}; + [2] = {reverse = true, bold = true}; + [3] = {reverse = true}; + } + screen:attach() + meths.buf_set_lines(0, 0, -1, 1, {"aaa", "bbb", "ccc", "ddd", "www", "xxx", "yyy", "zzz"}) + meths.set_option_value('modified', false, {}) + end) + + it('of current window', function() + local win = meths.get_current_win() + local buf = meths.get_current_buf() + + command('new | wincmd w') + meths.win_set_cursor(win, {8,0}) + + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + www | + xxx | + yyy | + ^zzz | + {2:[No Name] }| + | + ]]} + + meths.buf_set_lines(buf, 0, 2, true, {"aaabbb"}) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + www | + xxx | + yyy | + ^zzz | + {2:[No Name] [+] }| + | + ]]} + + -- replacing topline keeps it the topline + meths.buf_set_lines(buf, 3, 4, true, {"wwweeee"}) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + wwweeee | + xxx | + yyy | + ^zzz | + {2:[No Name] [+] }| + | + ]]} + + -- inserting just before topline does not scroll up if cursor would be moved + meths.buf_set_lines(buf, 3, 3, true, {"mmm"}) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + wwweeee | + xxx | + yyy | + ^zzz | + {2:[No Name] [+] }| + | + ]], unchanged=true} + + meths.win_set_cursor(0, {7, 0}) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + wwweeee | + xxx | + ^yyy | + zzz | + {2:[No Name] [+] }| + | + ]]} + + meths.buf_set_lines(buf, 4, 4, true, {"mmmeeeee"}) + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + mmmeeeee | + wwweeee | + xxx | + ^yyy | + {2:[No Name] [+] }| + | + ]]} + end) + + it('of non-current window', function() + local win = meths.get_current_win() + local buf = meths.get_current_buf() + + command('new') + meths.win_set_cursor(win, {8,0}) + + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] }| + www | + xxx | + yyy | + zzz | + {3:[No Name] }| + | + ]]} + + meths.buf_set_lines(buf, 0, 2, true, {"aaabbb"}) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] }| + www | + xxx | + yyy | + zzz | + {3:[No Name] [+] }| + | + ]]} + + -- replacing topline keeps it the topline + meths.buf_set_lines(buf, 3, 4, true, {"wwweeee"}) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] }| + wwweeee | + xxx | + yyy | + zzz | + {3:[No Name] [+] }| + | + ]]} + + -- inserting just before topline scrolls up + meths.buf_set_lines(buf, 3, 3, true, {"mmm"}) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] }| + mmm | + wwweeee | + xxx | + yyy | + {3:[No Name] [+] }| + | + ]]} + end) + + it('of split windows with same buffer', function() + local win = meths.get_current_win() + local buf = meths.get_current_buf() + + command('split') + meths.win_set_cursor(win, {8,0}) + meths.win_set_cursor(0, {1,0}) + + screen:expect{grid=[[ + ^aaa | + bbb | + ccc | + ddd | + www | + {2:[No Name] }| + www | + xxx | + yyy | + zzz | + {3:[No Name] }| + | + ]]} + meths.buf_set_lines(buf, 0, 2, true, {"aaabbb"}) + + screen:expect{grid=[[ + ^aaabbb | + ccc | + ddd | + www | + xxx | + {2:[No Name] [+] }| + www | + xxx | + yyy | + zzz | + {3:[No Name] [+] }| + | + ]]} + + -- replacing topline keeps it the topline + meths.buf_set_lines(buf, 3, 4, true, {"wwweeee"}) + screen:expect{grid=[[ + ^aaabbb | + ccc | + ddd | + wwweeee | + xxx | + {2:[No Name] [+] }| + wwweeee | + xxx | + yyy | + zzz | + {3:[No Name] [+] }| + | + ]]} + + -- inserting just before topline scrolls up + meths.buf_set_lines(buf, 3, 3, true, {"mmm"}) + screen:expect{grid=[[ + ^aaabbb | + ccc | + ddd | + mmm | + wwweeee | + {2:[No Name] [+] }| + mmm | + wwweeee | + xxx | + yyy | + {3:[No Name] [+] }| + | + ]]} + end) + end) + + it('handles clearing out non-current buffer #24911', function() + local buf = meths.get_current_buf() + meths.buf_set_lines(buf, 0, -1, true, {"aaa", "bbb", "ccc"}) + command("new") + + meths.buf_set_lines(0, 0, -1, true, {"xxx", "yyy", "zzz"}) + + meths.buf_set_lines(buf, 0, -1, true, {}) + eq({"xxx", "yyy", "zzz"}, meths.buf_get_lines(0, 0, -1, true)) + eq({''}, meths.buf_get_lines(buf, 0, -1, true)) + end) end) describe('deprecated: {get,set,del}_line', function() @@ -193,7 +513,7 @@ describe('api/buf', function() it('fails correctly when input is not valid', function() eq(1, api.curbufmeths.get_number()) - eq([[String cannot contain newlines]], + eq([['replacement string' item contains newlines]], pcall_err(bufmeths.set_lines, 1, 1, 2, false, {'b\na'})) end) @@ -393,6 +713,21 @@ describe('api/buf', function() feed('<c-w>p') eq(3, funcs.winnr()) end) + + it('set_lines on unloaded buffer #8659 #22670', function() + local bufnr = curbuf('get_number') + meths.buf_set_lines(bufnr, 0, -1, false, {'a', 'b', 'c'}) + meths.buf_set_name(bufnr, 'set_lines') + finally(function() + os.remove('set_lines') + end) + command('write!') + command('new') + command('bunload! '..bufnr) + local new_bufnr = funcs.bufnr('set_lines', true) + meths.buf_set_lines(new_bufnr, 0, -1, false, {}) + eq({''}, meths.buf_get_lines(new_bufnr, 0, -1, false)) + end) end) describe('nvim_buf_set_text', function() @@ -437,6 +772,10 @@ describe('api/buf', function() -- can append to a line set_text(1, 4, -1, 4, {' and', 'more'}) eq({'goodbye bar', 'text and', 'more'}, get_lines(0, 3, true)) + + -- can use negative column numbers + set_text(0, -5, 0, -1, {'!'}) + eq({'goodbye!'}, get_lines(0, 1, true)) end) it('works with undo', function() @@ -480,6 +819,754 @@ describe('api/buf', function() eq({1, 9}, curwin('get_cursor')) end) + it('updates the cursor position in non-current window', function() + insert([[ + hello world!]]) + + -- position the cursor on `!` + meths.win_set_cursor(0, {1, 11}) + + local win = meths.get_current_win() + local buf = meths.get_current_buf() + + command("new") + + -- replace 'world' with 'foo' + meths.buf_set_text(buf, 0, 6, 0, 11, {'foo'}) + eq({'hello foo!'}, meths.buf_get_lines(buf, 0, -1, true)) + -- cursor should be moved left by two columns (replacement is shorter by 2 chars) + eq({1, 9}, meths.win_get_cursor(win)) + end) + + it('updates the cursor position in TWO non-current windows', function() + insert([[ + hello world!]]) + + -- position the cursor on `!` + meths.win_set_cursor(0, {1, 11}) + local win = meths.get_current_win() + local buf = meths.get_current_buf() + + command("split") + local win2 = meths.get_current_win() + -- position the cursor on `w` + meths.win_set_cursor(0, {1, 6}) + + command("new") + + -- replace 'hello' with 'foo' + meths.buf_set_text(buf, 0, 0, 0, 5, {'foo'}) + eq({'foo world!'}, meths.buf_get_lines(buf, 0, -1, true)) + + -- both cursors should be moved left by two columns (replacement is shorter by 2 chars) + eq({1, 9}, meths.win_get_cursor(win)) + eq({1, 4}, meths.win_get_cursor(win2)) + end) + + describe('when text is being added right at cursor position #22526', function() + it('updates the cursor position in NORMAL mode', function() + insert([[ + abcd]]) + + -- position the cursor on 'c' + curwin('set_cursor', {1, 2}) + -- add 'xxx' before 'c' + set_text(0, 2, 0, 2, {'xxx'}) + eq({'abxxxcd'}, get_lines(0, -1, true)) + -- cursor should be on 'c' + eq({1, 5}, curwin('get_cursor')) + end) + + it('updates the cursor position only in non-current window when in INSERT mode', function() + insert([[ + abcd]]) + + -- position the cursor on 'c' + curwin('set_cursor', {1, 2}) + -- open vertical split + feed('<c-w>v') + -- get into INSERT mode to treat cursor + -- as being after 'b', not on 'c' + feed('i') + -- add 'xxx' between 'b' and 'c' + set_text(0, 2, 0, 2, {'xxx'}) + eq({'abxxxcd'}, get_lines(0, -1, true)) + -- in the current window cursor should stay after 'b' + eq({1, 2}, curwin('get_cursor')) + -- quit INSERT mode + feed('<esc>') + -- close current window + feed('<c-w>c') + -- in another window cursor should be on 'c' + eq({1, 5}, curwin('get_cursor')) |