mirror of
https://github.com/neovim/neovim.git
synced 2025-09-11 22:08:18 +00:00
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.
This commit is contained in:
@@ -386,27 +386,29 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
||||
}
|
||||
|
||||
try_start();
|
||||
aco_save_T aco;
|
||||
aucmd_prepbuf(&aco, buf);
|
||||
|
||||
if (!MODIFIABLE(buf)) {
|
||||
api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (u_save((linenr_T)(start - 1), (linenr_T)end) == FAIL) {
|
||||
if (!buf_ensure_loaded(buf)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (u_save_buf(buf, (linenr_T)(start - 1), (linenr_T)end) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to save undo information");
|
||||
goto end;
|
||||
}
|
||||
|
||||
bcount_t deleted_bytes = get_region_bytecount(curbuf, (linenr_T)start, (linenr_T)end, 0, 0);
|
||||
bcount_t deleted_bytes = get_region_bytecount(buf, (linenr_T)start, (linenr_T)end, 0, 0);
|
||||
|
||||
// If the size of the range is reducing (ie, new_len < old_len) we
|
||||
// need to delete some old_len. We do this at the start, by
|
||||
// repeatedly deleting line "start".
|
||||
size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
|
||||
for (size_t i = 0; i < to_delete; i++) {
|
||||
if (ml_delete((linenr_T)start, false) == FAIL) {
|
||||
if (ml_delete_buf(buf, (linenr_T)start, false) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to delete line");
|
||||
goto end;
|
||||
}
|
||||
@@ -428,7 +430,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
||||
goto end;
|
||||
});
|
||||
|
||||
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
||||
if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to replace line");
|
||||
goto end;
|
||||
}
|
||||
@@ -447,7 +449,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
||||
goto end;
|
||||
});
|
||||
|
||||
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||
if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to insert line");
|
||||
goto end;
|
||||
}
|
||||
@@ -462,20 +464,18 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
||||
|
||||
// Adjust marks. Invalidate any which lie in the
|
||||
// changed range, and move any in the remainder of the buffer.
|
||||
// Only adjust marks if we managed to switch to a window that holds
|
||||
// the buffer, otherwise line numbers will be invalid.
|
||||
mark_adjust((linenr_T)start,
|
||||
(linenr_T)(end - 1),
|
||||
MAXLNUM,
|
||||
(linenr_T)extra,
|
||||
kExtmarkNOOP);
|
||||
mark_adjust_buf(buf, (linenr_T)start, (linenr_T)(end - 1), MAXLNUM, (linenr_T)extra,
|
||||
true, true, kExtmarkNOOP);
|
||||
|
||||
extmark_splice(curbuf, (int)start - 1, 0, (int)(end - start), 0,
|
||||
extmark_splice(buf, (int)start - 1, 0, (int)(end - start), 0,
|
||||
deleted_bytes, (int)new_len, 0, inserted_bytes,
|
||||
kExtmarkUndo);
|
||||
|
||||
changed_lines((linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
|
||||
fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra);
|
||||
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);
|
||||
}
|
||||
|
||||
end:
|
||||
for (size_t i = 0; i < new_len; i++) {
|
||||
@@ -483,7 +483,6 @@ end:
|
||||
}
|
||||
|
||||
xfree(lines);
|
||||
aucmd_restbuf(&aco);
|
||||
try_end(err);
|
||||
}
|
||||
|
||||
@@ -630,17 +629,19 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
||||
}
|
||||
|
||||
try_start();
|
||||
aco_save_T aco;
|
||||
aucmd_prepbuf(&aco, buf);
|
||||
|
||||
if (!MODIFIABLE(buf)) {
|
||||
api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!buf_ensure_loaded(buf)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Small note about undo states: unlike set_lines, we want to save the
|
||||
// undo state of one past the end_row, since end_row is inclusive.
|
||||
if (u_save((linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
|
||||
if (u_save_buf(buf, (linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to save undo information");
|
||||
goto end;
|
||||
}
|
||||
@@ -653,7 +654,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
||||
// repeatedly deleting line "start".
|
||||
size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
|
||||
for (size_t i = 0; i < to_delete; i++) {
|
||||
if (ml_delete((linenr_T)start_row, false) == FAIL) {
|
||||
if (ml_delete_buf(buf, (linenr_T)start_row, false) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to delete line");
|
||||
goto end;
|
||||
}
|
||||
@@ -674,7 +675,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
||||
goto end;
|
||||
});
|
||||
|
||||
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
||||
if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to replace line");
|
||||
goto end;
|
||||
}
|
||||
@@ -691,7 +692,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
||||
goto end;
|
||||
});
|
||||
|
||||
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||
if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||
api_set_error(err, kErrorTypeException, "Failed to insert line");
|
||||
goto end;
|
||||
}
|
||||
@@ -702,35 +703,37 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
||||
extra++;
|
||||
}
|
||||
|
||||
// Adjust marks. Invalidate any which lie in the
|
||||
// changed range, and move any in the remainder of the buffer.
|
||||
mark_adjust((linenr_T)start_row,
|
||||
(linenr_T)end_row,
|
||||
MAXLNUM,
|
||||
(linenr_T)extra,
|
||||
kExtmarkNOOP);
|
||||
|
||||
colnr_T col_extent = (colnr_T)(end_col
|
||||
- ((end_row == start_row) ? start_col : 0));
|
||||
|
||||
// Adjust marks. Invalidate any which lie in the
|
||||
// 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);
|
||||
|
||||
extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
|
||||
(int)(end_row - start_row), col_extent, old_byte,
|
||||
(int)new_len - 1, (colnr_T)last_item.size, new_byte,
|
||||
kExtmarkUndo);
|
||||
|
||||
changed_lines((linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
|
||||
changed_lines(buf, (linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
|
||||
|
||||
// adjust cursor like an extmark ( i e it was inside last_part_len)
|
||||
if (curwin->w_cursor.lnum == end_row && curwin->w_cursor.col > end_col) {
|
||||
curwin->w_cursor.col -= col_extent - (colnr_T)last_item.size;
|
||||
FOR_ALL_TAB_WINDOWS(tp, win) {
|
||||
if (win->w_buffer == buf) {
|
||||
// adjust cursor like an extmark ( i e it was inside last_part_len)
|
||||
if (win->w_cursor.lnum == end_row && win->w_cursor.col > end_col) {
|
||||
win->w_cursor.col -= col_extent - (colnr_T)last_item.size;
|
||||
}
|
||||
fix_cursor(win, (linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
|
||||
}
|
||||
}
|
||||
fix_cursor((linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
|
||||
|
||||
end:
|
||||
for (size_t i = 0; i < new_len; i++) {
|
||||
xfree(lines[i]);
|
||||
}
|
||||
xfree(lines);
|
||||
aucmd_restbuf(&aco);
|
||||
try_end(err);
|
||||
|
||||
early_end:
|
||||
@@ -1317,21 +1320,19 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err)
|
||||
|
||||
// Check if deleting lines made the cursor position invalid.
|
||||
// Changed lines from `lo` to `hi`; added `extra` lines (negative if deleted).
|
||||
static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra)
|
||||
static void fix_cursor(win_T *win, linenr_T lo, linenr_T hi, linenr_T extra)
|
||||
{
|
||||
if (curwin->w_cursor.lnum >= lo) {
|
||||
if (win->w_cursor.lnum >= lo) {
|
||||
// Adjust cursor position if it's in/after the changed lines.
|
||||
if (curwin->w_cursor.lnum >= hi) {
|
||||
curwin->w_cursor.lnum += extra;
|
||||
check_cursor_col();
|
||||
if (win->w_cursor.lnum >= hi) {
|
||||
win->w_cursor.lnum += extra;
|
||||
} else if (extra < 0) {
|
||||
check_cursor();
|
||||
} else {
|
||||
check_cursor_col();
|
||||
check_cursor_lnum(win);
|
||||
}
|
||||
changed_cline_bef_curs();
|
||||
check_cursor_col_win(win);
|
||||
changed_cline_bef_curs(win);
|
||||
}
|
||||
invalidate_botline();
|
||||
invalidate_botline(win);
|
||||
}
|
||||
|
||||
/// Initialise a string array either:
|
||||
|
Reference in New Issue
Block a user