aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/edit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/edit.c')
-rw-r--r--src/nvim/edit.c259
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.