From ff3d04b75b4a9314815c37d53ebc4d035a043335 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Feb 2023 08:07:38 -0500 Subject: refactor(api): VALIDATE macros #22256 - VALIDATE() takes a format string - deduplicate check_string_array - VALIDATE_RANGE - validate UI args --- test/functional/api/buffer_spec.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 6b13729994..d454765edb 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -193,7 +193,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) @@ -553,20 +553,20 @@ describe('api/buf', function() insert([[ hello foo! text]]) - eq('start_row out of bounds', pcall_err(set_text, 2, 0, 3, 0, {})) - eq('start_row out of bounds', pcall_err(set_text, -3, 0, 0, 0, {})) - eq('end_row out of bounds', pcall_err(set_text, 0, 0, 2, 0, {})) - eq('end_row out of bounds', pcall_err(set_text, 0, 0, -3, 0, {})) - eq('start_col out of bounds', pcall_err(set_text, 1, 5, 1, 5, {})) - eq('end_col out of bounds', pcall_err(set_text, 1, 0, 1, 5, {})) + eq("Invalid 'start_row': out of range", pcall_err(set_text, 2, 0, 3, 0, {})) + eq("Invalid 'start_row': out of range", pcall_err(set_text, -3, 0, 0, 0, {})) + eq("Invalid 'end_row': out of range", pcall_err(set_text, 0, 0, 2, 0, {})) + eq("Invalid 'end_row': out of range", pcall_err(set_text, 0, 0, -3, 0, {})) + eq("Invalid 'start_col': out of range", pcall_err(set_text, 1, 5, 1, 5, {})) + eq("Invalid 'end_col': out of range", pcall_err(set_text, 1, 0, 1, 5, {})) end) it('errors when start is greater than end', function() insert([[ hello foo! text]]) - eq('start is higher than end', pcall_err(set_text, 1, 0, 0, 0, {})) - eq('start is higher than end', pcall_err(set_text, 0, 1, 0, 0, {})) + eq("'start' is higher than 'end'", pcall_err(set_text, 1, 0, 0, 0, {})) + eq("'start' is higher than 'end'", pcall_err(set_text, 0, 1, 0, 0, {})) end) it('no heap-use-after-free when called consecutively #19643', function() @@ -611,7 +611,7 @@ describe('api/buf', function() end) it('errors when start is greater than end', function() - eq('start is higher than end', pcall_err(get_text, 1, 0, 0, 0, {})) + eq("'start' is higher than 'end'", pcall_err(get_text, 1, 0, 0, 0, {})) eq('start_col must be less than end_col', pcall_err(get_text, 0, 1, 0, 0, {})) end) end) -- cgit From 1fe1bb084d0099fc4f9bfdc11189485d0f74b75a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 19 Dec 2022 16:37:45 +0000 Subject: refactor(options): deprecate nvim[_buf|_win]_[gs]et_option Co-authored-by: zeertzjq Co-authored-by: famiu --- test/functional/api/buffer_spec.lua | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index d454765edb..df9092fa14 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -630,19 +630,19 @@ describe('api/buf', function() eq('Index out of bounds', pcall_err(get_offset, 6)) eq('Index out of bounds', pcall_err(get_offset, -1)) - curbufmeths.set_option('eol', false) - curbufmeths.set_option('fixeol', false) + meths.set_option_value('eol', false, {buf=0}) + meths.set_option_value('fixeol', false, {buf=0}) eq(28, get_offset(5)) -- fileformat is ignored - curbufmeths.set_option('fileformat', 'dos') + meths.set_option_value('fileformat', 'dos', {buf=0}) eq(0, get_offset(0)) eq(6, get_offset(1)) eq(15, get_offset(2)) eq(16, get_offset(3)) eq(24, get_offset(4)) eq(28, get_offset(5)) - curbufmeths.set_option('eol', true) + meths.set_option_value('eol', true, {buf=0}) eq(29, get_offset(5)) command("set hidden") @@ -697,23 +697,23 @@ describe('api/buf', function() end) end) - describe('nvim_buf_get_option, nvim_buf_set_option', function() + describe('nvim_get_option_value, nvim_set_option_value', function() it('works', function() - eq(8, curbuf('get_option', 'shiftwidth')) - curbuf('set_option', 'shiftwidth', 4) - eq(4, curbuf('get_option', 'shiftwidth')) + eq(8, nvim('get_option_value', 'shiftwidth', {buf = 0})) + nvim('set_option_value', 'shiftwidth', 4, {buf=0}) + eq(4, nvim('get_option_value', 'shiftwidth', {buf = 0})) -- global-local option - curbuf('set_option', 'define', 'test') - eq('test', curbuf('get_option', 'define')) + nvim('set_option_value', 'define', 'test', {buf = 0}) + eq('test', nvim('get_option_value', 'define', {buf = 0})) -- Doesn't change the global value - eq([[^\s*#\s*define]], nvim('get_option', 'define')) + eq([[^\s*#\s*define]], nvim('get_option_value', 'define', {scope='global'})) end) it('returns values for unset local options', function() -- 'undolevels' is only set to its "unset" value when a new buffer is -- created command('enew') - eq(-123456, curbuf('get_option', 'undolevels')) + eq(-123456, nvim('get_option_value', 'undolevels', {buf=0})) end) end) -- cgit From 576dddb46168e81aa0f78c28816082c662dedea1 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Mon, 22 May 2023 12:47:10 +0600 Subject: test: don't unnecessarily specify win/buf for `nvim_(get|set)_option_value` `nvim_(get|set)_option_value` pick the current buffer / window by default for buffer-local/window-local (but not global-local) options. So specifying `buf = 0` or `win = 0` in opts is unnecessary for those options. This PR removes those to reduce code clutter. --- test/functional/api/buffer_spec.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index df9092fa14..bf9e952319 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -630,19 +630,19 @@ describe('api/buf', function() eq('Index out of bounds', pcall_err(get_offset, 6)) eq('Index out of bounds', pcall_err(get_offset, -1)) - meths.set_option_value('eol', false, {buf=0}) - meths.set_option_value('fixeol', false, {buf=0}) + meths.set_option_value('eol', false, {}) + meths.set_option_value('fixeol', false, {}) eq(28, get_offset(5)) -- fileformat is ignored - meths.set_option_value('fileformat', 'dos', {buf=0}) + meths.set_option_value('fileformat', 'dos', {}) eq(0, get_offset(0)) eq(6, get_offset(1)) eq(15, get_offset(2)) eq(16, get_offset(3)) eq(24, get_offset(4)) eq(28, get_offset(5)) - meths.set_option_value('eol', true, {buf=0}) + meths.set_option_value('eol', true, {}) eq(29, get_offset(5)) command("set hidden") @@ -699,9 +699,9 @@ describe('api/buf', function() describe('nvim_get_option_value, nvim_set_option_value', function() it('works', function() - eq(8, nvim('get_option_value', 'shiftwidth', {buf = 0})) - nvim('set_option_value', 'shiftwidth', 4, {buf=0}) - eq(4, nvim('get_option_value', 'shiftwidth', {buf = 0})) + eq(8, nvim('get_option_value', 'shiftwidth', {})) + nvim('set_option_value', 'shiftwidth', 4, {}) + eq(4, nvim('get_option_value', 'shiftwidth', {})) -- global-local option nvim('set_option_value', 'define', 'test', {buf = 0}) eq('test', nvim('get_option_value', 'define', {buf = 0})) -- cgit From fcfe535e9877cfdc824dc78f2d19e590400d34cd Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:26:29 -0500 Subject: refactor(defaults): do not use C specific default values for options (#22500) The options 'path', 'include', and 'define' all use C-specific default values. This may have made sense a long time ago when Vim was mostly used just for writing C, but this is no longer the case, and we have ample support for filetype specific configuration. Make the default values of these options empty and move the C-specific values into a filetype plugin where they belong. Co-authored-by: zeertzjq --- test/functional/api/buffer_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index bf9e952319..d9d4539fe8 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -706,7 +706,7 @@ describe('api/buf', function() nvim('set_option_value', 'define', 'test', {buf = 0}) eq('test', nvim('get_option_value', 'define', {buf = 0})) -- Doesn't change the global value - eq([[^\s*#\s*define]], nvim('get_option_value', 'define', {scope='global'})) + eq("", nvim('get_option_value', 'define', {scope='global'})) end) it('returns values for unset local options', function() -- cgit From 3ecd45ded044c47efa76b74e9e3b720fbe27adc7 Mon Sep 17 00:00:00 2001 From: notomo Date: Tue, 4 Jul 2023 23:07:55 +0900 Subject: fix(api): allow negative column arguments for nvim_buf_set_text (#23501) --- test/functional/api/buffer_spec.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index d9d4539fe8..512a1a08a6 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -437,6 +437,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() -- cgit From 008154954791001efcc46c28146e21403f3a698b Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 21 Aug 2023 14:52:17 +0200 Subject: refactor(change): do API changes to buffer without curbuf switch Most of the messy things when changing a non-current buffer is not about the buffer, it is about windows. In particular, it is about `curwin`. When editing a non-current buffer which is displayed in some other window in the current tabpage, one such window will be "borrowed" as the curwin. But this means if two or more non-current windows displayed the buffers, one of them will be treated differenty. this is not desirable. In particular, with nvim_buf_set_text, cursor _column_ position was only corrected for one single window. Two new tests are added: the test with just one non-current window passes, but the one with two didn't. Two corresponding such tests were also added for nvim_buf_set_lines. This already worked correctly on master, but make sure this is well-tested for future refactors. Also, nvim_create_buf no longer invokes autocmds just because you happened to use `scratch=true`. No option value was changed, therefore OptionSet must not be fired. --- test/functional/api/buffer_spec.lua | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 512a1a08a6..4784c0a9dd 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -76,6 +76,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') @@ -484,6 +516,50 @@ 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) + it('can handle NULs', function() set_text(0, 0, 0, 0, {'ab\0cd'}) eq('ab\0cd', curbuf_depr('get_line', 0)) -- cgit From 9b9030ff2ca820d4c4f4b559f86b0f9a3496645b Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 27 Aug 2023 11:20:59 +0200 Subject: fix(api): fix inconsistent behavior of topline touched in recent refactor The change in #24824 0081549 was not a regression, however it was an incomplete change. Unfortunately some common plugins come to depend on this exising self-inconsistent behavior. These plugins are going to need to update for 0.10 nvim_buf_set_lines used to NOT adjust the topline correctly if a buffer was displayed in just one window. However, if displayed in multiple windows, it was correctly adjusted for any window not deemed the current window for the buffer (which could be an arbitrary choice if the buffer was not already current, as noted in the last rafactor) This fixes so that all windows have their topline adjusted. The added tests show this behavior, which should be the reasonable one. --- test/functional/api/buffer_spec.lua | 266 ++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 4784c0a9dd..fda2ea17b8 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -137,6 +137,139 @@ 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] [+] }| + | + ]]} + 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] [+] }| + | + ]]} + 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] [+] }| + | + ]]} + end) + end) end) describe('deprecated: {get,set,del}_line', function() @@ -659,6 +792,139 @@ describe('api/buf', function() ]]) eq({'one', 'two'}, get_lines(0, 2, true)) 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_text(buf, 0,3, 1,0, {"X"}) + + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {3:[No Name] }| + www | + xxx | + yyy | + ^zzz | + {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_text(buf, 0,3, 1,0, {"X"}) + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] }| + www | + xxx | + yyy | + zzz | + {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,1}) + + screen:expect{grid=[[ + a^aa | + bbb | + ccc | + ddd | + www | + {2:[No Name] }| + www | + xxx | + yyy | + zzz | + {3:[No Name] }| + | + ]]} + meths.buf_set_text(buf, 0,3, 1,0, {"X"}) + + screen:expect{grid=[[ + a^aaXbbb | + ccc | + ddd | + www | + xxx | + {2:[No Name] [+] }| + www | + xxx | + yyy | + zzz | + {3:[No Name] [+] }| + | + ]]} + end) + end) end) describe_lua_and_rpc('nvim_buf_get_text', function(api) -- cgit From 840749d6c971f93aa9744bd6f76b383f11043463 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 27 Aug 2023 11:26:54 +0200 Subject: fix(undo): fix crash caused by checking undolevels in wrong buffer fixes #24894 --- test/functional/api/buffer_spec.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index fda2ea17b8..0afe619b03 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"}) -- cgit From 132bbd1cbd490a65bc186c4a9f449c6d2f0a7666 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 28 Aug 2023 12:36:00 +0200 Subject: fix(api): handle clearing out last line of non-current buffer fixes #24911 --- test/functional/api/buffer_spec.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 0afe619b03..73a8749682 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -279,6 +279,18 @@ describe('api/buf', function() ]]} 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() -- cgit From 0a81ec14a4c006822509b06396871509140b7a79 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 28 Aug 2023 11:35:09 +0200 Subject: fix(api): better topline adjustments in nvim_buf_set_lines Some more reasonable defaults for topline: - if topline was replaced with another line, that now becomes topline - if line was inserted just before topline, display it. This is more similar to the previous API behavior. --- test/functional/api/buffer_spec.lua | 136 +++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 73a8749682..aea1f359e6 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -182,8 +182,8 @@ describe('api/buf', function() {2:[No Name] }| | ]]} - meths.buf_set_lines(buf, 0, 2, true, {"aaabbb"}) + meths.buf_set_lines(buf, 0, 2, true, {"aaabbb"}) screen:expect{grid=[[ | {1:~ }| @@ -198,6 +198,72 @@ describe('api/buf', function() {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() @@ -237,6 +303,40 @@ describe('api/buf', function() {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() @@ -277,6 +377,40 @@ describe('api/buf', function() {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) -- cgit From b051b131f5ce99ffe1b1bc22a2032ebc886e4e35 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 30 Aug 2023 11:53:58 +0200 Subject: fix(api): nvim_buf_get_offset in a new buffer with zero or one lines fixes #24930 --- test/functional/api/buffer_spec.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index aea1f359e6..292e5a2d56 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1151,6 +1151,18 @@ describe('api/buf', function() eq(6, bufmeths.get_offset(1,1)) command("bunload! 1") eq(-1, bufmeths.get_offset(1,1)) + eq(-1, bufmeths.get_offset(1,0)) + end) + + it('works in empty buffer', function() + eq(0, get_offset(0)) + eq(1, get_offset(1)) + end) + + it('works in buffer with one line inserted', function() + feed('itext') + eq(0, get_offset(0)) + eq(5, get_offset(1)) end) end) -- cgit From d22172f36bbe147f3aa6b76a1c43ae445f481c2e Mon Sep 17 00:00:00 2001 From: Sergey Slipchenko Date: Mon, 11 Sep 2023 08:16:03 +0400 Subject: fix(api): more intuitive cursor updates in nvim_buf_set_text Fixes #22526 --- test/functional/api/buffer_spec.lua | 704 ++++++++++++++++++++++++++++++++++++ 1 file changed, 704 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 292e5a2d56..9833ebee4c 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -848,6 +848,710 @@ describe('api/buf', function() 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('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('') + -- close current window + feed('c') + -- in another window cursor should be on 'c' + eq({1, 5}, curwin('get_cursor')) + end) + end) + + describe('when text is being deleted right at cursor position', function() + it('leaves cursor at the same position in NORMAL mode', function() + insert([[ + abcd]]) + + -- position the cursor on 'b' + curwin('set_cursor', {1, 1}) + -- delete 'b' + set_text(0, 1, 0, 2, {}) + eq({'acd'}, get_lines(0, -1, true)) + -- cursor is now on 'c' + eq({1, 1}, curwin('get_cursor')) + end) + + it('leaves cursor at the same position in INSERT mode in current and non-current window', function() + insert([[ + abcd]]) + + -- position the cursor on 'b' + curwin('set_cursor', {1, 1}) + -- open vertical split + feed('v') + -- get into INSERT mode to treat cursor + -- as being after 'a', not on 'b' + feed('i') + -- delete 'b' + set_text(0, 1, 0, 2, {}) + eq({'acd'}, get_lines(0, -1, true)) + -- cursor in the current window should stay after 'a' + eq({1, 1}, curwin('get_cursor')) + -- quit INSERT mode + feed('') + -- close current window + feed('c') + -- cursor in non-current window should stay on 'c' + eq({1, 1}, curwin('get_cursor')) + end) + end) + + describe('when cursor is inside replaced row range', function() + it('keeps cursor at the same position if cursor is at start_row, but before start_col', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on ' ' before 'first' + curwin('set_cursor', {1, 14}) + + set_text(0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay at the same position + eq({1, 14}, curwin('get_cursor')) + end) + + it('keeps cursor at the same position if cursor is at start_row and column is still valid', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'f' in 'first' + curwin('set_cursor', {1, 15}) + + set_text(0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay at the same position + eq({1, 15}, curwin('get_cursor')) + end) + + it('adjusts cursor column to keep it valid if start_row got smaller', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 24, {'last'}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be last' }, get_lines(0, -1, true)) + -- cursor should end up on 't' in 'last' + eq({1, 18}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 18}, cursor) + end) + + it('adjusts cursor column to keep it valid if start_row got smaller in INSERT mode', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + -- enter INSERT mode to treat cursor as being after 't' + feed('a') + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 24, {'last'}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be last' }, get_lines(0, -1, true)) + -- cursor should end up after 't' in 'last' + eq({1, 19}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 19}, cursor) + end) + + it('adjusts cursor column to keep it valid in a row after start_row if it got smaller', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'w' in 'want' + curwin('set_cursor', {2, 31}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + '1', + 'then 2', + 'and then', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be 1', + 'then 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor column should end up at the end of a row + eq({2, 5}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 5}, cursor) + end) + + it('adjusts cursor column to keep it valid in a row after start_row if it got smaller in INSERT mode', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'w' in 'want' + curwin('set_cursor', {2, 31}) + -- enter INSERT mode + feed('a') + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + '1', + 'then 2', + 'and then', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be 1', + 'then 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor column should end up at the end of a row + eq({2, 6}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 6}, cursor) + end) + + it('adjusts cursor line and column to keep it inside replacement range', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'n' in 'finally' + curwin('set_cursor', {3, 6}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'y' in 'hopefully' + -- to stay in the range, because it got smaller + eq({2, 12}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 12}, cursor) + end) + + it('adjusts cursor line and column if replacement is empty', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'r' in 'there' + curwin('set_cursor', {2, 8}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 12, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be the last one' }, get_lines(0, -1, true)) + -- cursor should end up on the next column after deleted range + eq({1, 15}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 15}, cursor) + end) + + it('adjusts cursor line and column if replacement is empty and start_col == 0', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'r' in 'there' + curwin('set_cursor', {2, 8}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 0, 2, 4, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'finally the last one' }, get_lines(0, -1, true)) + -- cursor should end up in column 0 + eq({1, 0}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 0}, cursor) + end) + + it('adjusts cursor column if replacement ends at cursor row, after cursor column', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'y' in 'finally' + curwin('set_cursor', {3, 10}) + set_text(0, 15, 2, 11, { '1', 'this 2', 'and then' }) + + eq({ + 'This should be 1', + 'this 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'n' in 'then' + eq({3, 7}, curwin('get_cursor')) + end) + + it('adjusts cursor column if replacement ends at cursor row, at cursor column in INSERT mode', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'y' at 'finally' + curwin('set_cursor', {3, 10}) + -- enter INSERT mode to treat cursor as being between 'l' and 'y' + feed('i') + set_text(0, 15, 2, 11, { '1', 'this 2', 'and then' }) + + eq({ + 'This should be 1', + 'this 2', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor should end up after 'n' in 'then' + eq({3, 8}, curwin('get_cursor')) + end) + + it('adjusts cursor column if replacement is inside of a single line', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'y' in 'finally' + curwin('set_cursor', {3, 10}) + set_text(2, 4, 2, 11, { 'then' }) + + eq({ + 'This should be first', + 'then there is a line we do not want', + 'and then the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'n' in 'then' + eq({3, 7}, curwin('get_cursor')) + end) + + it('does not move cursor column after end of a line', function() + insert([[ + This should be the only line here + !!!]]) + + -- position cursor on the last '1' + curwin('set_cursor', {2, 2}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 33, 1, 3, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be the only line here' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 32}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 32}, cursor) + end) + + it('does not move cursor column before start of a line', function() + insert('\n!!!') + + -- position cursor on the last '1' + curwin('set_cursor', {2, 2}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 0, 1, 3, {}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ '' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 0}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 0}, cursor) + end) + + describe('with virtualedit', function() + it('adjusts cursor line and column to keep it inside replacement range if cursor is not after eol', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'want' + curwin('set_cursor', {2, 34}) + -- turn on virtualedit + command('set virtualedit=all') + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up on 'y' in 'hopefully' + -- to stay in the range + eq({2, 12}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 12}, cursor) + -- coladd should be 0 + eq(0, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row got shorter', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'want' + curwin('set_cursor', {2, 34}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol + exec_lua([[ + vim.fn.winrestview({ coladd = 5 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up at eol of a new row + eq({2, 26}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 26}, cursor) + -- coladd should be increased so that cursor stays in the same screen column + eq(13, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row got longer', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol + exec_lua([[ + vim.fn.winrestview({ coladd = 21 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up at eol of a new row + eq({1, 38}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 38}, cursor) + -- coladd should be increased so that cursor stays in the same screen column + eq(2, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row extended past cursor column', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position cursor on 't' in 'first' + curwin('set_cursor', {1, 19}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol just a bit + exec_lua([[ + vim.fn.winrestview({ coladd = 3 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay at the same screen column + eq({1, 22}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 22}, cursor) + -- coladd should become 0 + eq(0, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + + it('does not change cursor screen column when cursor is after eol and row range decreased', function() + insert([[ + This should be first + then there is a line we do not want + and one more + and finally the last one]]) + + -- position cursor on 'e' in 'more' + curwin('set_cursor', {3, 11}) + -- turn on virtualedit + command('set virtualedit=all') + -- move cursor after eol + exec_lua([[ + vim.fn.winrestview({ coladd = 28 }) + ]]) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 15, 3, 11, { + 'the line we do not want', + 'but hopefully', + }) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should end up at eol of a new row + eq({2, 26}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({2, 26}, cursor) + -- coladd should be increased so that cursor stays in the same screen column + eq(13, exec_lua([[ + return vim.fn.winsaveview().coladd + ]])) + end) + end) + end) + + describe('when cursor is at end_row and after end_col', function() + it('adjusts cursor column when only a newline is added or deleted', function() + insert([[ + first line + second + line]]) + + -- position the cursor on 'i' + curwin('set_cursor', {3, 2}) + set_text(1, 6, 2, 0, {}) + eq({'first line', 'second line'}, get_lines(0, -1, true)) + -- cursor should stay on 'i' + eq({2, 8}, curwin('get_cursor')) + + -- add a newline back + set_text(1, 6, 1, 6, {'', ''}) + eq({'first line', 'second', ' line'}, get_lines(0, -1, true)) + -- cursor should return back to the original position + eq({3, 2}, curwin('get_cursor')) + end) + + it('adjusts cursor column if the range is not bound to either start or end of a line', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 'h' in 'the' + curwin('set_cursor', {3, 13}) + set_text(0, 14, 2, 11, {}) + eq({'This should be the last one'}, get_lines(0, -1, true)) + -- cursor should stay on 'h' + eq({1, 16}, curwin('get_cursor')) + -- add deleted lines back + set_text(0, 14, 0, 14, { + ' first', + 'then there is a line we do not want', + 'and finally', + }) + eq({ + 'This should be first', + 'then there is a line we do not want', + 'and finally the last one', + }, get_lines(0, -1, true)) + -- cursor should return back to the original position + eq({3, 13}, curwin('get_cursor')) + end) + + it('adjusts cursor column if replacing lines in range, not just deleting and adding', function() + insert([[ + This should be first + then there is a line we do not want + and finally the last one]]) + + -- position the cursor on 's' in 'last' + curwin('set_cursor', {3, 18}) + set_text(0, 15, 2, 11, { + 'the line we do not want', + 'but hopefully', + }) + + eq({ + 'This should be the line we do not want', + 'but hopefully the last one', + }, get_lines(0, -1, true)) + -- cursor should stay on 's' + eq({2, 20}, curwin('get_cursor')) + + set_text(0, 15, 1, 13, { + 'first', + 'then there is a line we do not want', + 'and finally', + }) + + eq({ + 'This should be first', + 'then there is a line we do not want', + 'and finally the last one', + }, get_lines(0, -1, true)) + -- cursor should return back to the original position + eq({3, 18}, curwin('get_cursor')) + end) + + it('does not move cursor column after end of a line', function() + insert([[ + This should be the only line here + ]]) + + -- position cursor at the empty line + curwin('set_cursor', {2, 0}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 33, 1, 0, {'!'}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ 'This should be the only line here!' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 33}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 33}, cursor) + end) + + it('does not move cursor column before start of a line', function() + insert('\n') + + eq({ '', '' }, get_lines(0, -1, true)) + + -- position cursor on the last '1' + curwin('set_cursor', {2, 2}) + + local cursor = exec_lua([[ + vim.api.nvim_buf_set_text(0, 0, 0, 1, 0, {''}) + return vim.api.nvim_win_get_cursor(0) + ]]) + + eq({ '' }, get_lines(0, -1, true)) + -- cursor should end up on '!' + eq({1, 0}, curwin('get_cursor')) + -- immediate call to nvim_win_get_cursor should have returned the same position + eq({1, 0}, cursor) + end) + end) + it('can handle NULs', function() set_text(0, 0, 0, 0, {'ab\0cd'}) eq('ab\0cd', curbuf_depr('get_line', 0)) -- cgit From 0da27e9bdec14acf82731c4d5e0ad7d673697af7 Mon Sep 17 00:00:00 2001 From: Raphael Date: Sun, 29 Oct 2023 15:44:52 +0800 Subject: fix(api): load buffer first on nvim_buf_set_lines (#25823) Fix #22670 Fix #8659 --- test/functional/api/buffer_spec.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'test/functional/api/buffer_spec.lua') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 9833ebee4c..6ed9aa574a 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -713,6 +713,21 @@ describe('api/buf', function() feed('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() -- cgit