Thanks very much for this.

On Sun, 8 Sep 2019 at 14:28, Bram Moolenaar <[email protected]> wrote:
>
>
> 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.

-- 
-- 
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/CACoUkn7sOo7oe1cugBqs9mz1_mM5QuHUC4Rj9UY4rMXXJPJkuQ%40mail.gmail.com.

Raspunde prin e-mail lui