patch 8.1.2008: error for invalid range when using listener and undo

Problem:    Error for invalid range when using listener and undo. (Paul Jolly)
Solution:   Do not change the cursor before the lines are restored.
            (closes #4908)
This commit is contained in:
Bram Moolenaar
2019-09-08 15:27:21 +02:00
parent fca068b977
commit 4544bd2f24
3 changed files with 63 additions and 26 deletions

View File

@ -234,7 +234,7 @@ func Test_listener_garbage_collect()
new new
let id = listener_add(function('MyListener', [{}]), bufnr('')) let id = listener_add(function('MyListener', [{}]), bufnr(''))
call test_garbagecollect_now() call test_garbagecollect_now()
" must not crach caused by invalid memory access " must not crash caused by invalid memory access
normal ia normal ia
call assert_true(v:true) call assert_true(v:true)
@ -268,3 +268,25 @@ func Test_listener_caches_buffer_line()
iunmap <CR> iunmap <CR>
set nocindent set nocindent
endfunc endfunc
" Verify the fix for issue #4908
func Test_listener_undo_line_number()
function DoIt()
" NOP
endfunction
function EchoChanges(bufnr, start, end, added, changes)
call DoIt()
endfunction
new
let lid = listener_add("EchoChanges")
call setline(1, ['a', 'b', 'c'])
set undolevels& " start new undo block
call feedkeys("ggcG\<Esc>", 'xt')
undo
bwipe!
delfunc DoIt
delfunc EchoChanges
call listener_remove(lid)
endfunc

View File

@ -2624,6 +2624,7 @@ u_undoredo(int undo)
linenr_T top, bot; linenr_T top, bot;
linenr_T lnum; linenr_T lnum;
linenr_T newlnum = MAXLNUM; linenr_T newlnum = MAXLNUM;
pos_T new_curpos = curwin->w_cursor;
long i; long i;
u_entry_T *uep, *nuep; u_entry_T *uep, *nuep;
u_entry_T *newlist = NULL; u_entry_T *newlist = NULL;
@ -2667,29 +2668,31 @@ u_undoredo(int undo)
{ {
unblock_autocmds(); unblock_autocmds();
iemsg(_("E438: u_undo: line numbers wrong")); iemsg(_("E438: u_undo: line numbers wrong"));
changed(); /* don't want UNCHANGED now */ changed(); // don't want UNCHANGED now
return; return;
} }
oldsize = bot - top - 1; /* number of lines before undo */ oldsize = bot - top - 1; // number of lines before undo
newsize = uep->ue_size; /* number of lines after undo */ newsize = uep->ue_size; // number of lines after undo
// Decide about the cursor position, depending on what text changed.
// Don't set it yet, it may be invalid if lines are going to be added.
if (top < newlnum) if (top < newlnum)
{ {
/* If the saved cursor is somewhere in this undo block, move it to // If the saved cursor is somewhere in this undo block, move it to
* the remembered position. Makes "gwap" put the cursor back // the remembered position. Makes "gwap" put the cursor back
* where it was. */ // where it was.
lnum = curhead->uh_cursor.lnum; lnum = curhead->uh_cursor.lnum;
if (lnum >= top && lnum <= top + newsize + 1) if (lnum >= top && lnum <= top + newsize + 1)
{ {
curwin->w_cursor = curhead->uh_cursor; new_curpos = curhead->uh_cursor;
newlnum = curwin->w_cursor.lnum - 1; newlnum = new_curpos.lnum - 1;
} }
else else
{ {
/* Use the first line that actually changed. Avoids that // Use the first line that actually changed. Avoids that
* undoing auto-formatting puts the cursor in the previous // undoing auto-formatting puts the cursor in the previous
* line. */ // line.
for (i = 0; i < newsize && i < oldsize; ++i) for (i = 0; i < newsize && i < oldsize; ++i)
{ {
char_u *p = ml_get(top + 1 + i); char_u *p = ml_get(top + 1 + i);
@ -2702,28 +2705,29 @@ u_undoredo(int undo)
if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL) if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
{ {
newlnum = top; newlnum = top;
curwin->w_cursor.lnum = newlnum + 1; new_curpos.lnum = newlnum + 1;
} }
else if (i < newsize) else if (i < newsize)
{ {
newlnum = top + i; newlnum = top + i;
curwin->w_cursor.lnum = newlnum + 1; new_curpos.lnum = newlnum + 1;
} }
} }
} }
empty_buffer = FALSE; empty_buffer = FALSE;
/* delete the lines between top and bot and save them in newarray */ /*
* Delete the lines between top and bot and save them in newarray.
*/
if (oldsize > 0) if (oldsize > 0)
{ {
if ((newarray = U_ALLOC_LINE(sizeof(undoline_T) * oldsize)) == NULL) if ((newarray = U_ALLOC_LINE(sizeof(undoline_T) * oldsize)) == NULL)
{ {
do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize)); do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize));
/*
* We have messed up the entry list, repair is impossible. // We have messed up the entry list, repair is impossible.
* we have to free the rest of the list. // we have to free the rest of the list.
*/
while (uep != NULL) while (uep != NULL)
{ {
nuep = uep->ue_next; nuep = uep->ue_next;
@ -2732,14 +2736,14 @@ u_undoredo(int undo)
} }
break; break;
} }
/* delete backwards, it goes faster in most cases */ // delete backwards, it goes faster in most cases
for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum) for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
{ {
/* what can we do when we run out of memory? */ // what can we do when we run out of memory?
if (u_save_line(&newarray[i], lnum) == FAIL) if (u_save_line(&newarray[i], lnum) == FAIL)
do_outofmem_msg((long_u)0); do_outofmem_msg((long_u)0);
/* remember we deleted the last line in the buffer, and a // remember we deleted the last line in the buffer, and a
* dummy empty line will be inserted */ // dummy empty line will be inserted
if (curbuf->b_ml.ml_line_count == 1) if (curbuf->b_ml.ml_line_count == 1)
empty_buffer = TRUE; empty_buffer = TRUE;
ml_delete(lnum, FALSE); ml_delete(lnum, FALSE);
@ -2748,7 +2752,12 @@ u_undoredo(int undo)
else else
newarray = NULL; newarray = NULL;
/* insert the lines in u_array between top and bot */ // make sure the cursor is on a valid line after the deletions
check_cursor_lnum();
/*
* Insert the lines in u_array between top and bot.
*/
if (newsize) if (newsize)
{ {
for (lnum = top, i = 0; i < newsize; ++i, ++lnum) for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
@ -2766,7 +2775,7 @@ u_undoredo(int undo)
vim_free((char_u *)uep->ue_array); vim_free((char_u *)uep->ue_array);
} }
/* adjust marks */ // adjust marks
if (oldsize != newsize) if (oldsize != newsize)
{ {
mark_adjust(top + 1, top + oldsize, (long)MAXLNUM, mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
@ -2779,7 +2788,7 @@ u_undoredo(int undo)
changed_lines(top + 1, 0, bot, newsize - oldsize); changed_lines(top + 1, 0, bot, newsize - oldsize);
/* set '[ and '] mark */ // set '[ and '] mark
if (top + 1 < curbuf->b_op_start.lnum) if (top + 1 < curbuf->b_op_start.lnum)
curbuf->b_op_start.lnum = top + 1; curbuf->b_op_start.lnum = top + 1;
if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum) if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
@ -2801,6 +2810,10 @@ u_undoredo(int undo)
newlist = uep; newlist = uep;
} }
// Set the cursor to the desired position. Check that the line is valid.
curwin->w_cursor = new_curpos;
check_cursor_lnum();
curhead->uh_entry = newlist; curhead->uh_entry = newlist;
curhead->uh_flags = new_flags; curhead->uh_flags = new_flags;
if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) if ((old_flags & UH_EMPTYBUF) && BUFEMPTY())

View File

@ -757,6 +757,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2008,
/**/ /**/
2007, 2007,
/**/ /**/