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:
@ -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
|
||||||
|
|||||||
63
src/undo.c
63
src/undo.c
@ -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())
|
||||||
|
|||||||
@ -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,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
Reference in New Issue
Block a user