Merge pull request #24889 from bfredl/topline

fix(api): fix issues with nvim_buf_set_lines refactor
This commit is contained in:
bfredl
2023-08-27 13:56:20 +02:00
committed by GitHub
4 changed files with 300 additions and 25 deletions

View File

@@ -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,

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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)