diff options
author | bfredl <bjorn.linse@gmail.com> | 2023-08-27 13:56:20 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-27 13:56:20 +0200 |
commit | 4c69279f97e566277a33b81146b26b7ca20de4f3 (patch) | |
tree | e9ed01feacbc4be34b96faf46e4baa1a8c83de16 | |
parent | b7d5b55f74fd589dee27d8356d45b31c552705c3 (diff) | |
parent | 840749d6c971f93aa9744bd6f76b383f11043463 (diff) | |
download | rneovim-4c69279f97e566277a33b81146b26b7ca20de4f3.tar.gz rneovim-4c69279f97e566277a33b81146b26b7ca20de4f3.tar.bz2 rneovim-4c69279f97e566277a33b81146b26b7ca20de4f3.zip |
Merge pull request #24889 from bfredl/topline
fix(api): fix issues with nvim_buf_set_lines refactor
-rw-r--r-- | src/nvim/api/buffer.c | 10 | ||||
-rw-r--r-- | src/nvim/mark.c | 34 | ||||
-rw-r--r-- | src/nvim/undo.c | 6 | ||||
-rw-r--r-- | test/functional/api/buffer_spec.lua | 275 |
4 files changed, 300 insertions, 25 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 775b6e8ea7..d36f0dd050 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -472,9 +472,11 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ kExtmarkUndo); changed_lines(buf, (linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true); - if (curwin->w_buffer == buf) { - // mark_adjust_buf handles non-current windows - fix_cursor(curwin, (linenr_T)start, (linenr_T)end, (linenr_T)extra); + + FOR_ALL_TAB_WINDOWS(tp, win) { + if (win->w_buffer == buf) { + fix_cursor(win, (linenr_T)start, (linenr_T)end, (linenr_T)extra); + } } end: @@ -710,7 +712,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In // changed range, and move any in the remainder of the buffer. // Do not adjust any cursors. need to use column-aware logic (below) mark_adjust_buf(buf, (linenr_T)start_row, (linenr_T)end_row, MAXLNUM, (linenr_T)extra, - true, false, kExtmarkNOOP); + true, true, kExtmarkNOOP); extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col, (int)(end_row - start_row), col_extent, old_byte, diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 2e08a6f591..584a6c5827 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -1121,7 +1121,7 @@ void ex_changes(exarg_T *eap) void mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after, ExtmarkOp op) { - mark_adjust_buf(curbuf, line1, line2, amount, amount_after, true, true, op); + mark_adjust_buf(curbuf, line1, line2, amount, amount_after, true, false, op); } // mark_adjust_nofold() does the same as mark_adjust() but without adjusting @@ -1132,11 +1132,11 @@ void mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amoun void mark_adjust_nofold(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after, ExtmarkOp op) { - mark_adjust_buf(curbuf, line1, line2, amount, amount_after, false, true, op); + mark_adjust_buf(curbuf, line1, line2, amount, amount_after, false, false, op); } void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount, - linenr_T amount_after, bool adjust_folds, bool adj_cursor, ExtmarkOp op) + linenr_T amount_after, bool adjust_folds, bool by_api, ExtmarkOp op) { int fnum = buf->b_fnum; linenr_T *lp; @@ -1243,7 +1243,7 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount // topline and cursor position for windows with the same buffer // other than the current window - if (win != curwin) { + if (win != curwin || by_api) { if (win->w_topline >= line1 && win->w_topline <= line2) { if (amount == MAXLNUM) { // topline is deleted if (line1 <= 1) { @@ -1261,21 +1261,21 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount win->w_topline += amount_after; win->w_topfill = 0; } - if (adj_cursor) { - if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2) { - if (amount == MAXLNUM) { // line with cursor is deleted - if (line1 <= 1) { - win->w_cursor.lnum = 1; - } else { - win->w_cursor.lnum = line1 - 1; - } - win->w_cursor.col = 0; - } else { // keep cursor on the same line - win->w_cursor.lnum += amount; + } + if (win != curwin && !by_api) { + if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2) { + if (amount == MAXLNUM) { // line with cursor is deleted + if (line1 <= 1) { + win->w_cursor.lnum = 1; + } else { + win->w_cursor.lnum = line1 - 1; } - } else if (amount_after && win->w_cursor.lnum > line2) { - win->w_cursor.lnum += amount_after; + win->w_cursor.col = 0; + } else { // keep cursor on the same line + win->w_cursor.lnum += amount; } + } else if (amount_after && win->w_cursor.lnum > line2) { + win->w_cursor.lnum += amount_after; } } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 552120d4ae..1194eeca35 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -3208,15 +3208,13 @@ u_header_T *u_force_get_undo_header(buf_T *buf) } // Create the first undo header for the buffer if (!uhp) { - // Undo is normally invoked in change code, which already has swapped - // curbuf. // Args are tricky: this means replace empty range by empty range.. - u_savecommon(curbuf, 0, 1, 1, true); + u_savecommon(buf, 0, 1, 1, true); uhp = buf->b_u_curhead; if (!uhp) { uhp = buf->b_u_newhead; - if (get_undolevel(curbuf) > 0 && !uhp) { + if (get_undolevel(buf) > 0 && !uhp) { abort(); } } diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 4784c0a9dd..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"}) @@ -137,6 +146,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 +801,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) |