diff options
Diffstat (limited to 'src/nvim/edit.c')
-rw-r--r-- | src/nvim/edit.c | 259 |
1 files changed, 107 insertions, 152 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 220b92d099..f06dc124f0 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -18,6 +18,7 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" @@ -135,6 +136,8 @@ static TriState dont_sync_undo = kFalse; // CTRL-G U prevents syncing undo static linenr_T o_lnum = 0; +static kvec_t(char) replace_stack = KV_INITIAL_VALUE; + static void insert_enter(InsertState *s) { s->did_backspace = true; @@ -270,11 +273,7 @@ static void insert_enter(InsertState *s) if (restart_edit != 0 && stuff_empty()) { // After a paste we consider text typed to be part of the insert for // the pasted text. You can backspace over the pasted text too. - if (where_paste_started.lnum) { - arrow_used = false; - } else { - arrow_used = true; - } + arrow_used = where_paste_started.lnum == 0; restart_edit = 0; // If the cursor was after the end-of-line before the CTRL-O and it is @@ -467,13 +466,15 @@ static int insert_check(VimState *state) && !curwin->w_p_sms && !s->did_backspace && curwin->w_topline == s->old_topline - && curwin->w_topfill == s->old_topfill) { + && curwin->w_topfill == s->old_topfill + && s->count <= 1) { s->mincol = curwin->w_wcol; validate_cursor_col(curwin); if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, - curbuf->b_p_vts_array) + curbuf->b_p_vts_array, + false) && curwin->w_wrow == curwin->w_height_inner - 1 - get_scrolloff_value(curwin) && (curwin->w_cursor.lnum != curwin->w_topline || curwin->w_topfill > 0)) { @@ -488,11 +489,15 @@ static int insert_check(VimState *state) } // May need to adjust w_topline to show the cursor. - update_topline(curwin); + if (s->count <= 1) { + update_topline(curwin); + } s->did_backspace = false; - validate_cursor(curwin); // may set must_redraw + if (s->count <= 1) { + validate_cursor(curwin); // may set must_redraw + } // Redraw the display when no characters are waiting. // Also shows mode, ruler and positions cursor. @@ -506,7 +511,9 @@ static int insert_check(VimState *state) do_check_cursorbind(); } - update_curswant(); + if (s->count <= 1) { + update_curswant(); + } s->old_topline = curwin->w_topline; s->old_topfill = curwin->w_topfill; @@ -900,6 +907,10 @@ static int insert_handle_key(InsertState *s) case K_IGNORE: // Something mapped to nothing break; + case K_PASTE_START: + paste_repeat(1); + goto check_pum; + case K_EVENT: // some event state_handle_k_event(); // If CTRL-G U was used apply it to the next typed key. @@ -1539,9 +1550,7 @@ static void init_prompt(int cmdchar_todo) if (cmdchar_todo == 'A') { coladvance(curwin, MAXCOL); } - if (curwin->w_cursor.col < (colnr_T)strlen(prompt)) { - curwin->w_cursor.col = (colnr_T)strlen(prompt); - } + curwin->w_cursor.col = MAX(curwin->w_cursor.col, (colnr_T)strlen(prompt)); // Make sure the cursor is in a valid position. check_cursor(curwin); } @@ -1576,7 +1585,7 @@ void edit_unputchar(void) /// text. Only works when cursor is in the line that changes. void display_dollar(colnr_T col_arg) { - colnr_T col = col_arg < 0 ? 0 : col_arg; + colnr_T col = MAX(col_arg, 0); if (!redrawing()) { return; @@ -1615,9 +1624,8 @@ void undisplay_dollar(void) /// type == INDENT_SET set indent to "amount" /// /// @param round if true, round the indent to 'shiftwidth' (only with _INC and _Dec). -/// @param replaced replaced character, put on replace stack /// @param call_changed_bytes call changed_bytes() -void change_indent(int type, int amount, int round, int replaced, bool call_changed_bytes) +void change_indent(int type, int amount, int round, bool call_changed_bytes) { int insstart_less; // reduction for Insstart.col colnr_T orig_col = 0; // init for GCC @@ -1734,12 +1742,7 @@ void change_indent(int type, int amount, int round, int replaced, bool call_chan } curwin->w_p_list = save_p_list; - - if (new_cursor_col <= 0) { - curwin->w_cursor.col = 0; - } else { - curwin->w_cursor.col = (colnr_T)new_cursor_col; - } + curwin->w_cursor.col = MAX(0, (colnr_T)new_cursor_col); curwin->w_set_curswant = true; changed_cline_bef_curs(curwin); @@ -1769,12 +1772,8 @@ void change_indent(int type, int amount, int round, int replaced, bool call_chan replace_join(0); // remove a NUL from the replace stack start_col--; } - while (start_col < (int)curwin->w_cursor.col || replaced) { - replace_push(NUL); - if (replaced) { - replace_push(replaced); - replaced = NUL; - } + while (start_col < (int)curwin->w_cursor.col) { + replace_push_nul(); start_col++; } } @@ -2025,7 +2024,6 @@ static void insert_special(int c, int allow_modmask, int ctrlv) // '0' and '^' are special, because they can be followed by CTRL-D. #define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^') -/// /// "flags": INSCHAR_FORMAT - force formatting /// INSCHAR_CTRLV - char typed just after CTRL-V /// INSCHAR_NO_FEX - don't use 'formatexpr' @@ -2328,7 +2326,7 @@ int stop_arrow(void) static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) { stop_redo_ins(); - replace_flush(); // abandon replace stack + kv_destroy(replace_stack); // abandon replace stack (reinitializes) // Save the inserted text for later redo with ^@ and CTRL-A. // Don't do it when "restart_edit" was set and nothing was inserted, @@ -2606,9 +2604,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) hasFolding(wp, lnum, &lnum, NULL); } } - if (lnum < 1) { - lnum = 1; - } + lnum = MAX(lnum, 1); } else { lnum -= n; } @@ -2649,7 +2645,6 @@ void cursor_down_inner(win_T *wp, int n) // count each sequence of folded lines as one logical line while (n--) { - // Move to last line of fold, will fail if it's the end-of-file. if (hasFoldingWin(wp, lnum, NULL, &last, true, NULL)) { lnum = last + 1; } else { @@ -2659,9 +2654,7 @@ void cursor_down_inner(win_T *wp, int n) break; } } - if (lnum > line_count) { - lnum = line_count; - } + lnum = MIN(lnum, line_count); } else { lnum += (linenr_T)n; } @@ -2672,8 +2665,10 @@ void cursor_down_inner(win_T *wp, int n) /// @param upd_topline When true: update topline int cursor_down(int n, bool upd_topline) { - // This fails if the cursor is already in the last line. - if (n > 0 && curwin->w_cursor.lnum >= curwin->w_buffer->b_ml.ml_line_count) { + linenr_T lnum = curwin->w_cursor.lnum; + // This fails if the cursor is already in the last (folded) line. + hasFoldingWin(curwin, lnum, NULL, &lnum, true, NULL); + if (n > 0 && lnum >= curwin->w_buffer->b_ml.ml_line_count) { return FAIL; } cursor_down_inner(curwin, n); @@ -2808,55 +2803,51 @@ static bool echeck_abbr(int c) // that the NL replaced. The extra one stores the characters after the cursor // that were deleted (always white space). -static uint8_t *replace_stack = NULL; -static ssize_t replace_stack_nr = 0; // next entry in replace stack -static ssize_t replace_stack_len = 0; // max. number of entries - /// Push character that is replaced onto the replace stack. /// /// replace_offset is normally 0, in which case replace_push will add a new /// character at the end of the stack. If replace_offset is not 0, that many /// characters will be left on the stack above the newly inserted character. /// -/// @param c character that is replaced (NUL is none) -void replace_push(int c) +/// @param str character that is replaced (NUL is none) +/// @param len length of character in bytes +void replace_push(char *str, size_t len) { - if (replace_stack_nr < replace_offset) { // nothing to do + // TODO(bfredl): replace_offset is suss af, if we don't need it, this + // function is just kv_concat() :p + if (kv_size(replace_stack) < (size_t)replace_offset) { // nothing to do return; } - if (replace_stack_len <= replace_stack_nr) { - replace_stack_len += 50; - replace_stack = xrealloc(replace_stack, (size_t)replace_stack_len); - } - uint8_t *p = replace_stack + replace_stack_nr - replace_offset; + kv_ensure_space(replace_stack, len); + + char *p = replace_stack.items + kv_size(replace_stack) - replace_offset; if (replace_offset) { - memmove(p + 1, p, (size_t)replace_offset); + memmove(p + len, p, (size_t)replace_offset); } - *p = (uint8_t)c; - replace_stack_nr++; + memcpy(p, str, len); + kv_size(replace_stack) += len; } -/// Push a character onto the replace stack. Handles a multi-byte character in -/// reverse byte order, so that the first byte is popped off first. -/// -/// @return the number of bytes done (includes composing characters). -int replace_push_mb(char *p) +/// push NUL as separator between entries in the stack +void replace_push_nul(void) { - int l = utfc_ptr2len(p); - - for (int j = l - 1; j >= 0; j--) { - replace_push(p[j]); - } - return l; + replace_push("", 1); } -/// Pop one item from the replace stack. +/// Check top of replace stack, pop it if it was NUL +/// +/// when a non-NUL byte is found, use mb_replace_pop_ins() to +/// pop one complete multibyte character. /// -/// @return -1 if stack is empty, replaced character or NUL otherwise -static int replace_pop(void) +/// @return -1 if stack is empty, last byte of char or NUL otherwise +static int replace_pop_if_nul(void) { - return (replace_stack_nr == 0) ? -1 : (int)replace_stack[--replace_stack_nr]; + int ch = (kv_size(replace_stack)) ? (uint8_t)kv_A(replace_stack, kv_size(replace_stack) - 1) : -1; + if (ch == NUL) { + kv_size(replace_stack)--; + } + return ch; } /// Join the top two items on the replace stack. This removes to "off"'th NUL @@ -2865,11 +2856,11 @@ static int replace_pop(void) /// @param off offset for which NUL to remove static void replace_join(int off) { - for (ssize_t i = replace_stack_nr; --i >= 0;) { - if (replace_stack[i] == NUL && off-- <= 0) { - replace_stack_nr--; - memmove(replace_stack + i, replace_stack + i + 1, - (size_t)(replace_stack_nr - i)); + for (ssize_t i = (ssize_t)kv_size(replace_stack); --i >= 0;) { + if (kv_A(replace_stack, i) == NUL && off-- <= 0) { + kv_size(replace_stack)--; + memmove(&kv_A(replace_stack, i), &kv_A(replace_stack, i + 1), + (kv_size(replace_stack) - (size_t)i)); return; } } @@ -2879,70 +2870,25 @@ static void replace_join(int off) /// before the cursor. Can only be used in MODE_REPLACE or MODE_VREPLACE state. static void replace_pop_ins(void) { - int cc; int oldState = State; State = MODE_NORMAL; // don't want MODE_REPLACE here - while ((cc = replace_pop()) > 0) { - mb_replace_pop_ins(cc); + while ((replace_pop_if_nul()) > 0) { + mb_replace_pop_ins(); dec_cursor(); } State = oldState; } -// Insert bytes popped from the replace stack. "cc" is the first byte. If it -// indicates a multi-byte char, pop the other bytes too. -static void mb_replace_pop_ins(int cc) -{ - int n; - uint8_t buf[MB_MAXBYTES + 1]; - - if ((n = MB_BYTE2LEN(cc)) > 1) { - buf[0] = (uint8_t)cc; - for (int i = 1; i < n; i++) { - buf[i] = (uint8_t)replace_pop(); - } - ins_bytes_len((char *)buf, (size_t)n); - } else { - ins_char(cc); - } - - // Handle composing chars. - while (true) { - int c = replace_pop(); - if (c == -1) { // stack empty - break; - } - if ((n = MB_BYTE2LEN(c)) == 1) { - // Not a multi-byte char, put it back. - replace_push(c); - break; - } - - buf[0] = (uint8_t)c; - assert(n > 1); - for (int i = 1; i < n; i++) { - buf[i] = (uint8_t)replace_pop(); - } - if (utf_iscomposing(utf_ptr2char((char *)buf))) { - ins_bytes_len((char *)buf, (size_t)n); - } else { - // Not a composing char, put it back. - for (int i = n - 1; i >= 0; i--) { - replace_push(buf[i]); - } - break; - } - } -} - -// make the replace stack empty -// (called when exiting replace mode) -static void replace_flush(void) +/// Insert multibyte char popped from the replace stack. +/// +/// caller must already have checked the top of the stack is not NUL!! +static void mb_replace_pop_ins(void) { - XFREE_CLEAR(replace_stack); - replace_stack_len = 0; - replace_stack_nr = 0; + int len = utf_head_off(&kv_A(replace_stack, 0), + &kv_A(replace_stack, kv_size(replace_stack) - 1)) + 1; + kv_size(replace_stack) -= (size_t)len; + ins_bytes_len(&kv_A(replace_stack, kv_size(replace_stack)), (size_t)len); } // Handle doing a BS for one character. @@ -2957,7 +2903,7 @@ static void replace_do_bs(int limit_col) colnr_T start_vcol; const int l_State = State; - int cc = replace_pop(); + int cc = replace_pop_if_nul(); if (cc > 0) { int orig_len = 0; int orig_vcols = 0; @@ -2971,7 +2917,6 @@ static void replace_do_bs(int limit_col) if (l_State & VREPLACE_FLAG) { orig_len = get_cursor_pos_len(); } - replace_push(cc); replace_pop_ins(); if (l_State & VREPLACE_FLAG) { @@ -3630,9 +3575,9 @@ static void ins_shift(int c, int lastc) if (lastc == '^') { old_indent = get_indent(); // remember curr. indent } - change_indent(INDENT_SET, 0, true, 0, true); + change_indent(INDENT_SET, 0, true, true); } else { - change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, true, 0, true); + change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, true, true); } if (did_ai && *skipwhite(get_cursor_line_ptr()) != NUL) { @@ -3751,7 +3696,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) // cc >= 0: NL was replaced, put original characters back cc = -1; if (State & REPLACE_FLAG) { - cc = replace_pop(); // returns -1 if NL was inserted + cc = replace_pop_if_nul(); // returns -1 if NL was inserted } // In replace mode, in the line we started replacing, we only move the // cursor. @@ -3797,9 +3742,9 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) // restore characters (blanks) deleted after cursor while (cc > 0) { colnr_T save_col = curwin->w_cursor.col; - mb_replace_pop_ins(cc); + mb_replace_pop_ins(); curwin->w_cursor.col = save_col; - cc = replace_pop(); + cc = replace_pop_if_nul(); } // restore the characters that NL replaced replace_pop_ins(); @@ -3856,7 +3801,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) space_sci = sci; space_vcol = vcol; } - vcol += charsize_nowrap(curbuf, use_ts, vcol, sci.chr.value); + vcol += charsize_nowrap(curbuf, sci.ptr, use_ts, vcol, sci.chr.value); sci = utfc_next(sci); prev_space = cur_space; } @@ -3872,7 +3817,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) // Find the position to stop backspacing. // Use charsize_nowrap() so that virtual text and wrapping are ignored. while (true) { - int size = charsize_nowrap(curbuf, use_ts, space_vcol, space_sci.chr.value); + int size = charsize_nowrap(curbuf, space_sci.ptr, use_ts, space_vcol, space_sci.chr.value); if (space_vcol + size > want_vcol) { break; } @@ -3908,7 +3853,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) } else { ins_str(" "); if ((State & REPLACE_FLAG)) { - replace_push(NUL); + replace_push_nul(); } } } @@ -3943,7 +3888,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) bool has_composing = false; if (p_deco) { char *p0 = get_cursor_pos_ptr(); - has_composing = utf_composinglike(p0, p0 + utf_ptr2len(p0)); + has_composing = utf_composinglike(p0, p0 + utf_ptr2len(p0), NULL); } del_char(false); // If there are combining characters and 'delcombine' is set @@ -4318,7 +4263,7 @@ static bool ins_tab(void) } else { ins_str(" "); if (State & REPLACE_FLAG) { // no char replaced - replace_push(NUL); + replace_push_nul(); } } } @@ -4419,18 +4364,30 @@ static bool ins_tab(void) // Delete following spaces. int i = cursor->col - fpos.col; if (i > 0) { - STRMOVE(ptr, ptr + i); + if (!(State & VREPLACE_FLAG)) { + char *newp = xmalloc((size_t)(curbuf->b_ml.ml_line_len - i)); + ptrdiff_t col = ptr - curbuf->b_ml.ml_line_ptr; + if (col > 0) { + memmove(newp, ptr - col, (size_t)col); + } + memmove(newp + col, ptr + i, (size_t)(curbuf->b_ml.ml_line_len - col - i)); + if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) { + xfree(curbuf->b_ml.ml_line_ptr); + } + curbuf->b_ml.ml_line_ptr = newp; + curbuf->b_ml.ml_line_len -= i; + curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; + inserted_bytes(fpos.lnum, change_col, + cursor->col - change_col, fpos.col - change_col); + } else { + STRMOVE(ptr, ptr + i); + } // correct replace stack. if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { for (temp = i; --temp >= 0;) { replace_join(repl_off); } } - if (!(State & VREPLACE_FLAG)) { - curbuf->b_ml.ml_line_len -= i; - inserted_bytes(fpos.lnum, change_col, - cursor->col - change_col, fpos.col - change_col); - } } cursor->col -= i; @@ -4473,7 +4430,7 @@ bool ins_eol(int c) // character under the cursor. Only push a NUL on the replace stack, // nothing to put back when the NL is deleted. if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { - replace_push(NUL); + replace_push_nul(); } // In MODE_VREPLACE state, a NL replaces the rest of the line, and starts @@ -4674,7 +4631,7 @@ static void ins_try_si(int c) i = get_indent(); curwin->w_cursor = old_pos; if (State & VREPLACE_FLAG) { - change_indent(INDENT_SET, i, false, NUL, true); + change_indent(INDENT_SET, i, false, true); } else { set_indent(i, SIN_CHANGED); } @@ -4712,9 +4669,7 @@ static void ins_try_si(int c) } // Adjust ai_col, the char at this position can be deleted. - if (ai_col > curwin->w_cursor.col) { - ai_col = curwin->w_cursor.col; - } + ai_col = MIN(ai_col, curwin->w_cursor.col); } // Get the value that w_virtcol would have when 'list' is off. |