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.

Raspunde prin e-mail lui