Compare commits

...

2 Commits

Author SHA1 Message Date
zeertzjq
f7d08562c6
Merge 7ee02ad56e into 3085c9d9da 2024-08-28 16:37:58 -04:00
zeertzjq
7ee02ad56e fix(extmarks): redraw new position after deleting lines
Problem:
Virtual text not redrawn properly when its extmark's line is deleted.

Solution:
Redraw new positions of extmarks in the range when deleting lines.
2024-02-13 16:09:03 +08:00
5 changed files with 112 additions and 10 deletions

View File

@ -167,6 +167,13 @@ void changed_internal(buf_T *buf)
static void changed_lines_invalidate_win(win_T *wp, linenr_T lnum, colnr_T col, linenr_T lnume,
linenr_T xtra)
{
// If lines haven been inserted/deleted and the buffer has virt_lines,
// invalidate the line after the changed lines as some virt_lines may
// now be drawn above a different line.
if (xtra != 0 && buf_meta_total(wp->w_buffer, kMTMetaLines) > 0) {
lnume++;
}
// If the changed line is in a range of previously folded lines,
// compare with the first line in that range.
if (wp->w_cursor.lnum <= lnum) {
@ -195,12 +202,7 @@ static void changed_lines_invalidate_win(win_T *wp, linenr_T lnum, colnr_T col,
if (wp->w_lines[i].wl_lnum >= lnum) {
// Do not change wl_lnum at index zero, it is used to compare with w_topline.
// Invalidate it instead.
// If lines haven been inserted/deleted and the buffer has virt_lines,
// invalidate the line after the changed lines as some virt_lines may
// now be drawn above a different line.
if (i == 0 || wp->w_lines[i].wl_lnum < lnume
|| (xtra != 0 && wp->w_lines[i].wl_lnum == lnume
&& buf_meta_total(wp->w_buffer, kMTMetaLines) > 0)) {
if (i == 0 || wp->w_lines[i].wl_lnum < lnume) {
// line included in change
wp->w_lines[i].wl_valid = false;
} else if (xtra != 0) {
@ -503,6 +505,10 @@ void deleted_lines(linenr_T lnum, linenr_T count)
/// be triggered to display the cursor.
void deleted_lines_mark(linenr_T lnum, int count)
{
u_header_T *uhp = u_force_get_undo_header(curbuf);
extmark_undo_vec_t *uvp = uhp ? &uhp->uh_extmark : NULL;
size_t prev_uvp_size = uvp ? kv_size(*uvp) : 0;
bool made_empty = (count > 0) && curbuf->b_ml.ml_flags & ML_EMPTY;
mark_adjust(lnum, (linenr_T)(lnum + count - 1), MAXLNUM, -(linenr_T)count, kExtmarkNOOP);
@ -510,6 +516,10 @@ void deleted_lines_mark(linenr_T lnum, int count)
extmark_adjust(curbuf, lnum, (linenr_T)(lnum + count - 1), MAXLNUM,
-(linenr_T)count + (made_empty ? 1 : 0), kExtmarkUndo);
changed_lines(curbuf, lnum, 0, lnum + (linenr_T)count, (linenr_T)(-count), true);
if (uvp) {
extmark_redraw_after_delete(uvp, prev_uvp_size);
}
}
/// Marks the area to be redrawn after a change.
@ -521,6 +531,13 @@ void deleted_lines_mark(linenr_T lnum, int count)
/// @param xtra number of extra lines (negative when deleting)
void changed_lines_redraw_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, linenr_T xtra)
{
// If lines haven been inserted/deleted and the buffer has virt_lines,
// mark the line after the changed lines to be redrawn as some virt_lines may
// now be drawn above a different line.
if (xtra != 0 && buf_meta_total(buf, kMTMetaLines) > 0) {
lnume++;
}
if (buf->b_mod_set) {
// find the maximum area that must be redisplayed
buf->b_mod_top = MIN(buf->b_mod_top, lnum);

View File

@ -99,7 +99,6 @@ void decor_redraw(buf_T *buf, int row1, int row2, int col1, DecorInline decor)
linenr_T vt_lnum = row1 + 1 + below;
redraw_buf_line_later(buf, vt_lnum, true);
if (vt->flags & kVTIsLines || vt->pos == kVPosInline) {
// changed_lines_redraw_buf(buf, vt_lnum, vt_lnum + 1, 0);
colnr_T vt_col = vt->flags & kVTIsLines ? 0 : col1;
changed_lines_invalidate_buf(buf, vt_lnum, vt_col, vt_lnum + 1, 0);
}

View File

@ -121,8 +121,9 @@ static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool
// SavePos from a modified mark. Avoid adding that to the decor again.
invalid = invalid && mt_invalid(key);
assert(buf->b_mod_set);
// Only the position before undo needs to be redrawn here,
// as the position after undo should be marked as changed.
// as the position after undo should have been marked as changed.
if (!invalid && mt_decor_any(key) && key.pos.row != row) {
decor_redraw(buf, key.pos.row, key.pos.row, key.pos.col, mt_decor(key));
}
@ -416,6 +417,29 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
}
}
/// Mark new positions of extmarks in the range for redraw after deleting lines.
/// This should be called after marking changed lines for redraw and splicing extmarks,
/// otherwise the range marked for redraw will be larger than needed.
void extmark_redraw_after_delete(extmark_undo_vec_t *uvp, size_t prev_uvp_size)
{
assert(curbuf->b_mod_set);
for (size_t i = prev_uvp_size; i < kv_size(*uvp); i++) {
const ExtmarkUndoObject *item = &kv_A(*uvp, i);
if (item->type != kExtmarkSavePos) {
continue;
}
const ExtmarkSavePos *pos = &item->data.savepos;
if (pos->invalidated) {
continue;
}
MarkTreeIter itr[1] = { 0 };
MTKey key = marktree_lookup(curbuf->b_marktree, pos->mark, itr);
if (mt_decor_any(key)) {
decor_redraw(curbuf, key.pos.row, key.pos.row, key.pos.col, mt_decor(key));
}
}
}
/// undo or redo an extmark operation
void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo)
{

View File

@ -2412,13 +2412,14 @@ static void u_undoredo(bool undo, bool do_buf_event)
// Adjust Extmarks
if (undo) {
for (int i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) {
extmark_apply_undo(kv_A(curhead->uh_extmark, i), undo);
extmark_apply_undo(kv_A(curhead->uh_extmark, i), true);
}
// redo
} else {
for (int i = 0; i < (int)kv_size(curhead->uh_extmark); i++) {
extmark_apply_undo(kv_A(curhead->uh_extmark, i), undo);
extmark_apply_undo(kv_A(curhead->uh_extmark, i), false);
}
extmark_redraw_after_delete(&curhead->uh_extmark, 0);
}
if (curhead->uh_flags & UH_RELOAD) {
// TODO(bfredl): this is a bit crude. When 'undoreload' is used we

View File

@ -4114,6 +4114,67 @@ describe('decorations: inline virtual text', function()
|
]])
end)
it('is redrawn correctly after delete or redo #27370', function()
screen:try_resize(50, 12)
exec([[
call setline(1, ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff'])
call setline(3, repeat('c', winwidth(0) - 1))
]])
api.nvim_buf_set_extmark(0, ns, 1, 0, { virt_text = { { '!!!' } }, virt_text_pos = 'inline' })
feed('j')
local before_delete = [[
aaa |
!!!^bbb |
ccccccccccccccccccccccccccccccccccccccccccccccccc |
ddd |
eee |
fff |
{1:~ }|*5
|
]]
screen:expect(before_delete)
feed('dd')
local after_delete = [[
aaa |
!!!^ccccccccccccccccccccccccccccccccccccccccccccccc|
cc |
ddd |
eee |
fff |
{1:~ }|*5
|
]]
screen:expect(after_delete)
command('silent undo')
screen:expect(before_delete)
command('silent redo')
screen:expect(after_delete)
command('silent undo')
screen:expect(before_delete)
command('set report=100')
feed('yypk2P')
before_delete = [[
aaa |
^bbb |
bbb |
!!!bbb |
bbb |
ccccccccccccccccccccccccccccccccccccccccccccccccc |
ddd |
eee |
fff |
{1:~ }|*2
|
]]
screen:expect(before_delete)
feed('4dd')
screen:expect(after_delete)
command('silent undo')
screen:expect(before_delete)
command('silent redo')
screen:expect(after_delete)
end)
end)
describe('decorations: virtual lines', function()