From 893eaab41fdfc2c7adc1d3ee23b41a86d335c515 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Jul 2010 17:51:46 +0200 Subject: Make joining a range of lines much faster. (Milan Vancura) --- src/edit.c | 6 +- src/ex_docmd.c | 2 +- src/normal.c | 4 +- src/ops.c | 241 +++++++++++++++++++++++++++--------------------------- src/proto/ops.pro | 3 +- 5 files changed, 126 insertions(+), 130 deletions(-) (limited to 'src') diff --git a/src/edit.c b/src/edit.c index f465f20177..3ee76553c4 100644 --- a/src/edit.c +++ b/src/edit.c @@ -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(); } diff --git a/src/ex_docmd.c b/src/ex_docmd.c index d165305e05..1105a87695 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -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); } diff --git a/src/normal.c b/src/normal.c index efcdb79929..3321b3ff83 100644 --- a/src/normal.c +++ b/src/normal.c @@ -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); } } } diff --git a/src/ops.c b/src/ops.c index f8cde3b13e..cf83a884a7 100644 --- 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; diff --git a/src/proto/ops.pro b/src/proto/ops.pro index 37c319414f..d07c260dc2 100644 --- a/src/proto/ops.pro +++ b/src/proto/ops.pro @@ -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)); -- cgit v1.2.3