mirror of
https://github.com/neovim/neovim.git
synced 2026-08-29 18:41:48 +00:00
Merge pull request #24889 from bfredl/topline
fix(api): fix issues with nvim_buf_set_lines refactor
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user