]> granicus.if.org Git - vim/commitdiff
Make joining a range of lines much faster. (Milan Vancura)
authorBram Moolenaar <Bram@vim.org>
Sat, 10 Jul 2010 15:51:46 +0000 (17:51 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 10 Jul 2010 15:51:46 +0000 (17:51 +0200)
runtime/doc/todo.txt
src/edit.c
src/ex_docmd.c
src/normal.c
src/ops.c
src/proto/ops.pro

index b08d40e367b232ca5800b86d81058579c6dad4ae..a09a44d66b23948e63f3a3d03b68d88a42e786c6 100644 (file)
@@ -1093,9 +1093,6 @@ Vim 7.3:
 - Conceal feature: no update when moving to another window. (Dominique Pelle,
   2010 Jul 5)  Vince will look into it.
 Patches to include:
-- Make do_do_join() work faster.  Could at least do a binary search way of
-  doing this.  Hint from Dominique Pelle, Dec 10; Lee Naish, Dec 11.
-  Patch from Milan Vancura, 2010 May 16.
 - Patch for Lisp support with ECL (Mikael Jansson, 2008 Oct 25)
 - Gvimext patch to support wide file names. (Szabolcs Horvat 2008 Sep 10)
 - Patch to support netbeans for Mac. (Kazuki Sakamoto, 2009 Jun 25)
index f465f20177a0c66d61505fa480146a537ad192f4..3ee76553c44531fdbd475ab65b2d13cdceba5428 100644 (file)
@@ -8366,9 +8366,7 @@ ins_del()
     {
        temp = curwin->w_cursor.col;
        if (!can_bs(BS_EOL)             /* only if "eol" included */
-               || u_save((linenr_T)(curwin->w_cursor.lnum - 1),
-                   (linenr_T)(curwin->w_cursor.lnum + 2)) == FAIL
-               || do_join(FALSE) == FAIL)
+               || do_join(2, FALSE, TRUE) == FAIL)
            vim_beep();
        else
            curwin->w_cursor.col = temp;
@@ -8549,7 +8547,7 @@ ins_bs(c, mode, inserted_space_p)
                        ptr[len - 1] = NUL;
                }
 
-               (void)do_join(FALSE);
+               (void)do_join(2, FALSE, FALSE);
                if (temp == NUL && gchar_cursor() != NUL)
                    inc_cursor();
            }
index d165305e052b78879250694f794017084c6f76c7..1105a87695b96dc9e4067ec286fb7707214ca50f 100644 (file)
@@ -8394,7 +8394,7 @@ ex_join(eap)
        }
        ++eap->line2;
     }
-    do_do_join(eap->line2 - eap->line1 + 1, !eap->forceit);
+    (void)do_join(eap->line2 - eap->line1 + 1, !eap->forceit, TRUE);
     beginline(BL_WHITE | BL_FIX);
     ex_may_print(eap);
 }
index efcdb7992929e49b029c9cf5522315667a9eb60c..3321b3ff83f6b26e08fa33c8c87c4ca36ed378b9 100644 (file)
@@ -1908,7 +1908,7 @@ do_pending_operator(cap, old_col, gui_yank)
                beep_flush();
            else
            {
-               do_do_join(oap->line_count, oap->op_type == OP_JOIN);
+               (void)do_join(oap->line_count, oap->op_type == OP_JOIN, TRUE);
                auto_format(FALSE, TRUE);
            }
            break;
@@ -9111,7 +9111,7 @@ nv_join(cap)
        {
            prep_redo(cap->oap->regname, cap->count0,
                         NUL, cap->cmdchar, NUL, NUL, cap->nchar);
-           do_do_join(cap->count0, cap->nchar == NUL);
+           (void)do_join(cap->count0, cap->nchar == NUL, TRUE);
        }
     }
 }
index f8cde3b13e43ffcef151578f8ccd59526e327b15..cf83a884a7775ff408b8080ee871a96be7a60b00 100644 (file)
--- a/src/ops.c
+++ b/src/ops.c
@@ -1949,7 +1949,7 @@ op_delete(oap)
                                                            );
            curwin->w_cursor = curpos;          /* restore curwin->w_cursor */
 
-           (void)do_join(FALSE);
+           (void)do_join(2, FALSE, FALSE);
        }
     }
 
@@ -4140,138 +4140,137 @@ dis_msg(p, skip_esc)
 }
 
 /*
- * join 'count' lines (minimal 2), including u_save()
- */
-    void
-do_do_join(count, insert_space)
-    long    count;
-    int            insert_space;
-{
-    colnr_T    col = MAXCOL;
-
-    if (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
-                   (linenr_T)(curwin->w_cursor.lnum + count)) == FAIL)
-       return;
-
-    while (--count > 0)
-    {
-       line_breakcheck();
-       if (got_int || do_join(insert_space) == FAIL)
-       {
-           beep_flush();
-           break;
-       }
-       if (col == MAXCOL && vim_strchr(p_cpo, CPO_JOINCOL) != NULL)
-           col = curwin->w_cursor.col;
-    }
-
-    /* Vi compatible: use the column of the first join */
-    if (col != MAXCOL && vim_strchr(p_cpo, CPO_JOINCOL) != NULL)
-       curwin->w_cursor.col = col;
-
-#if 0
-    /*
-     * Need to update the screen if the line where the cursor is became too
-     * long to fit on the screen.
-     */
-    update_topline_redraw();
-#endif
-}
-
-/*
- * Join two lines at the cursor position.
- * "redraw" is TRUE when the screen should be updated.
- * Caller must have setup for undo.
+ * Join 'count' lines (minimal 2) at cursor position.
+ * When "save_undo" is TRUE save lines for undo first.
  *
  * return FAIL for failure, OK otherwise
  */
     int
-do_join(insert_space)
-    int                insert_space;
+do_join(count, insert_space, save_undo)
+    long    count;
+    int            insert_space;
+    int            save_undo;
 {
-    char_u     *curr;
-    char_u     *next, *next_start;
+    char_u     *curr = NULL;
+    char_u     *cend;
     char_u     *newp;
+    char_u     *spaces;        /* number of spaces inserte before a line */
     int                endcurr1, endcurr2;
-    int                currsize;       /* size of the current line */
-    int                nextsize;       /* size of the next line */
-    int                spaces;         /* number of spaces to insert */
+    int                currsize = 0;   /* size of the current line */
+    int                sumsize = 0;    /* size of the long new line */
     linenr_T   t;
+    colnr_T    col = 0;
+    int                ret = OK;
 
-    if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
-       return FAIL;            /* can't join on last line */
+    if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
+                           (linenr_T)(curwin->w_cursor.lnum + count)) == FAIL)
+       return FAIL;
+
+    /* Allocate an array to store the number of spaces inserted before each
+     * line.  We will use it to pre-compute the length of the new line and the
+     * proper placement of each original line in the new one. */
+    spaces = lalloc_clear((long_u)count, TRUE);
+    if (spaces == NULL)
+       return FAIL;
 
-    curr = ml_get_curline();
-    currsize = (int)STRLEN(curr);
-    endcurr1 = endcurr2 = NUL;
-    if (insert_space && currsize > 0)
+    /*
+     * Don't move anything, just compute the final line length
+     * and setup the array of space strings lengths
+     */
+    for (t = 0; t < count; ++t)
     {
-#ifdef FEAT_MBYTE
-       if (has_mbyte)
+       curr = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
+       if (insert_space && t > 0)
        {
-           next = curr + currsize;
-           mb_ptr_back(curr, next);
-           endcurr1 = (*mb_ptr2char)(next);
-           if (next > curr)
+           curr = skipwhite(curr);
+           if (*curr != ')' && currsize != 0 && endcurr1 != TAB
+#ifdef FEAT_MBYTE
+                   && (!has_format_option(FO_MBYTE_JOIN)
+                       || (mb_ptr2char(curr) < 0x100 && endcurr1 < 0x100))
+                   && (!has_format_option(FO_MBYTE_JOIN2)
+                       || mb_ptr2char(curr) < 0x100 || endcurr1 < 0x100)
+#endif
+              )
            {
-               mb_ptr_back(curr, next);
-               endcurr2 = (*mb_ptr2char)(next);
+               /* don't add a space if the line is ending in a space */
+               if (endcurr1 == ' ')
+                   endcurr1 = endcurr2;
+               else
+                   ++spaces[t];
+               /* extra space when 'joinspaces' set and line ends in '.' */
+               if (       p_js
+                       && (endcurr1 == '.'
+                           || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
+                               && (endcurr1 == '?' || endcurr1 == '!'))))
+                   ++spaces[t];
            }
        }
-       else
-#endif
+       currsize = (int)STRLEN(curr);
+       sumsize += currsize + spaces[t];
+       endcurr1 = endcurr2 = NUL;
+       if (insert_space && currsize > 0)
        {
-           endcurr1 = *(curr + currsize - 1);
-           if (currsize > 1)
-               endcurr2 = *(curr + currsize - 2);
-       }
-    }
-
-    next = next_start = ml_get((linenr_T)(curwin->w_cursor.lnum + 1));
-    spaces = 0;
-    if (insert_space)
-    {
-       next = skipwhite(next);
-       if (*next != ')' && currsize != 0 && endcurr1 != TAB
 #ifdef FEAT_MBYTE
-               && (!has_format_option(FO_MBYTE_JOIN)
-                       || (mb_ptr2char(next) < 0x100 && endcurr1 < 0x100))
-               && (!has_format_option(FO_MBYTE_JOIN2)
-                       || mb_ptr2char(next) < 0x100 || endcurr1 < 0x100)
+           if (has_mbyte)
+           {
+               cend = curr + currsize;
+               mb_ptr_back(curr, cend);
+               endcurr1 = (*mb_ptr2char)(cend);
+               if (cend > curr)
+               {
+                   mb_ptr_back(curr, cend);
+                   endcurr2 = (*mb_ptr2char)(cend);
+               }
+           }
+           else
 #endif
-               )
+           {
+               endcurr1 = *(curr + currsize - 1);
+               if (currsize > 1)
+                   endcurr2 = *(curr + currsize - 2);
+           }
+       }
+       line_breakcheck();
+       if (got_int)
        {
-           /* don't add a space if the line is ending in a space */
-           if (endcurr1 == ' ')
-               endcurr1 = endcurr2;
-           else
-               ++spaces;
-           /* extra space when 'joinspaces' set and line ends in '.' */
-           if (       p_js
-                   && (endcurr1 == '.'
-                       || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
-                           && (endcurr1 == '?' || endcurr1 == '!'))))
-               ++spaces;
+           ret = FAIL;
+           goto theend;
        }
     }
-    nextsize = (int)STRLEN(next);
 
-    newp = alloc_check((unsigned)(currsize + nextsize + spaces + 1));
-    if (newp == NULL)
-       return FAIL;
+    /* store the column position before last line */
+    col = sumsize - currsize - spaces[count - 1];
+
+    /* allocate the space for the new line */
+    newp = alloc_check((unsigned)(sumsize + 1));
+    cend = newp + sumsize;
+    *cend = 0;
 
     /*
-     * Insert the next line first, because we already have that pointer.
-     * Curr has to be obtained again, because getting next will have
-     * invalidated it.
+     * Move affected lines to the new long one.
+     *
+     * Move marks from each deleted line to the joined line, adjusting the
+     * column.  This is not Vi compatible, but Vi deletes the marks, thus that
+     * should not really be a problem.
      */
-    mch_memmove(newp + currsize + spaces, next, (size_t)(nextsize + 1));
-
-    curr = ml_get_curline();
-    mch_memmove(newp, curr, (size_t)currsize);
-
-    copy_spaces(newp + currsize, (size_t)spaces);
-
+    for (t = count - 1; ; --t)
+    {
+       cend -= currsize;
+       mch_memmove(cend, curr, (size_t)currsize);
+       if (spaces[t] > 0)
+       {
+           cend -= spaces[t];
+           copy_spaces(cend, (size_t)(spaces[t]));
+       }
+       mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
+                                (long)(cend - newp + spaces[t]));
+       if (t == 0)
+           break;
+       curr = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
+       if (insert_space && t > 1)
+           curr = skipwhite(curr);
+       currsize = (int)STRLEN(curr);
+    }
     ml_replace(curwin->w_cursor.lnum, newp, FALSE);
 
     /* Only report the change in the first line here, del_lines() will report
@@ -4280,32 +4279,32 @@ do_join(insert_space)
                                               curwin->w_cursor.lnum + 1, 0L);
 
     /*
-     * Delete the following line. To do this we move the cursor there
+     * Delete following lines. To do this we move the cursor there
      * briefly, and then move it back. After del_lines() the cursor may
      * have moved up (last line deleted), so the current lnum is kept in t.
-     *
-     * Move marks from the deleted line to the joined line, adjusting the
-     * column.  This is not Vi compatible, but Vi deletes the marks, thus that
-     * should not really be a problem.
      */
     t = curwin->w_cursor.lnum;
-    mark_col_adjust(t + 1, (colnr_T)0, (linenr_T)-1,
-                            (long)(currsize + spaces - (next - next_start)));
     ++curwin->w_cursor.lnum;
-    del_lines(1L, FALSE);
+    del_lines(count - 1, FALSE);
     curwin->w_cursor.lnum = t;
 
     /*
-     * go to first character of the joined line
+     * Set the cursor column:
+     * Vi compatible: use the column of the first join
+     * vim:           use the column of the last join
      */
-    curwin->w_cursor.col = currsize;
+    curwin->w_cursor.col =
+                   (vim_strchr(p_cpo, CPO_JOINCOL) != NULL ? currsize : col);
     check_cursor_col();
+
 #ifdef FEAT_VIRTUALEDIT
     curwin->w_cursor.coladd = 0;
 #endif
     curwin->w_set_curswant = TRUE;
 
-    return OK;
+theend:
+    vim_free(spaces);
+    return ret;
 }
 
 #ifdef FEAT_COMMENTS
@@ -4718,7 +4717,7 @@ format_lines(line_count, avoid_fex)
                curwin->w_cursor.lnum++;
                curwin->w_cursor.col = 0;
                if (line_count < 0 && u_save_cursor() == FAIL)
-                       break;
+                   break;
 #ifdef FEAT_COMMENTS
                (void)del_bytes((long)next_leader_len, FALSE, FALSE);
                if (next_leader_len > 0)
@@ -4726,7 +4725,7 @@ format_lines(line_count, avoid_fex)
                                                      (long)-next_leader_len);
 #endif
                curwin->w_cursor.lnum--;
-               if (do_join(TRUE) == FAIL)
+               if (do_join(2, TRUE, FALSE) == FAIL)
                {
                    beep_flush();
                    break;
index 37c319414ff29f50acd082905629ca802a52fae4..d07c260dc21e9b4bb1dcb93c29facdc76139be3a 100644 (file)
@@ -36,8 +36,7 @@ void adjust_cursor_eol __ARGS((void));
 int preprocs_left __ARGS((void));
 int get_register_name __ARGS((int num));
 void ex_display __ARGS((exarg_T *eap));
-void do_do_join __ARGS((long count, int insert_space));
-int do_join __ARGS((int insert_space));
+int do_join __ARGS((long count, int insert_space, int save_undo));
 void op_format __ARGS((oparg_T *oap, int keep_cursor));
 void op_formatexpr __ARGS((oparg_T *oap));
 int fex_format __ARGS((linenr_T lnum, long count, int c));