Patch 8.1.2008
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)
Files: src/undo.c, src/testdir/test_listener.vim
*** ../vim-8.1.2007/src/undo.c 2019-09-04 17:48:11.712877356 +0200
--- src/undo.c 2019-09-08 15:20:23.437532804 +0200
***************
*** 2624,2629 ****
--- 2624,2630 ----
linenr_T top, bot;
linenr_T lnum;
linenr_T newlnum = MAXLNUM;
+ pos_T new_curpos = curwin->w_cursor;
long i;
u_entry_T *uep, *nuep;
u_entry_T *newlist = NULL;
***************
*** 2667,2695 ****
{
unblock_autocmds();
iemsg(_("E438: u_undo: line numbers wrong"));
! changed(); /* don't want UNCHANGED now */
return;
}
! oldsize = bot - top - 1; /* number of lines before undo */
! newsize = uep->ue_size; /* number of lines after undo */
if (top < newlnum)
{
! /* If the saved cursor is somewhere in this undo block, move it to
! * the remembered position. Makes "gwap" put the cursor back
! * where it was. */
lnum = curhead->uh_cursor.lnum;
if (lnum >= top && lnum <= top + newsize + 1)
{
! curwin->w_cursor = curhead->uh_cursor;
! newlnum = curwin->w_cursor.lnum - 1;
}
else
{
! /* Use the first line that actually changed. Avoids that
! * undoing auto-formatting puts the cursor in the previous
! * line. */
for (i = 0; i < newsize && i < oldsize; ++i)
{
char_u *p = ml_get(top + 1 + i);
--- 2668,2698 ----
{
unblock_autocmds();
iemsg(_("E438: u_undo: line numbers wrong"));
! changed(); // don't want UNCHANGED now
return;
}
! oldsize = bot - top - 1; // number of lines before 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 the saved cursor is somewhere in this undo block, move it to
! // the remembered position. Makes "gwap" put the cursor back
! // where it was.
lnum = curhead->uh_cursor.lnum;
if (lnum >= top && lnum <= top + newsize + 1)
{
! new_curpos = curhead->uh_cursor;
! newlnum = new_curpos.lnum - 1;
}
else
{
! // Use the first line that actually changed. Avoids that
! // undoing auto-formatting puts the cursor in the previous
! // line.
for (i = 0; i < newsize && i < oldsize; ++i)
{
char_u *p = ml_get(top + 1 + i);
***************
*** 2702,2729 ****
if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
{
newlnum = top;
! curwin->w_cursor.lnum = newlnum + 1;
}
else if (i < newsize)
{
newlnum = top + i;
! curwin->w_cursor.lnum = newlnum + 1;
}
}
}
empty_buffer = FALSE;
! /* delete the lines between top and bot and save them in newarray */
if (oldsize > 0)
{
if ((newarray = U_ALLOC_LINE(sizeof(undoline_T) * oldsize)) == NULL)
{
do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize));
! /*
! * We have messed up the entry list, repair is impossible.
! * we have to free the rest of the list.
! */
while (uep != NULL)
{
nuep = uep->ue_next;
--- 2705,2733 ----
if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL)
{
newlnum = top;
! new_curpos.lnum = newlnum + 1;
}
else if (i < newsize)
{
newlnum = top + i;
! new_curpos.lnum = newlnum + 1;
}
}
}
empty_buffer = FALSE;
! /*
! * Delete the lines between top and bot and save them in newarray.
! */
if (oldsize > 0)
{
if ((newarray = U_ALLOC_LINE(sizeof(undoline_T) * oldsize)) == NULL)
{
do_outofmem_msg((long_u)(sizeof(undoline_T) * oldsize));
!
! // We have messed up the entry list, repair is impossible.
! // we have to free the rest of the list.
while (uep != NULL)
{
nuep = uep->ue_next;
***************
*** 2732,2745 ****
}
break;
}
! /* delete backwards, it goes faster in most cases */
for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
{
! /* what can we do when we run out of memory? */
if (u_save_line(&newarray[i], lnum) == FAIL)
do_outofmem_msg((long_u)0);
! /* remember we deleted the last line in the buffer, and a
! * dummy empty line will be inserted */
if (curbuf->b_ml.ml_line_count == 1)
empty_buffer = TRUE;
ml_delete(lnum, FALSE);
--- 2736,2749 ----
}
break;
}
! // delete backwards, it goes faster in most cases
for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
{
! // what can we do when we run out of memory?
if (u_save_line(&newarray[i], lnum) == FAIL)
do_outofmem_msg((long_u)0);
! // remember we deleted the last line in the buffer, and a
! // dummy empty line will be inserted
if (curbuf->b_ml.ml_line_count == 1)
empty_buffer = TRUE;
ml_delete(lnum, FALSE);
***************
*** 2748,2754 ****
else
newarray = NULL;
! /* insert the lines in u_array between top and bot */
if (newsize)
{
for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
--- 2752,2763 ----
else
newarray = NULL;
! // 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)
{
for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
***************
*** 2766,2772 ****
vim_free((char_u *)uep->ue_array);
}
! /* adjust marks */
if (oldsize != newsize)
{
mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
--- 2775,2781 ----
vim_free((char_u *)uep->ue_array);
}
! // adjust marks
if (oldsize != newsize)
{
mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
***************
*** 2779,2785 ****
changed_lines(top + 1, 0, bot, newsize - oldsize);
! /* set '[ and '] mark */
if (top + 1 < curbuf->b_op_start.lnum)
curbuf->b_op_start.lnum = top + 1;
if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
--- 2788,2794 ----
changed_lines(top + 1, 0, bot, newsize - oldsize);
! // set '[ and '] mark
if (top + 1 < curbuf->b_op_start.lnum)
curbuf->b_op_start.lnum = top + 1;
if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
***************
*** 2801,2806 ****
--- 2810,2819 ----
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_flags = new_flags;
if ((old_flags & UH_EMPTYBUF) && BUFEMPTY())
*** ../vim-8.1.2007/src/testdir/test_listener.vim 2019-08-31
22:16:30.774127008 +0200
--- src/testdir/test_listener.vim 2019-09-08 15:08:56.524062392 +0200
***************
*** 234,240 ****
new
let id = listener_add(function('MyListener', [{}]), bufnr(''))
call test_garbagecollect_now()
! " must not crach caused by invalid memory access
normal ia
call assert_true(v:true)
--- 234,240 ----
new
let id = listener_add(function('MyListener', [{}]), bufnr(''))
call test_garbagecollect_now()
! " must not crash caused by invalid memory access
normal ia
call assert_true(v:true)
***************
*** 268,270 ****
--- 268,292 ----
iunmap <CR>
set nocindent
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
*** ../vim-8.1.2007/src/version.c 2019-09-08 14:07:43.104866704 +0200
--- src/version.c 2019-09-08 15:25:54.516311353 +0200
***************
*** 759,760 ****
--- 759,762 ----
{ /* Add new patch number below this line */
+ /**/
+ 2008,
/**/
--
Don't be humble ... you're not that great.
-- Golda Meir
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/201909081328.x88DS5nX006278%40masaka.moolenaar.net.