Loading ...
Sorry, an error occurred while loading the content.
 

invalid cursor lnum position in del_lines()

Expand Messages
  • Xavier de Gaye
    After deleting the last line of a buffer, the cursor lnum position is invalid and causes the following error message, if a key happens to be pressed while Vim
    Message 1 of 2 , Jul 8, 2009
      After deleting the last line of a buffer, the cursor lnum position is
      invalid and causes the following error message, if a key happens to be
      pressed while Vim has passed control to the gui:

      "E315: ml_get: invalid lnum: 2"

      See below the test case description and the corresponding gdb
      backtrace. This has been tested with Vim 7.2.

      The problem does not occur anymore when changing the code in
      misc1.c:del_lines() at line 2365, to fix curwin->w_cursor.lnum:

      /* If we delete the last line in the file, stop */
      if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
      {
      // 3 lines have been added: fix curwin->w_cursor.lnum
      curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
      if (curwin->w_cursor.lnum == 0)
      curwin->w_cursor.lnum = 1;
      break;
      }
      }
      /* adjust marks, mark the buffer as changed and prepare for displaying */
      deleted_lines_mark(curwin->w_cursor.lnum, n);


      This change does not seem to be the correct one, as
      deleted_lines_mark() expect (as far as I understand) the previous
      value of lnum, not the actual one after the last line is deleted.


      Xavier


      === Test case ===================================================

      The test is run with pyclewn and the 'simple' debugger (a fake
      debugger for testing pyclewn). Two watched variables are defined and
      the user hits continuously the step key. This causes the two following
      lines to be redrawn continuously by pyclewn -> Vim/netbeans in the
      watched variables Vim buffer:

      a ={*} 1
      b ={=} 1

      Each line is changed by a 'remove' followed by an 'insert' or an
      'insert' followed by a 'remove' with an increment of the number, or
      a change from '{*}' to '{=}' or vice-versa.

      The problem occurs within 3 seconds at most, in all tests.


      The following pyclewn log file shows the sequence of netbeans
      functions sent to Vim (and its last lines):

      3:insert/159 0 " a ={*} 2\n"
      3:remove/160 20 20
      3:insert/161 20 " b ={=} 1\n"
      3:remove/162 40 20
      ....
      ....
      3:remove/442 0 20
      3:insert/443 0 " a ={=} 7\n"
      3:remove/444 20 20


      The following Vim/netbeans log file shows the last lines of the
      corresponding processing done by Vim:

      FUN 442: (3) remove 0 20
      FIRST POS: line 1, col 0
      LAST POS: line 1, col 19
      No sign on line 1
      Deleting lines 1 through 1
      REP 442: <none>
      FUN 443: (3) insert <text>
      REP 443: <none>
      FUN 444: (3) remove 20 20
      FIRST POS: line 2, col 0
      LAST POS: line 2, col 19
      No sign on line 2
      Deleting lines 2 through 2

      A breakpoint has been set in memline.c:ml_get_buf() at line 2087 where
      the error message is printed. Vim stops while processing netbeans
      function 444 (see above) and the corresponding backtrace is below:

      * at frame #43, del_lines has deleted the last line, line 2, but
      lnum is still equal to 2
      * control is given back to the gui at frame #34 in
      gtk_main_iteration_do
      * a key is pressed at frame #21
      * gui gives back control to Vim at frame #3 in im_commit_cb
      * lnum=2 is invalid in ml_get_buf at frame #0

      === gdb backtrace ===============================================

      Breakpoint 1, ml_get_buf (buf=0x8336ee8, lnum=2, will_change=0) at
      memline.c:2087
      (gdb) where
      #0 ml_get_buf (buf=0x8336ee8, lnum=2, will_change=0) at memline.c:2087
      #1 0x0805c9c6 in getvcol (wp=0x838e600, pos=0x838e610,
      start=0x81f06f0, cursor=0x0, end=0x0) at charset.c:1246
      #2 0x0810d04b in init_preedit_start_col () at mbyte.c:3442
      #3 0x0810d341 in im_commit_cb (context=0x82d4550, str=0x839d3f0 "S",
      data=0x0) at mbyte.c:3609
      #4 0xb7ade5eb in g_cclosure_marshal_VOID__STRING () from
      /usr/lib/libgobject-2.0.so.0
      #5 0xb7ad198b in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
      #6 0xb7ae1f2d in g_signal_chain_from_overridden () from
      /usr/lib/libgobject-2.0.so.0
      #7 0xb7ae3429 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
      #8 0xb7ae61ce in g_signal_emit_by_name () from /usr/lib/libgobject-2.0.so.0
      #9 0xb7dc624e in gtk_im_multicontext_append_menuitems () from
      /usr/lib/libgtk-x11-2.0.so.0
      #10 0xb7ade5eb in g_cclosure_marshal_VOID__STRING () from
      /usr/lib/libgobject-2.0.so.0
      #11 0xb7ad198b in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
      #12 0xb7ae1f2d in g_signal_chain_from_overridden () from
      /usr/lib/libgobject-2.0.so.0
      #13 0xb7ae3429 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
      #14 0xb7ae61ce in g_signal_emit_by_name () from /usr/lib/libgobject-2.0.so.0
      #15 0xb7dc4c45 in gtk_im_context_simple_new () from /usr/lib/libgtk-x11-2.0.so.0
      #16 0xb7dc52c8 in gtk_im_context_simple_new () from /usr/lib/libgtk-x11-2.0.so.0
      #17 0xb7dc4273 in gtk_im_context_filter_keypress () from
      /usr/lib/libgtk-x11-2.0.so.0
      #18 0xb7dc6c35 in gtk_im_multicontext_new () from /usr/lib/libgtk-x11-2.0.so.0
      #19 0xb7dc4273 in gtk_im_context_filter_keypress () from
      /usr/lib/libgtk-x11-2.0.so.0
      #20 0x0810e1c8 in xim_queue_key_press_event (event=0x8334c60, down=1)
      at mbyte.c:4248
      #21 0x081b0a33 in key_press_event (widget=0x82ce818, event=0x8334c60,
      data=0x0) at gui_gtk_x11.c:1002
      #22 0xb7de5250 in _gtk_marshal_BOOLEAN__BOXED () from
      /usr/lib/libgtk-x11-2.0.so.0
      #23 0xb7ad198b in g_closure_invoke () from /usr/lib/libgobject-2.0.so.0
      #24 0xb7ae1f2d in g_signal_chain_from_overridden () from
      /usr/lib/libgobject-2.0.so.0
      #25 0xb7ae3208 in g_signal_emit_valist () from /usr/lib/libgobject-2.0.so.0
      #26 0xb7ae35d9 in g_signal_emit () from /usr/lib/libgobject-2.0.so.0
      #27 0xb7ecef64 in gtk_widget_get_default_style () from
      /usr/lib/libgtk-x11-2.0.so.0
      #28 0xb7ddec0a in gtk_propagate_event () from /usr/lib/libgtk-x11-2.0.so.0
      #29 0xb7ddfe07 in gtk_main_do_event () from /usr/lib/libgtk-x11-2.0.so.0
      #30 0xb7c78eea in _gdk_events_init () from /usr/lib/libgdk-x11-2.0.so.0
      #31 0xb7a5e731 in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
      #32 0xb7a617a6 in g_main_context_check () from /usr/lib/libglib-2.0.so.0
      #33 0xb7a61d27 in g_main_context_iteration () from /usr/lib/libglib-2.0.so.0
      #34 0xb7de0073 in gtk_main_iteration_do () from /usr/lib/libgtk-x11-2.0.so.0
      #35 0x081b70bc in gui_mch_update () at gui_gtk_x11.c:6384
      #36 0x08196202 in ui_breakcheck () at ui.c:365
      #37 0x080e14dc in mf_sync (mfp=0x8382190, flags=8) at memfile.c:607
      #38 0x080e87d8 in ml_setflags (buf=0x8336ee8) at memline.c:4439
      #39 0x080f680e in changed () at misc1.c:2517
      #40 0x080f6c15 in changed_common (lnum=2, col=0, lnume=3, xtra=-1) at
      misc1.c:2732
      #41 0x080f6b57 in changed_lines (lnum=2, col=0, lnume=3, xtra=-1) at
      misc1.c:2680
      #42 0x080f6a60 in deleted_lines_mark (lnum=2, count=1) at misc1.c:2636
      #43 0x080f65f8 in del_lines (nlines=1, undo=0) at misc1.c:2374
      #44 0x081bdb35 in nb_do_cmd (bufno=3, cmd=0x83972a2 "remove", func=1,
      cmdno=444, args=0x83972b2 "") at netbeans.c:1591
      #45 0x081bc438 in nb_parse_cmd (cmd=0x83972a0 "3:remove") at netbeans.c:866
      #46 0x081bc0cf in netbeans_parse_messages () at netbeans.c:680
      #47 0x081b716d in gui_mch_wait_for_chars (wtime=3600000) at gui_gtk_x11.c:6483
      #48 0x081a81f0 in gui_wait_for_chars (wtime=-1) at gui.c:2692
      #49 0x08196027 in ui_inchar (buf=0x838838f "", maxlen=82, wtime=-1,
      tb_change_cnt=1313) at ui.c:184
      #50 0x080cd1f7 in inchar (buf=0x838838f "", maxlen=248, wait_time=-1,
      tb_change_cnt=1313) at getchar.c:2959
      #51 0x080cce6c in vgetorpeek (advance=1) at getchar.c:2735
      #52 0x080cb47e in vgetc () at getchar.c:1552
      #53 0x080cb9db in safe_vgetc () at getchar.c:1757
      #54 0x0810ed00 in normal_cmd (oap=0xbfbfd440, toplevel=1) at normal.c:643
      #55 0x080d8f53 in main_loop (cmdwin=0, noexmode=0) at main.c:1179
      #56 0x080d8ab1 in main (argc=8, argv=0xbfbfd634) at main.c:938

      =====================================================================

      --~--~---------~--~----~------------~-------~--~----~
      You received this message from the "vim_dev" maillist.
      For more information, visit http://www.vim.org/maillist.php
      -~----------~----~----~----~------~----~------~--~---
    • Bram Moolenaar
      ... The stack trace helped pinpointing the problem. Adjusting the marks takes a while, and displaying the cursor is triggered there. Thus we need to adjust
      Message 2 of 2 , Jul 9, 2009
        Xavier de Gaye wrote:

        > After deleting the last line of a buffer, the cursor lnum position is
        > invalid and causes the following error message, if a key happens to be
        > pressed while Vim has passed control to the gui:
        >
        > "E315: ml_get: invalid lnum: 2"
        >
        > See below the test case description and the corresponding gdb
        > backtrace. This has been tested with Vim 7.2.
        >
        > The problem does not occur anymore when changing the code in
        > misc1.c:del_lines() at line 2365, to fix curwin->w_cursor.lnum:
        >
        > /* If we delete the last line in the file, stop */
        > if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
        > {
        > // 3 lines have been added: fix curwin->w_cursor.lnum
        > curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
        > if (curwin->w_cursor.lnum == 0)
        > curwin->w_cursor.lnum = 1;
        > break;
        > }
        > }
        > /* adjust marks, mark the buffer as changed and prepare for displaying */
        > deleted_lines_mark(curwin->w_cursor.lnum, n);
        >
        >
        > This change does not seem to be the correct one, as
        > deleted_lines_mark() expect (as far as I understand) the previous
        > value of lnum, not the actual one after the last line is deleted.

        The stack trace helped pinpointing the problem. Adjusting the marks
        takes a while, and displaying the cursor is triggered there. Thus we
        need to adjust the cursor position before calling deleted_lines_mark().

        This patch should fix it:


        *** ../vim-7.2.222/src/misc1.c 2009-06-24 16:25:23.000000000 +0200
        --- src/misc1.c 2009-07-09 12:50:24.000000000 +0200
        ***************
        *** 2345,2356 ****
        int undo; /* if TRUE, prepare for undo */
        {
        long n;

        if (nlines <= 0)
        return;

        /* save the deleted lines for undo */
        ! if (undo && u_savedel(curwin->w_cursor.lnum, nlines) == FAIL)
        return;

        for (n = 0; n < nlines; )
        --- 2345,2357 ----
        int undo; /* if TRUE, prepare for undo */
        {
        long n;
        + linenr_T first = curwin->w_cursor.lnum;

        if (nlines <= 0)
        return;

        /* save the deleted lines for undo */
        ! if (undo && u_savedel(first, nlines) == FAIL)
        return;

        for (n = 0; n < nlines; )
        ***************
        *** 2358,2375 ****
        if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
        break;

        ! ml_delete(curwin->w_cursor.lnum, TRUE);
        ++n;

        /* If we delete the last line in the file, stop */
        ! if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
        break;
        }
        - /* adjust marks, mark the buffer as changed and prepare for displaying */
        - deleted_lines_mark(curwin->w_cursor.lnum, n);

        curwin->w_cursor.col = 0;
        check_cursor_lnum();
        }

        int
        --- 2359,2379 ----
        if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
        break;

        ! ml_delete(first, TRUE);
        ++n;

        /* If we delete the last line in the file, stop */
        ! if (first > curbuf->b_ml.ml_line_count)
        break;
        }

        + /* Correct the cursor position before calling deleted_lines_mark(), it may
        + * trigger a callback to display the cursor. */
        curwin->w_cursor.col = 0;
        check_cursor_lnum();
        +
        + /* adjust marks, mark the buffer as changed and prepare for displaying */
        + deleted_lines_mark(first, n);
        }

        int


        --
        hundred-and-one symptoms of being an internet addict:
        72. Somebody at IRC just mentioned a way to obtain full motion video without
        a PC using a wireless protocol called NTSC, you wonder how you never
        heard about it

        /// Bram Moolenaar -- Bram@... -- http://www.Moolenaar.net \\\
        /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
        \\\ download, build and distribute -- http://www.A-A-P.org ///
        \\\ help me help AIDS victims -- http://ICCF-Holland.org ///

        --~--~---------~--~----~------------~-------~--~----~
        You received this message from the "vim_dev" maillist.
        For more information, visit http://www.vim.org/maillist.php
        -~----------~----~----~----~------~----~------~--~---
      Your message has been successfully submitted and would be delivered to recipients shortly.