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:
bfredl
2023-08-21 14:52:17 +02:00
parent 1635c9e75e
commit 0081549547
33 changed files with 737 additions and 548 deletions

View File

@@ -101,28 +101,28 @@ void change_warning(buf_T *buf, int col)
}
}
/// Call this function when something in the current buffer is changed.
/// Call this function when something in a buffer is changed.
///
/// Most often called through changed_bytes() and changed_lines(), which also
/// mark the area of the display to be redrawn.
///
/// Careful: may trigger autocommands that reload the buffer.
void changed(void)
void changed(buf_T *buf)
{
if (!curbuf->b_changed) {
if (!buf->b_changed) {
int save_msg_scroll = msg_scroll;
// Give a warning about changing a read-only file. This may also
// check-out the file, thus change "curbuf"!
change_warning(curbuf, 0);
change_warning(buf, 0);
// Create a swap file if that is wanted.
// Don't do this for "nofile" and "nowrite" buffer types.
if (curbuf->b_may_swap && !bt_dontwrite(curbuf)) {
if (buf->b_may_swap && !bt_dontwrite(buf)) {
bool save_need_wait_return = need_wait_return;
need_wait_return = false;
ml_open_file(curbuf);
ml_open_file(buf);
// The ml_open_file() can cause an ATTENTION message.
// Wait two seconds, to make sure the user reads this unexpected
@@ -137,9 +137,9 @@ void changed(void)
need_wait_return = save_need_wait_return;
}
}
changed_internal();
changed_internal(buf);
}
buf_inc_changedtick(curbuf);
buf_inc_changedtick(buf);
// If a pattern is highlighted, the position may now be invalid.
highlight_match = false;
@@ -147,12 +147,12 @@ void changed(void)
/// Internal part of changed(), no user interaction.
/// Also used for recovery.
void changed_internal(void)
void changed_internal(buf_T *buf)
{
curbuf->b_changed = true;
curbuf->b_changed_invalid = true;
ml_setflags(curbuf);
redraw_buf_status_later(curbuf);
buf->b_changed = true;
buf->b_changed_invalid = true;
ml_setflags(buf);
redraw_buf_status_later(buf);
redraw_tabline = true;
need_maketitle = true; // set window title later
}
@@ -160,13 +160,15 @@ void changed_internal(void)
/// Common code for when a change was made.
/// See changed_lines() for the arguments.
/// Careful: may trigger autocommands that reload the buffer.
static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra)
static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra)
{
// mark the buffer as modified
changed();
changed(buf);
if (curwin->w_p_diff && diff_internal()) {
curtab->tp_diff_update = true;
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
if (win->w_buffer == buf && win->w_p_diff && diff_internal()) {
curtab->tp_diff_update = true;
}
}
// set the '. mark
@@ -174,22 +176,25 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
fmarkv_T view = INIT_FMARKV;
// Set the markview only if lnum is visible, as changes might be done
// outside of the current window view.
if (lnum >= curwin->w_topline && lnum <= curwin->w_botline) {
view = mark_view_make(curwin->w_topline, curwin->w_cursor);
if (curwin->w_buffer == buf) {
if (lnum >= curwin->w_topline && lnum <= curwin->w_botline) {
view = mark_view_make(curwin->w_topline, curwin->w_cursor);
}
}
RESET_FMARK(&curbuf->b_last_change, ((pos_T) { lnum, col, 0 }), curbuf->handle, view);
RESET_FMARK(&buf->b_last_change, ((pos_T) { lnum, col, 0 }), buf->handle, view);
// Create a new entry if a new undo-able change was started or we
// don't have an entry yet.
if (curbuf->b_new_change || curbuf->b_changelistlen == 0) {
if (buf->b_new_change || buf->b_changelistlen == 0) {
int add;
if (curbuf->b_changelistlen == 0) {
if (buf->b_changelistlen == 0) {
add = true;
} else {
// Don't create a new entry when the line number is the same
// as the last one and the column is not too far away. Avoids
// creating many entries for typing "xxxxx".
pos_T *p = &curbuf->b_changelist[curbuf->b_changelistlen - 1].mark;
pos_T *p = &buf->b_changelist[buf->b_changelistlen - 1].mark;
if (p->lnum != lnum) {
add = true;
} else {
@@ -204,17 +209,17 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
// This is the first of a new sequence of undo-able changes
// and it's at some distance of the last change. Use a new
// position in the changelist.
curbuf->b_new_change = false;
buf->b_new_change = false;
if (curbuf->b_changelistlen == JUMPLISTSIZE) {
if (buf->b_changelistlen == JUMPLISTSIZE) {
// changelist is full: remove oldest entry
curbuf->b_changelistlen = JUMPLISTSIZE - 1;
memmove(curbuf->b_changelist, curbuf->b_changelist + 1,
sizeof(curbuf->b_changelist[0]) * (JUMPLISTSIZE - 1));
buf->b_changelistlen = JUMPLISTSIZE - 1;
memmove(buf->b_changelist, buf->b_changelist + 1,
sizeof(buf->b_changelist[0]) * (JUMPLISTSIZE - 1));
FOR_ALL_TAB_WINDOWS(tp, wp) {
// Correct position in changelist for other windows on
// this buffer.
if (wp->w_buffer == curbuf && wp->w_changelistidx > 0) {
if (wp->w_buffer == buf && wp->w_changelistidx > 0) {
wp->w_changelistidx--;
}
}
@@ -222,27 +227,29 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
FOR_ALL_TAB_WINDOWS(tp, wp) {
// For other windows, if the position in the changelist is
// at the end it stays at the end.
if (wp->w_buffer == curbuf
&& wp->w_changelistidx == curbuf->b_changelistlen) {
if (wp->w_buffer == buf
&& wp->w_changelistidx == buf->b_changelistlen) {
wp->w_changelistidx++;
}
}
curbuf->b_changelistlen++;
buf->b_changelistlen++;
}
}
curbuf->b_changelist[curbuf->b_changelistlen - 1] =
curbuf->b_last_change;
buf->b_changelist[buf->b_changelistlen - 1] =
buf->b_last_change;
// The current window is always after the last change, so that "g,"
// takes you back to it.
curwin->w_changelistidx = curbuf->b_changelistlen;
if (curwin->w_buffer == buf) {
curwin->w_changelistidx = buf->b_changelistlen;
}
}
if (VIsual_active) {
if (curwin->w_buffer == buf && VIsual_active) {
check_visual_pos();
}
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == curbuf) {
if (wp->w_buffer == buf) {
// Mark this window to be redrawn later.
if (wp->w_redr_type < UPD_VALID) {
wp->w_redr_type = UPD_VALID;
@@ -295,7 +302,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
if (wp->w_cursor.lnum > lnum) {
changed_line_abv_curs_win(wp);
} else if (wp->w_cursor.lnum == lnum && wp->w_cursor.col >= col) {
changed_cline_bef_curs_win(wp);
changed_cline_bef_curs(wp);
}
if (wp->w_botline >= lnum) {
// Assume that botline doesn't change (inserted lines make
@@ -361,7 +368,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
}
// when the cursor line is changed always trigger CursorMoved
if (last_cursormoved_win == curwin
if (last_cursormoved_win == curwin && curwin->w_buffer == buf
&& lnum <= curwin->w_cursor.lnum
&& lnume + (xtra < 0 ? -xtra : xtra) > curwin->w_cursor.lnum) {
last_cursormoved.lnum = 0;
@@ -394,7 +401,7 @@ static void changedOneline(buf_T *buf, linenr_T lnum)
void changed_bytes(linenr_T lnum, colnr_T col)
{
changedOneline(curbuf, lnum);
changed_common(lnum, col, lnum + 1, 0);
changed_common(curbuf, lnum, col, lnum + 1, 0);
// When text has been changed at the end of the line, possibly the start of
// the next line may have SpellCap that should be removed or it needs to be
// displayed. Schedule the next line for redrawing just in case.
@@ -438,14 +445,14 @@ void inserted_bytes(linenr_T lnum, colnr_T start_col, int old_col, int new_col)
/// Takes care of marking the buffer to be redrawn and sets the changed flag.
void appended_lines(linenr_T lnum, linenr_T count)
{
changed_lines(lnum + 1, 0, lnum + 1, count, true);
changed_lines(curbuf, lnum + 1, 0, lnum + 1, count, true);
}
/// Like appended_lines(), but adjust marks first.
void appended_lines_mark(linenr_T lnum, int count)
{
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, (linenr_T)count, 0L, kExtmarkUndo);
changed_lines(lnum + 1, 0, lnum + 1, (linenr_T)count, true);
changed_lines(curbuf, lnum + 1, 0, lnum + 1, (linenr_T)count, true);
}
/// Deleted "count" lines at line "lnum" in the current buffer.
@@ -453,7 +460,7 @@ void appended_lines_mark(linenr_T lnum, int count)
/// Takes care of marking the buffer to be redrawn and sets the changed flag.
void deleted_lines(linenr_T lnum, linenr_T count)
{
changed_lines(lnum, 0, lnum + count, -count, true);
changed_lines(curbuf, lnum, 0, lnum + count, -count, true);
}
/// Like deleted_lines(), but adjust marks first.
@@ -467,7 +474,7 @@ void deleted_lines_mark(linenr_T lnum, int count)
// if we deleted the entire buffer, we need to implicitly add a new empty line
extmark_adjust(curbuf, lnum, (linenr_T)(lnum + count - 1), MAXLNUM,
-(linenr_T)count + (made_empty ? 1 : 0), kExtmarkUndo);
changed_lines(lnum, 0, lnum + (linenr_T)count, (linenr_T)(-count), true);
changed_lines(curbuf, lnum, 0, lnum + (linenr_T)count, (linenr_T)(-count), true);
}
/// Marks the area to be redrawn after a change.
@@ -477,7 +484,7 @@ void deleted_lines_mark(linenr_T lnum, int count)
/// @param lnum first line with change
/// @param lnume line below last changed line
/// @param xtra number of extra lines (negative when deleting)
void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra)
void buf_redraw_changed_lines_later(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra)
{
if (buf->b_mod_set) {
// find the maximum area that must be redisplayed
@@ -504,7 +511,7 @@ void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra)
}
}
/// Changed lines for the current buffer.
/// Changed lines for a buffer.
/// Must be called AFTER the change and after mark_adjust().
/// - mark the buffer changed by calling changed()
/// - mark the windows on this buffer to be redisplayed
@@ -522,11 +529,12 @@ void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra)
/// @param do_buf_event some callers like undo/redo call changed_lines() and
/// then increment changedtick *again*. This flag allows these callers to send
/// the nvim_buf_lines_event events after they're done modifying changedtick.
void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra, bool do_buf_event)
void changed_lines(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra,
bool do_buf_event)
{
changed_lines_buf(curbuf, lnum, lnume, xtra);
buf_redraw_changed_lines_later(buf, lnum, lnume, xtra);
if (xtra == 0 && curwin->w_p_diff && !diff_internal()) {
if (xtra == 0 && curwin->w_p_diff && curwin->w_buffer == buf && !diff_internal()) {
// When the number of lines doesn't change then mark_adjust() isn't
// called and other diff buffers still need to be marked for
// displaying.
@@ -537,19 +545,19 @@ void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra, bo
redraw_later(wp, UPD_VALID);
wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changed_lines_buf(wp->w_buffer, wlnum,
lnume - lnum + wlnum, 0L);
buf_redraw_changed_lines_later(wp->w_buffer, wlnum,
lnume - lnum + wlnum, 0L);
}
}
}
}
changed_common(lnum, col, lnume, xtra);
changed_common(buf, lnum, col, lnume, xtra);
if (do_buf_event) {
int64_t num_added = (int64_t)(lnume + xtra - lnum);
int64_t num_removed = lnume - lnum;
buf_updates_send_changes(curbuf, lnum, num_added, num_removed);
buf_updates_send_changes(buf, lnum, num_added, num_removed);
}
}
@@ -1119,7 +1127,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
*p_extra = NUL;
}
u_clearline(); // cannot do "U" command when adding lines
u_clearline(curbuf); // cannot do "U" command when adding lines
did_si = false;
ai_col = 0;
@@ -1807,7 +1815,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
saved_line = NULL;
if (did_append) {
changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col,
changed_lines(curbuf, curwin->w_cursor.lnum, curwin->w_cursor.col,
curwin->w_cursor.lnum + 1, 1L, true);
did_append = false;
@@ -1833,7 +1841,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
curwin->w_cursor.lnum = old_cursor.lnum + 1;
}
if (did_append) {
changed_lines(curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L, true);
changed_lines(curbuf, curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L, true);
// bail out and just get the final length of the line we just manipulated
bcount_t extra = (bcount_t)strlen(ml_get(curwin->w_cursor.lnum));
extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, 0,
@@ -1958,7 +1966,7 @@ void del_lines(long nlines, bool undo)
// Correct the cursor position before calling deleted_lines_mark(), it may
// trigger a callback to display the cursor.
curwin->w_cursor.col = 0;
check_cursor_lnum();
check_cursor_lnum(curwin);
// adjust marks, mark the buffer as changed and prepare for displaying
deleted_lines_mark(first, n);