To: vim_dev@googlegroups.com Subject: Patch 8.1.2008 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 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\", '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 -- Bram@Moolenaar.net -- 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 ///