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.c1719
1 files changed, 900 insertions, 819 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index b2f9601f82..abd16e57ae 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -52,6 +52,7 @@
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
+#include "nvim/state.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
#include "nvim/ui.h"
@@ -192,6 +193,28 @@ static expand_T compl_xp;
static int compl_opt_refresh_always = FALSE;
+typedef struct insert_state {
+ VimState state;
+ cmdarg_T *ca;
+ int mincol;
+ int cmdchar;
+ int startln;
+ long count;
+ int c;
+ int lastc;
+ int i;
+ bool did_backspace; // previous char was backspace
+ bool line_is_white; // line is empty before insert
+ linenr_T old_topline; // topline before insertion
+ int old_topfill;
+ int inserted_space; // just inserted a space
+ int replaceState;
+ int did_restart_edit; // remember if insert mode was restarted
+ // after a ctrl+o
+ bool nomove;
+ uint8_t *ptr;
+} InsertState;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "edit.c.generated.h"
@@ -228,113 +251,48 @@ static int ins_need_undo; /* call u_save() before inserting a
static int did_add_space = FALSE; /* auto_format() added an extra space
under the cursor */
-static int dont_sync_undo = false; /* CTRL-G U prevents syncing undo for
- the next left/right cursor */
+static int dont_sync_undo = false; // CTRL-G U prevents syncing undo
+ // for the next left/right cursor
-/*
- * edit(): Start inserting text.
- *
- * "cmdchar" can be:
- * 'i' normal insert command
- * 'a' normal append command
- * 'R' replace command
- * 'r' "r<CR>" command: insert one <CR>. Note: count can be > 1, for redo,
- * but still only one <CR> is inserted. The <Esc> is not used for redo.
- * 'g' "gI" command.
- * 'V' "gR" command for Virtual Replace mode.
- * 'v' "gr" command for single character Virtual Replace mode.
- *
- * This function is not called recursively. For CTRL-O commands, it returns
- * and lets the caller handle the Normal-mode command.
- *
- * Return TRUE if a CTRL-O command caused the return (insert mode pending).
- */
-int
-edit (
- int cmdchar,
- int startln, /* if set, insert at start of line */
- long count
-)
-{
- if (curbuf->terminal) {
- if (ex_normal_busy) {
- // don't enter terminal mode from `ex_normal`, which can result in all
- // kinds of havoc(such as terminal mode recursiveness). Instead, set a
- // flag that allow us to force-set the value of `restart_edit` before
- // `ex_normal` returns
- restart_edit = 'i';
- force_restart_edit = true;
- } else {
- terminal_enter();
- }
- return false;
- }
+static linenr_T o_lnum = 0;
- int c = 0;
- char_u *ptr;
- int lastc;
- int mincol;
- static linenr_T o_lnum = 0;
- int i;
- int did_backspace = TRUE; /* previous char was backspace */
- int line_is_white = FALSE; /* line is empty before insert */
- linenr_T old_topline = 0; /* topline before insertion */
- int old_topfill = -1;
- int inserted_space = FALSE; /* just inserted a space */
- int replaceState = REPLACE;
- int nomove = FALSE; /* don't move cursor on return */
-
- /* Remember whether editing was restarted after CTRL-O. */
+static void insert_enter(InsertState *s)
+{
+ s->did_backspace = true;
+ s->old_topfill = -1;
+ s->replaceState = REPLACE;
+ // Remember whether editing was restarted after CTRL-O
did_restart_edit = restart_edit;
-
- /* sleep before redrawing, needed for "CTRL-O :" that results in an
- * error message */
- check_for_delay(TRUE);
-
+ // sleep before redrawing, needed for "CTRL-O :" that results in an
+ // error message
+ check_for_delay(true);
// set Insstart_orig to Insstart
update_Insstart_orig = true;
- // Don't allow inserting in the sandbox.
- if (sandbox != 0) {
- EMSG(_(e_sandbox));
- return FALSE;
- }
- /* Don't allow changes in the buffer while editing the cmdline. The
- * caller of getcmdline() may get confused. */
- if (textlock != 0) {
- EMSG(_(e_secure));
- return FALSE;
- }
-
- /* Don't allow recursive insert mode when busy with completion. */
- if (compl_started || compl_busy || pum_visible()) {
- EMSG(_(e_secure));
- return FALSE;
- }
- ins_compl_clear(); /* clear stuff for CTRL-X mode */
+ ins_compl_clear(); // clear stuff for CTRL-X mode
- /*
- * Trigger InsertEnter autocommands. Do not do this for "r<CR>" or "grx".
- */
- if (cmdchar != 'r' && cmdchar != 'v') {
+ // Trigger InsertEnter autocommands. Do not do this for "r<CR>" or "grx".
+ if (s->cmdchar != 'r' && s->cmdchar != 'v') {
pos_T save_cursor = curwin->w_cursor;
- if (cmdchar == 'R')
- ptr = (char_u *)"r";
- else if (cmdchar == 'V')
- ptr = (char_u *)"v";
- else
- ptr = (char_u *)"i";
- set_vim_var_string(VV_INSERTMODE, ptr, 1);
+ if (s->cmdchar == 'R') {
+ s->ptr = (char_u *)"r";
+ } else if (s->cmdchar == 'V') {
+ s->ptr = (char_u *)"v";
+ } else {
+ s->ptr = (char_u *)"i";
+ }
+
+ set_vim_var_string(VV_INSERTMODE, s->ptr, 1);
set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */
- apply_autocmds(EVENT_INSERTENTER, NULL, NULL, FALSE, curbuf);
-
- /* Make sure the cursor didn't move. Do call check_cursor_col() in
- * case the text was modified. Since Insert mode was not started yet
- * a call to check_cursor_col() may move the cursor, especially with
- * the "A" command, thus set State to avoid that. Also check that the
- * line number is still valid (lines may have been deleted).
- * Do not restore if v:char was set to a non-empty string. */
+ apply_autocmds(EVENT_INSERTENTER, NULL, NULL, false, curbuf);
+
+ // Make sure the cursor didn't move. Do call check_cursor_col() in
+ // case the text was modified. Since Insert mode was not started yet
+ // a call to check_cursor_col() may move the cursor, especially with
+ // the "A" command, thus set State to avoid that. Also check that the
+ // line number is still valid (lines may have been deleted).
+ // Do not restore if v:char was set to a non-empty string.
if (!equalpos(curwin->w_cursor, save_cursor)
&& *get_vim_var_str(VV_CHAR) == NUL
&& save_cursor.lnum <= curbuf->b_ml.ml_line_count) {
@@ -347,906 +305,1033 @@ edit (
}
}
- /* Check if the cursor line needs redrawing before changing State. If
- * 'concealcursor' is "n" it needs to be redrawn without concealing. */
+ // Check if the cursor line needs redrawing before changing State. If
+ // 'concealcursor' is "n" it needs to be redrawn without concealing.
conceal_check_cursur_line();
- /*
- * When doing a paste with the middle mouse button, Insstart is set to
- * where the paste started.
- */
- if (where_paste_started.lnum != 0)
+ // When doing a paste with the middle mouse button, Insstart is set to
+ // where the paste started.
+ if (where_paste_started.lnum != 0) {
Insstart = where_paste_started;
- else {
+ } else {
Insstart = curwin->w_cursor;
- if (startln)
+ if (s->startln) {
Insstart.col = 0;
+ }
}
+
Insstart_textlen = (colnr_T)linetabsize(get_cursor_line_ptr());
Insstart_blank_vcol = MAXCOL;
- if (!did_ai)
+
+ if (!did_ai) {
ai_col = 0;
+ }
- if (cmdchar != NUL && restart_edit == 0) {
+ if (s->cmdchar != NUL && restart_edit == 0) {
ResetRedobuff();
- AppendNumberToRedobuff(count);
- if (cmdchar == 'V' || cmdchar == 'v') {
- /* "gR" or "gr" command */
+ AppendNumberToRedobuff(s->count);
+ if (s->cmdchar == 'V' || s->cmdchar == 'v') {
+ // "gR" or "gr" command
AppendCharToRedobuff('g');
- AppendCharToRedobuff((cmdchar == 'v') ? 'r' : 'R');
+ AppendCharToRedobuff((s->cmdchar == 'v') ? 'r' : 'R');
} else {
- AppendCharToRedobuff(cmdchar);
- if (cmdchar == 'g') /* "gI" command */
+ AppendCharToRedobuff(s->cmdchar);
+ if (s->cmdchar == 'g') { // "gI" command
AppendCharToRedobuff('I');
- else if (cmdchar == 'r') /* "r<CR>" command */
- count = 1; /* insert only one <CR> */
+ } else if (s->cmdchar == 'r') { // "r<CR>" command
+ s->count = 1; // insert only one <CR>
+ }
}
}
- if (cmdchar == 'R') {
+ if (s->cmdchar == 'R') {
if (p_fkmap && p_ri) {
beep_flush();
- EMSG(farsi_text_3); /* encoded in Farsi */
+ EMSG(farsi_text_3); // encoded in Farsi
State = INSERT;
- } else
+ } else {
State = REPLACE;
- } else if (cmdchar == 'V' || cmdchar == 'v') {
+ }
+ } else if (s->cmdchar == 'V' || s->cmdchar == 'v') {
State = VREPLACE;
- replaceState = VREPLACE;
+ s->replaceState = VREPLACE;
orig_line_count = curbuf->b_ml.ml_line_count;
vr_lines_changed = 1;
- } else
+ } else {
State = INSERT;
+ }
- stop_insert_mode = FALSE;
+ stop_insert_mode = false;
- /*
- * Need to recompute the cursor position, it might move when the cursor is
- * on a TAB or special character.
- */
- curs_columns(TRUE);
+ // Need to recompute the cursor position, it might move when the cursor is
+ // on a TAB or special character.
+ curs_columns(true);
- /*
- * Enable langmap or IME, indicated by 'iminsert'.
- * Note that IME may enabled/disabled without us noticing here, thus the
- * 'iminsert' value may not reflect what is actually used. It is updated
- * when hitting <Esc>.
- */
- if (curbuf->b_p_iminsert == B_IMODE_LMAP)
+ // Enable langmap or IME, indicated by 'iminsert'.
+ // Note that IME may enabled/disabled without us noticing here, thus the
+ // 'iminsert' value may not reflect what is actually used. It is updated
+ // when hitting <Esc>.
+ if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
State |= LANGMAP;
+ }
setmouse();
clear_showcmd();
- /* there is no reverse replace mode */
+ // there is no reverse replace mode
revins_on = (State == INSERT && p_ri);
- if (revins_on)
+ if (revins_on) {
undisplay_dollar();
+ }
revins_chars = 0;
revins_legal = 0;
revins_scol = -1;
- /*
- * Handle restarting Insert mode.
- * Don't do this for "CTRL-O ." (repeat an insert): we get here with
- * restart_edit non-zero, and something in the stuff buffer.
- */
+ // Handle restarting Insert mode.
+ // Don't do this for "CTRL-O ." (repeat an insert): we get here with
+ // restart_edit non-zero, and something in the stuff buffer.
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;
+ // 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;
+ }
restart_edit = 0;
- /*
- * If the cursor was after the end-of-line before the CTRL-O and it is
- * now at the end-of-line, put it after the end-of-line (this is not
- * correct in very rare cases).
- * Also do this if curswant is greater than the current virtual
- * column. Eg after "^O$" or "^O80|".
- */
+ // If the cursor was after the end-of-line before the CTRL-O and it is
+ // now at the end-of-line, put it after the end-of-line (this is not
+ // correct in very rare cases).
+ // Also do this if curswant is greater than the current virtual
+ // column. Eg after "^O$" or "^O80|".
validate_virtcol();
update_curswant();
if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum)
|| curwin->w_curswant > curwin->w_virtcol)
- && *(ptr = get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) {
- if (ptr[1] == NUL)
+ && *(s->ptr = get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) {
+ if (s->ptr[1] == NUL) {
++curwin->w_cursor.col;
- else if (has_mbyte) {
- i = (*mb_ptr2len)(ptr);
- if (ptr[i] == NUL)
- curwin->w_cursor.col += i;
+ } else if (has_mbyte) {
+ s->i = (*mb_ptr2len)(s->ptr);
+ if (s->ptr[s->i] == NUL) {
+ curwin->w_cursor.col += s->i;
+ }
}
}
- ins_at_eol = FALSE;
- } else
- arrow_used = FALSE;
+ ins_at_eol = false;
+ } else {
+ arrow_used = false;
+ }
- /* we are in insert mode now, don't need to start it anymore */
- need_start_insertmode = FALSE;
+ // we are in insert mode now, don't need to start it anymore
+ need_start_insertmode = false;
- /* Need to save the line for undo before inserting the first char. */
- ins_need_undo = TRUE;
+ // Need to save the line for undo before inserting the first char.
+ ins_need_undo = true;
where_paste_started.lnum = 0;
- can_cindent = TRUE;
- /* The cursor line is not in a closed fold, unless 'insertmode' is set or
- * restarting. */
- if (!p_im && did_restart_edit == 0)
+ can_cindent = true;
+ // The cursor line is not in a closed fold, unless 'insertmode' is set or
+ // restarting.
+ if (!p_im && did_restart_edit == 0) {
foldOpenCursor();
+ }
- /*
- * If 'showmode' is set, show the current (insert/replace/..) mode.
- * A warning message for changing a readonly file is given here, before
- * actually changing anything. It's put after the mode, if any.
- */
- i = 0;
- if (p_smd && msg_silent == 0)
- i = showmode();
+ // If 'showmode' is set, show the current (insert/replace/..) mode.
+ // A warning message for changing a readonly file is given here, before
+ // actually changing anything. It's put after the mode, if any.
+ s->i = 0;
+ if (p_smd && msg_silent == 0) {
+ s->i = showmode();
+ }
- if (!p_im && did_restart_edit == 0)
- change_warning(i == 0 ? 0 : i + 1);
+ if (!p_im && did_restart_edit == 0) {
+ change_warning(s->i == 0 ? 0 : s->i + 1);
+ }
ui_cursor_shape(); /* may show different cursor shape */
do_digraph(-1); /* clear digraphs */
- /*
- * Get the current length of the redo buffer, those characters have to be
- * skipped if we want to get to the inserted characters.
- */
- ptr = get_inserted();
- if (ptr == NULL)
+ // Get the current length of the redo buffer, those characters have to be
+ // skipped if we want to get to the inserted characters.
+ s->ptr = get_inserted();
+ if (s->ptr == NULL) {
new_insert_skip = 0;
- else {
- new_insert_skip = (int)STRLEN(ptr);
- xfree(ptr);
+ } else {
+ new_insert_skip = (int)STRLEN(s->ptr);
+ xfree(s->ptr);
}
old_indent = 0;
- /*
- * Main loop in Insert mode: repeat until Insert mode is left.
- */
- for (;; ) {
- if (!revins_legal)
- revins_scol = -1; /* reset on illegal motions */
- else
- revins_legal = 0;
- if (arrow_used) /* don't repeat insert when arrow key used */
- count = 0;
+ do {
+ state_enter(&s->state);
+ // If s->count != 0, `ins_esc` will prepare the redo buffer for reprocessing
+ // and return false, causing `state_enter` to be called again.
+ } while (!ins_esc(&s->count, s->cmdchar, s->nomove));
- if (update_Insstart_orig) {
- Insstart_orig = Insstart;
- }
+ // Always update o_lnum, so that a "CTRL-O ." that adds a line
+ // still puts the cursor back after the inserted text.
+ if (ins_at_eol && gchar_cursor() == NUL) {
+ o_lnum = curwin->w_cursor.lnum;
+ }
- if (stop_insert_mode) {
- /* ":stopinsert" used or 'insertmode' reset */
- count = 0;
- goto doESCkey;
- }
+ if (s->cmdchar != 'r' && s->cmdchar != 'v') {
+ apply_autocmds(EVENT_INSERTLEAVE, NULL, NULL, false, curbuf);
+ }
+ did_cursorhold = false;
+}
+
+static int insert_check(VimState *state)
+{
+ InsertState *s = (InsertState *)state;
+
+ // If typed something may trigger CursorHoldI again.
+ if (s->c != K_EVENT) {
+ did_cursorhold = false;
+ }
+
+ // If the cursor was moved we didn't just insert a space */
+ if (arrow_used) {
+ s->inserted_space = false;
+ }
+
+ if (can_cindent && cindent_on() && ctrl_x_mode == 0) {
+ insert_do_cindent(s);
+ }
+
+ if (!revins_legal) {
+ revins_scol = -1; // reset on illegal motions
+ } else {
+ revins_legal = 0;
+ }
+
+ if (arrow_used) { // don't repeat insert when arrow key used
+ s->count = 0;
+ }
+
+ if (update_Insstart_orig) {
+ Insstart_orig = Insstart;
+ }
+
+ if (stop_insert_mode) {
+ // ":stopinsert" used or 'insertmode' reset
+ s->count = 0;
+ return 0; // exit insert mode
+ }
- /* set curwin->w_curswant for next K_DOWN or K_UP */
- if (!arrow_used)
- curwin->w_set_curswant = TRUE;
+ // set curwin->w_curswant for next K_DOWN or K_UP
+ if (!arrow_used) {
+ curwin->w_set_curswant = true;
+ }
- /* If there is no typeahead may check for timestamps (e.g., for when a
- * menu invoked a shell command). */
- if (stuff_empty()) {
- did_check_timestamps = FALSE;
- if (need_check_timestamps)
- check_timestamps(FALSE);
+ // If there is no typeahead may check for timestamps (e.g., for when a
+ // menu invoked a shell command).
+ if (stuff_empty()) {
+ did_check_timestamps = false;
+ if (need_check_timestamps) {
+ check_timestamps(false);
}
+ }
- /*
- * When emsg() was called msg_scroll will have been set.
- */
- msg_scroll = FALSE;
+ // When emsg() was called msg_scroll will have been set.
+ msg_scroll = false;
- /* Open fold at the cursor line, according to 'foldopen'. */
- if (fdo_flags & FDO_INSERT)
- foldOpenCursor();
- /* Close folds where the cursor isn't, according to 'foldclose' */
- if (!char_avail())
- foldCheckClose();
+ // Open fold at the cursor line, according to 'foldopen'.
+ if (fdo_flags & FDO_INSERT) {
+ foldOpenCursor();
+ }
- /*
- * If we inserted a character at the last position of the last line in
- * the window, scroll the window one line up. This avoids an extra
- * redraw.
- * This is detected when the cursor column is smaller after inserting
- * something.
- * Don't do this when the topline changed already, it has
- * already been adjusted (by insertchar() calling open_line())).
- */
- if (curbuf->b_mod_set
- && curwin->w_p_wrap
- && !did_backspace
- && curwin->w_topline == old_topline
- && curwin->w_topfill == old_topfill
- ) {
- mincol = curwin->w_wcol;
- validate_cursor_col();
-
- if (curwin->w_wcol < mincol - curbuf->b_p_ts
- && curwin->w_wrow == curwin->w_winrow
- + curwin->w_height - 1 - p_so
- && (curwin->w_cursor.lnum != curwin->w_topline
- || curwin->w_topfill > 0
- )) {
- if (curwin->w_topfill > 0)
- --curwin->w_topfill;
- else if (hasFolding(curwin->w_topline, NULL, &old_topline))
- set_topline(curwin, old_topline + 1);
- else
- set_topline(curwin, curwin->w_topline + 1);
+ // Close folds where the cursor isn't, according to 'foldclose'
+ if (!char_avail()) {
+ foldCheckClose();
+ }
+
+ // If we inserted a character at the last position of the last line in the
+ // window, scroll the window one line up. This avoids an extra redraw. This
+ // is detected when the cursor column is smaller after inserting something.
+ // Don't do this when the topline changed already, it has already been
+ // adjusted (by insertchar() calling open_line())).
+ if (curbuf->b_mod_set
+ && curwin->w_p_wrap
+ && !s->did_backspace
+ && curwin->w_topline == s->old_topline
+ && curwin->w_topfill == s->old_topfill) {
+ s->mincol = curwin->w_wcol;
+ validate_cursor_col();
+
+ if (curwin->w_wcol < s->mincol - curbuf->b_p_ts
+ && curwin->w_wrow == curwin->w_winrow
+ + curwin->w_height - 1 - p_so
+ && (curwin->w_cursor.lnum != curwin->w_topline
+ || curwin->w_topfill > 0)) {
+ if (curwin->w_topfill > 0) {
+ --curwin->w_topfill;
+ } else if (hasFolding(curwin->w_topline, NULL, &s->old_topline)) {
+ set_topline(curwin, s->old_topline + 1);
+ } else {
+ set_topline(curwin, curwin->w_topline + 1);
}
}
+ }
- /* May need to adjust w_topline to show the cursor. */
- update_topline();
+ // May need to adjust w_topline to show the cursor.
+ update_topline();
- did_backspace = FALSE;
+ s->did_backspace = false;
- validate_cursor(); /* may set must_redraw */
+ validate_cursor(); // may set must_redraw
- /*
- * Redraw the display when no characters are waiting.
- * Also shows mode, ruler and positions cursor.
- */
- ins_redraw(TRUE);
+ // Redraw the display when no characters are waiting.
+ // Also shows mode, ruler and positions cursor.
+ ins_redraw(true);
- if (curwin->w_p_scb)
- do_check_scrollbind(TRUE);
+ if (curwin->w_p_scb) {
+ do_check_scrollbind(true);
+ }
- if (curwin->w_p_crb)
- do_check_cursorbind();
- update_curswant();
- old_topline = curwin->w_topline;
- old_topfill = curwin->w_topfill;
+ if (curwin->w_p_crb) {
+ do_check_cursorbind();
+ }
+ update_curswant();
+ s->old_topline = curwin->w_topline;
+ s->old_topfill = curwin->w_topfill;
+ s->lastc = s->c; // remember previous char for CTRL-D
- /*
- * Get a character for Insert mode. Ignore K_IGNORE.
- */
- lastc = c; /* remember previous char for CTRL-D */
+ // After using CTRL-G U the next cursor key will not break undo.
+ if (dont_sync_undo == MAYBE) {
+ dont_sync_undo = true;
+ } else {
+ dont_sync_undo = false;
+ }
- // After using CTRL-G U the next cursor key will not break undo.
- if (dont_sync_undo == MAYBE)
- dont_sync_undo = true;
- else
- dont_sync_undo = false;
+ return 1;
+}
- input_enable_events();
- do {
- c = safe_vgetc();
- } while (c == K_IGNORE);
- input_disable_events();
+static int insert_execute(VimState *state, int key)
+{
+ if (key == K_IGNORE) {
+ return -1; // get another key
+ }
+ InsertState *s = (InsertState *)state;
+ s->c = key;
- if (c == K_EVENT) {
- c = lastc;
- queue_process_events(loop.events);
- continue;
- }
+ // Don't want K_EVENT with cursorhold for the second key, e.g., after CTRL-V.
+ did_cursorhold = true;
- /* Don't want K_CURSORHOLD for the second key, e.g., after CTRL-V. */
- did_cursorhold = TRUE;
+ if (p_hkmap && KeyTyped) {
+ s->c = hkmap(s->c); // Hebrew mode mapping
+ }
- if (p_hkmap && KeyTyped)
- c = hkmap(c); /* Hebrew mode mapping */
- if (p_fkmap && KeyTyped)
- c = fkmap(c); /* Farsi mode mapping */
+ if (p_fkmap && KeyTyped) {
+ s->c = fkmap(s->c); // Farsi mode mapping
+ }
- /*
- * Special handling of keys while the popup menu is visible or wanted
- * and the cursor is still in the completed word. Only when there is
- * a match, skip this when no matches were found.
- */
- if (compl_started
- && pum_wanted()
- && curwin->w_cursor.col >= compl_col
- && (compl_shown_match == NULL
- || compl_shown_match != compl_shown_match->cp_next)) {
- /* BS: Delete one character from "compl_leader". */
- if ((c == K_BS || c == Ctrl_H)
- && curwin->w_cursor.col > compl_col
- && (c = ins_compl_bs()) == NUL)
- continue;
-
- /* When no match was selected or it was edited. */
- if (!compl_used_match) {
- /* CTRL-L: Add one character from the current match to
- * "compl_leader". Except when at the original match and
- * there is nothing to add, CTRL-L works like CTRL-P then. */
- if (c == Ctrl_L
- && (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)
- || (int)STRLEN(compl_shown_match->cp_str)
- > curwin->w_cursor.col - compl_col)) {
- ins_compl_addfrommatch();
- continue;
- }
+ // Special handling of keys while the popup menu is visible or wanted
+ // and the cursor is still in the completed word. Only when there is
+ // a match, skip this when no matches were found.
+ if (compl_started
+ && pum_wanted()
+ && curwin->w_cursor.col >= compl_col
+ && (compl_shown_match == NULL
+ || compl_shown_match != compl_shown_match->cp_next)) {
+ // BS: Delete one character from "compl_leader".
+ if ((s->c == K_BS || s->c == Ctrl_H)
+ && curwin->w_cursor.col > compl_col
+ && (s->c = ins_compl_bs()) == NUL) {
+ return 1; // continue
+ }
- /* A non-white character that fits in with the current
- * completion: Add to "compl_leader". */
- if (ins_compl_accept_char(c)) {
- /* Trigger InsertCharPre. */
- char_u *str = do_insert_char_pre(c);
- char_u *p;
-
- if (str != NULL) {
- for (p = str; *p != NUL; mb_ptr_adv(p))
- ins_compl_addleader(PTR2CHAR(p));
- xfree(str);
- } else
- ins_compl_addleader(c);
- continue;
- }
+ // When no match was selected or it was edited.
+ if (!compl_used_match) {
+ // CTRL-L: Add one character from the current match to
+ // "compl_leader". Except when at the original match and
+ // there is nothing to add, CTRL-L works like CTRL-P then.
+ if (s->c == Ctrl_L
+ && (!CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)
+ || (int)STRLEN(compl_shown_match->cp_str)
+ > curwin->w_cursor.col - compl_col)) {
+ ins_compl_addfrommatch();
+ return 1; // continue
+ }
+
+ // A non-white character that fits in with the current
+ // completion: Add to "compl_leader".
+ if (ins_compl_accept_char(s->c)) {
+ // Trigger InsertCharPre.
+ char_u *str = do_insert_char_pre(s->c);
+ char_u *p;
- /* Pressing CTRL-Y selects the current match. When
- * compl_enter_selects is set the Enter key does the same. */
- if (c == Ctrl_Y || (compl_enter_selects
- && (c == CAR || c == K_KENTER || c == NL))) {
- ins_compl_delete();
- ins_compl_insert();
+ if (str != NULL) {
+ for (p = str; *p != NUL; mb_ptr_adv(p)) {
+ ins_compl_addleader(PTR2CHAR(p));
+ }
+ xfree(str);
+ } else {
+ ins_compl_addleader(s->c);
}
+ return 1; // continue
+ }
+
+ // Pressing CTRL-Y selects the current match. When
+ // compl_enter_selects is set the Enter key does the same.
+ if (s->c == Ctrl_Y
+ || (compl_enter_selects
+ && (s->c == CAR || s->c == K_KENTER || s->c == NL))) {
+ ins_compl_delete();
+ ins_compl_insert();
}
}
+ }
- /* Prepare for or stop CTRL-X mode. This doesn't do completion, but
- * it does fix up the text when finishing completion. */
- compl_get_longest = FALSE;
- if (ins_compl_prep(c))
- continue;
+ // Prepare for or stop CTRL-X mode. This doesn't do completion, but it does
+ // fix up the text when finishing completion.
+ compl_get_longest = false;
+ if (ins_compl_prep(s->c)) {
+ return 1; // continue
+ }
- /* CTRL-\ CTRL-N goes to Normal mode,
- * CTRL-\ CTRL-G goes to mode selected with 'insertmode',
- * CTRL-\ CTRL-O is like CTRL-O but without moving the cursor. */
- if (c == Ctrl_BSL) {
- /* may need to redraw when no more chars available now */
- ins_redraw(FALSE);
- ++no_mapping;
- ++allow_keys;
- c = plain_vgetc();
- --no_mapping;
- --allow_keys;
- if (c != Ctrl_N && c != Ctrl_G && c != Ctrl_O) {
- /* it's something else */
- vungetc(c);
- c = Ctrl_BSL;
- } else if (c == Ctrl_G && p_im)
- continue;
- else {
- if (c == Ctrl_O) {
- ins_ctrl_o();
- ins_at_eol = FALSE; /* cursor keeps its column */
- nomove = TRUE;
- }
- count = 0;
- goto doESCkey;
+ // CTRL-\ CTRL-N goes to Normal mode,
+ // CTRL-\ CTRL-G goes to mode selected with 'insertmode',
+ // CTRL-\ CTRL-O is like CTRL-O but without moving the cursor
+ if (s->c == Ctrl_BSL) {
+ // may need to redraw when no more chars available now
+ ins_redraw(false);
+ ++no_mapping;
+ ++allow_keys;
+ s->c = plain_vgetc();
+ --no_mapping;
+ --allow_keys;
+ if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) {
+ // it's something else
+ vungetc(s->c);
+ s->c = Ctrl_BSL;
+ } else if (s->c == Ctrl_G && p_im) {
+ return 1; // continue
+ } else {
+ if (s->c == Ctrl_O) {
+ ins_ctrl_o();
+ ins_at_eol = false; // cursor keeps its column
+ s->nomove = true;
}
+ s->count = 0;
+ return 0;
}
+ }
- c = do_digraph(c);
+ s->c = do_digraph(s->c);
- if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE)
- goto docomplete;
- if (c == Ctrl_V || c == Ctrl_Q) {
- ins_ctrl_v();
- c = Ctrl_V; /* pretend CTRL-V is last typed character */
- continue;
+ if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) {
+ insert_do_complete(s);
+ return 1;
+ }
+
+ if (s->c == Ctrl_V || s->c == Ctrl_Q) {
+ ins_ctrl_v();
+ s->c = Ctrl_V; // pretend CTRL-V is last typed character
+ return 1; // continue
+ }
+
+ if (cindent_on()
+ && ctrl_x_mode == 0) {
+ // A key name preceded by a bang means this key is not to be
+ // inserted. Skip ahead to the re-indenting below.
+ // A key name preceded by a star means that indenting has to be
+ // done before inserting the key.
+ s->line_is_white = inindent(0);
+ if (in_cinkeys(s->c, '!', s->line_is_white)) {
+ insert_do_cindent(s);
+ return 1; // continue
}
- if (cindent_on()
- && ctrl_x_mode == 0
- ) {
- /* A key name preceded by a bang means this key is not to be
- * inserted. Skip ahead to the re-indenting below.
- * A key name preceded by a star means that indenting has to be
- * done before inserting the key. */
- line_is_white = inindent(0);
- if (in_cinkeys(c, '!', line_is_white))
- goto force_cindent;
- if (can_cindent && in_cinkeys(c, '*', line_is_white)
- && stop_arrow() == OK)
- do_c_expr_indent();
+ if (can_cindent && in_cinkeys(s->c, '*', s->line_is_white)
+ && stop_arrow() == OK) {
+ do_c_expr_indent();
}
+ }
- if (curwin->w_p_rl)
- switch (c) {
- case K_LEFT: c = K_RIGHT; break;
- case K_S_LEFT: c = K_S_RIGHT; break;
- case K_C_LEFT: c = K_C_RIGHT; break;
- case K_RIGHT: c = K_LEFT; break;
- case K_S_RIGHT: c = K_S_LEFT; break;
- case K_C_RIGHT: c = K_C_LEFT; break;
- }
+ if (curwin->w_p_rl)
+ switch (s->c) {
+ case K_LEFT: s->c = K_RIGHT; break;
+ case K_S_LEFT: s->c = K_S_RIGHT; break;
+ case K_C_LEFT: s->c = K_C_RIGHT; break;
+ case K_RIGHT: s->c = K_LEFT; break;
+ case K_S_RIGHT: s->c = K_S_LEFT; break;
+ case K_C_RIGHT: s->c = K_C_LEFT; break;
+ }
- /*
- * If 'keymodel' contains "startsel", may start selection. If it
- * does, a CTRL-O and c will be stuffed, we need to get these
- * characters.
- */
- if (ins_start_select(c))
- continue;
+ // If 'keymodel' contains "startsel", may start selection. If it
+ // does, a CTRL-O and c will be stuffed, we need to get these
+ // characters.
+ if (ins_start_select(s->c)) {
+ return 1; // continue
+ }
- /*
- * The big switch to handle a character in insert mode.
- */
- switch (c) {
- case ESC: /* End input mode */
- if (echeck_abbr(ESC + ABBR_OFF))
- break;
- /*FALLTHROUGH*/
-
- case Ctrl_C: /* End input mode */
- if (c == Ctrl_C && cmdwin_type != 0) {
- /* Close the cmdline window. */
- cmdwin_result = K_IGNORE;
- got_int = FALSE; /* don't stop executing autocommands et al. */
- nomove = TRUE;
- goto doESCkey;
- }
+ return insert_handle_key(s);
+}
-#ifdef UNIX
-do_intr:
-#endif
- // when 'insertmode' set, and not halfway through a mapping, don't leave
- // Insert mode
- if (goto_im()) {
- if (got_int) {
- (void)vgetc(); /* flush all buffers */
- got_int = false;
- } else {
- vim_beep(BO_IM);
- }
- break;
- }
-doESCkey:
- /*
- * This is the ONLY return from edit()!
- */
- /* Always update o_lnum, so that a "CTRL-O ." that adds a line
- * still puts the cursor back after the inserted text. */
- if (ins_at_eol && gchar_cursor() == NUL)
- o_lnum = curwin->w_cursor.lnum;
-
- if (ins_esc(&count, cmdchar, nomove)) {
- if (cmdchar != 'r' && cmdchar != 'v')
- apply_autocmds(EVENT_INSERTLEAVE, NULL, NULL,
- FALSE, curbuf);
- did_cursorhold = FALSE;
- return c == Ctrl_O;
- }
- continue;
+static int insert_handle_key(InsertState *s)
+{
+ // The big switch to handle a character in insert mode.
+ // TODO(tarruda): This could look better if a lookup table is used.
+ // (similar to normal mode `nv_cmds[]`)
+ switch (s->c) {
+ case ESC: // End input mode
+ if (echeck_abbr(ESC + ABBR_OFF)) {
+ break;
+ }
+ // FALLTHROUGH
- case Ctrl_Z: /* suspend when 'insertmode' set */
- if (!p_im)
- goto normalchar; /* insert CTRL-Z as normal char */
- stuffReadbuff((char_u *)":st\r");
- c = Ctrl_O;
- /*FALLTHROUGH*/
-
- case Ctrl_O: /* execute one command */
- if (ctrl_x_mode == CTRL_X_OMNI)
- goto docomplete;
- if (echeck_abbr(Ctrl_O + ABBR_OFF))
- break;
- ins_ctrl_o();
+ case Ctrl_C: // End input mode
+ if (s->c == Ctrl_C && cmdwin_type != 0) {
+ // Close the cmdline window. */
+ cmdwin_result = K_IGNORE;
+ got_int = false; // don't stop executing autocommands et al
+ s->nomove = true;
+ return 0; // exit insert mode
+ }
- /* don't move the cursor left when 'virtualedit' has "onemore". */
- if (ve_flags & VE_ONEMORE) {
- ins_at_eol = FALSE;
- nomove = TRUE;
+ // when 'insertmode' set, and not halfway through a mapping, don't leave
+ // Insert mode
+ if (goto_im()) {
+ if (got_int) {
+ (void)vgetc(); // flush all buffers
+ got_int = false;
+ } else {
+ vim_beep(BO_IM);
}
- count = 0;
- goto doESCkey;
+ break;
+ }
+ return 0; // exit insert mode
- case K_INS: /* toggle insert/replace mode */
- case K_KINS:
- ins_insert(replaceState);
+ case Ctrl_Z: // suspend when 'insertmode' set
+ if (!p_im) {
+ goto normalchar; // insert CTRL-Z as normal char
+ }
+ stuffReadbuff((char_u *)":st\r");
+ s->c = Ctrl_O;
+ // FALLTHROUGH
+
+ case Ctrl_O: // execute one command
+ if (ctrl_x_mode == CTRL_X_OMNI) {
+ insert_do_complete(s);
break;
+ }
- case K_SELECT: /* end of Select mode mapping - ignore */
+ if (echeck_abbr(Ctrl_O + ABBR_OFF)) {
break;
+ }
+ ins_ctrl_o();
- case K_HELP: /* Help key works like <ESC> <Help> */
- case K_F1:
- case K_XF1:
- stuffcharReadbuff(K_HELP);
- if (p_im)
- need_start_insertmode = TRUE;
- goto doESCkey;
-
-
- case K_ZERO: /* Insert the previously inserted text. */
- case NUL:
- case Ctrl_A:
- /* For ^@ the trailing ESC will end the insert, unless there is an
- * error. */
- if (stuff_inserted(NUL, 1L, (c == Ctrl_A)) == FAIL
- && c != Ctrl_A && !p_im)
- goto doESCkey; /* quit insert mode */
- inserted_space = FALSE;
- break;
+ // don't move the cursor left when 'virtualedit' has "onemore".
+ if (ve_flags & VE_ONEMORE) {
+ ins_at_eol = false;
+ s->nomove = true;
+ }
- case Ctrl_R: /* insert the contents of a register */
- ins_reg();
- auto_format(FALSE, TRUE);
- inserted_space = FALSE;
- break;
+ s->count = 0;
+ return 0; // exit insert mode
- case Ctrl_G: /* commands starting with CTRL-G */
- ins_ctrl_g();
- break;
+ case K_INS: // toggle insert/replace mode
+ case K_KINS:
+ ins_insert(s->replaceState);
+ break;
- case Ctrl_HAT: /* switch input mode and/or langmap */
- ins_ctrl_hat();
- break;
+ case K_SELECT: // end of Select mode mapping - ignore
+ break;
- case Ctrl__: /* switch between languages */
- if (!p_ari)
- goto normalchar;
- ins_ctrl_();
- break;
- case Ctrl_D: /* Make indent one shiftwidth smaller. */
- if (ctrl_x_mode == CTRL_X_PATH_DEFINES)
- goto docomplete;
- /* FALLTHROUGH */
+ case K_HELP: // Help key works like <ESC> <Help>
+ case K_F1:
+ case K_XF1:
+ stuffcharReadbuff(K_HELP);
+ if (p_im) {
+ need_start_insertmode = true;
+ }
+ return 0; // exit insert mode
- case Ctrl_T: /* Make indent one shiftwidth greater. */
- if (c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) {
- if (has_compl_option(FALSE))
- goto docomplete;
- break;
- }
- ins_shift(c, lastc);
- auto_format(FALSE, TRUE);
- inserted_space = FALSE;
- break;
- case K_DEL: /* delete character under the cursor */
- case K_KDEL:
- ins_del();
- auto_format(FALSE, TRUE);
- break;
+ case K_ZERO: // Insert the previously inserted text.
+ case NUL:
+ case Ctrl_A:
+ // For ^@ the trailing ESC will end the insert, unless there is an
+ // error.
+ if (stuff_inserted(NUL, 1L, (s->c == Ctrl_A)) == FAIL
+ && s->c != Ctrl_A && !p_im) {
+ return 0; // exit insert mode
+ }
+ s->inserted_space = false;
+ break;
- case K_BS: /* delete character before the cursor */
- case Ctrl_H:
- did_backspace = ins_bs(c, BACKSPACE_CHAR, &inserted_space);
- auto_format(FALSE, TRUE);
- break;
+ case Ctrl_R: // insert the contents of a register
+ ins_reg();
+ auto_format(false, true);
+ s->inserted_space = false;
+ break;
- case Ctrl_W: /* delete word before the cursor */
- did_backspace = ins_bs(c, BACKSPACE_WORD, &inserted_space);
- auto_format(FALSE, TRUE);
- break;
+ case Ctrl_G: // commands starting with CTRL-G
+ ins_ctrl_g();
+ break;
- case Ctrl_U: /* delete all inserted text in current line */
- /* CTRL-X CTRL-U completes with 'completefunc'. */
- if (ctrl_x_mode == CTRL_X_FUNCTION)
- goto docomplete;
- did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space);
- auto_format(FALSE, TRUE);
- inserted_space = FALSE;
- break;
+ case Ctrl_HAT: // switch input mode and/or langmap
+ ins_ctrl_hat();
+ break;
- case K_LEFTMOUSE: /* mouse keys */
- case K_LEFTMOUSE_NM:
- case K_LEFTDRAG:
- case K_LEFTRELEASE:
- case K_LEFTRELEASE_NM:
- case K_MIDDLEMOUSE:
- case K_MIDDLEDRAG:
- case K_MIDDLERELEASE:
- case K_RIGHTMOUSE:
- case K_RIGHTDRAG:
- case K_RIGHTRELEASE:
- case K_X1MOUSE:
- case K_X1DRAG:
- case K_X1RELEASE:
- case K_X2MOUSE:
- case K_X2DRAG:
- case K_X2RELEASE:
- ins_mouse(c);
- break;
+ case Ctrl__: // switch between languages
+ if (!p_ari) {
+ goto normalchar;
+ }
+ ins_ctrl_();
+ break;
- case K_MOUSEDOWN: /* Default action for scroll wheel up: scroll up */
- ins_mousescroll(MSCR_DOWN);
+ case Ctrl_D: // Make indent one shiftwidth smaller.
+ if (ctrl_x_mode == CTRL_X_PATH_DEFINES) {
+ insert_do_complete(s);
break;
+ }
+ // FALLTHROUGH
- case K_MOUSEUP: /* Default action for scroll wheel down: scroll down */
- ins_mousescroll(MSCR_UP);
+ case Ctrl_T: // Make indent one shiftwidth greater.
+ if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) {
+ if (has_compl_option(false)) {
+ insert_do_complete(s);
+ }
break;
+ }
+ ins_shift(s->c, s->lastc);
+ auto_format(false, true);
+ s->inserted_space = false;
+ break;
- case K_MOUSELEFT: /* Scroll wheel left */
- ins_mousescroll(MSCR_LEFT);
- break;
+ case K_DEL: // delete character under the cursor
+ case K_KDEL:
+ ins_del();
+ auto_format(false, true);
+ break;
- case K_MOUSERIGHT: /* Scroll wheel right */
- ins_mousescroll(MSCR_RIGHT);
- break;
+ case K_BS: // delete character before the cursor
+ case Ctrl_H:
+ s->did_backspace = ins_bs(s->c, BACKSPACE_CHAR, &s->inserted_space);
+ auto_format(false, true);
+ break;
- case K_IGNORE: /* Something mapped to nothing */
- break;
+ case Ctrl_W: // delete word before the cursor
+ s->did_backspace = ins_bs(s->c, BACKSPACE_WORD, &s->inserted_space);
+ auto_format(false, true);
+ break;
- case K_CURSORHOLD: /* Didn't type something for a while. */
- apply_autocmds(EVENT_CURSORHOLDI, NULL, NULL, FALSE, curbuf);
- did_cursorhold = TRUE;
- break;
+ case Ctrl_U: // delete all inserted text in current line
+ // CTRL-X CTRL-U completes with 'completefunc'.
+ if (ctrl_x_mode == CTRL_X_FUNCTION) {
+ insert_do_complete(s);
+ } else {
+ s->did_backspace = ins_bs(s->c, BACKSPACE_LINE, &s->inserted_space);
+ auto_format(false, true);
+ s->inserted_space = false;
+ }
+ break;
- case K_HOME: /* <Home> */
- case K_KHOME:
- case K_S_HOME:
- case K_C_HOME:
- ins_home(c);
- break;
+ case K_LEFTMOUSE: // mouse keys
+ case K_LEFTMOUSE_NM:
+ case K_LEFTDRAG:
+ case K_LEFTRELEASE:
+ case K_LEFTRELEASE_NM:
+ case K_MIDDLEMOUSE:
+ case K_MIDDLEDRAG:
+ case K_MIDDLERELEASE:
+ case K_RIGHTMOUSE:
+ case K_RIGHTDRAG:
+ case K_RIGHTRELEASE:
+ case K_X1MOUSE:
+ case K_X1DRAG:
+ case K_X1RELEASE:
+ case K_X2MOUSE:
+ case K_X2DRAG:
+ case K_X2RELEASE:
+ ins_mouse(s->c);
+ break;
- case K_END: /* <End> */
- case K_KEND:
- case K_S_END:
- case K_C_END:
- ins_end(c);
- break;
+ case K_MOUSEDOWN: // Default action for scroll wheel up: scroll up
+ ins_mousescroll(MSCR_DOWN);
+ break;
- case K_LEFT: /* <Left> */
- if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))
- ins_s_left();
- else
- ins_left(dont_sync_undo == false);
- break;
+ case K_MOUSEUP: // Default action for scroll wheel down: scroll down
+ ins_mousescroll(MSCR_UP);
+ break;
+
+ case K_MOUSELEFT: // Scroll wheel left
+ ins_mousescroll(MSCR_LEFT);
+ break;
+
+ case K_MOUSERIGHT: // Scroll wheel right
+ ins_mousescroll(MSCR_RIGHT);
+ break;
+
+ case K_IGNORE: // Something mapped to nothing
+ break;
- case K_S_LEFT: /* <S-Left> */
- case K_C_LEFT:
+ case K_EVENT: // some event
+ queue_process_events(loop.events);
+ break;
+
+ case K_HOME: // <Home>
+ case K_KHOME:
+ case K_S_HOME:
+ case K_C_HOME:
+ ins_home(s->c);
+ break;
+
+ case K_END: // <End>
+ case K_KEND:
+ case K_S_END:
+ case K_C_END:
+ ins_end(s->c);
+ break;
+
+ case K_LEFT: // <Left>
+ if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) {
ins_s_left();
- break;
+ } else {
+ ins_left(dont_sync_undo == false);
+ }
+ break;
- case K_RIGHT: /* <Right> */
- if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))
- ins_s_right();
- else
- ins_right(dont_sync_undo == false);
- break;
+ case K_S_LEFT: // <S-Left>
+ case K_C_LEFT:
+ ins_s_left();
+ break;
- case K_S_RIGHT: /* <S-Right> */
- case K_C_RIGHT:
+ case K_RIGHT: // <Right>
+ if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) {
ins_s_right();
- break;
+ } else {
+ ins_right(dont_sync_undo == false);
+ }
+ break;
- case K_UP: /* <Up> */
- if (pum_visible())
- goto docomplete;
- if (mod_mask & MOD_MASK_SHIFT)
- ins_pageup();
- else
- ins_up(FALSE);
- break;
+ case K_S_RIGHT: // <S-Right>
+ case K_C_RIGHT:
+ ins_s_right();
+ break;
- case K_S_UP: /* <S-Up> */
- case K_PAGEUP:
- case K_KPAGEUP:
- if (pum_visible())
- goto docomplete;
+ case K_UP: // <Up>
+ if (pum_visible()) {
+ insert_do_complete(s);
+ } else if (mod_mask & MOD_MASK_SHIFT) {
ins_pageup();
- break;
+ } else {
+ ins_up(false);
+ }
+ break;
- case K_DOWN: /* <Down> */
- if (pum_visible())
- goto docomplete;
- if (mod_mask & MOD_MASK_SHIFT)
- ins_pagedown();
- else
- ins_down(FALSE);
- break;
+ case K_S_UP: // <S-Up>
+ case K_PAGEUP:
+ case K_KPAGEUP:
+ if (pum_visible()) {
+ insert_do_complete(s);
+ } else {
+ ins_pageup();
+ }
+ break;
- case K_S_DOWN: /* <S-Down> */
- case K_PAGEDOWN:
- case K_KPAGEDOWN:
- if (pum_visible())
- goto docomplete;
+ case K_DOWN: // <Down>
+ if (pum_visible()) {
+ insert_do_complete(s);
+ } else if (mod_mask & MOD_MASK_SHIFT) {
ins_pagedown();
- break;
+ } else {
+ ins_down(false);
+ }
+ break;
+
+ case K_S_DOWN: // <S-Down>
+ case K_PAGEDOWN:
+ case K_KPAGEDOWN:
+ if (pum_visible()) {
+ insert_do_complete(s);
+ } else {
+ ins_pagedown();
+ }
+ break;
- case K_S_TAB: /* When not mapped, use like a normal TAB */
- c = TAB;
- /* FALLTHROUGH */
+ case K_S_TAB: // When not mapped, use like a normal TAB
+ s->c = TAB;
+ // FALLTHROUGH
- case TAB: /* TAB or Complete patterns along path */
- if (ctrl_x_mode == CTRL_X_PATH_PATTERNS)
- goto docomplete;
- inserted_space = FALSE;
- if (ins_tab())
- goto normalchar; /* insert TAB as a normal char */
- auto_format(FALSE, TRUE);
+ case TAB: // TAB or Complete patterns along path
+ if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) {
+ insert_do_complete(s);
break;
+ }
+ s->inserted_space = false;
+ if (ins_tab()) {
+ goto normalchar; // insert TAB as a normal char
+ }
+ auto_format(false, true);
+ break;
- case K_KENTER: /* <Enter> */
- c = CAR;
- /* FALLTHROUGH */
- case CAR:
- case NL:
- /* In a quickfix window a <CR> jumps to the error under the
- * cursor. */
- if (bt_quickfix(curbuf) && c == CAR) {
- if (curwin->w_llist_ref == NULL) /* quickfix window */
- do_cmdline_cmd(".cc");
- else /* location list window */
- do_cmdline_cmd(".ll");
- break;
- }
- if (cmdwin_type != 0) {
- /* Execute the command in the cmdline window. */
- cmdwin_result = CAR;
- goto doESCkey;
+ case K_KENTER: // <Enter>
+ s->c = CAR;
+ // FALLTHROUGH
+ case CAR:
+ case NL:
+ // In a quickfix window a <CR> jumps to the error under the
+ // cursor.
+ if (bt_quickfix(curbuf) && s->c == CAR) {
+ if (curwin->w_llist_ref == NULL) { // quickfix window
+ do_cmdline_cmd(".cc");
+ } else { // location list window
+ do_cmdline_cmd(".ll");
}
- if (ins_eol(c) && !p_im)
- goto doESCkey; /* out of memory */
- auto_format(FALSE, FALSE);
- inserted_space = FALSE;
break;
+ }
+ if (cmdwin_type != 0) {
+ // Execute the command in the cmdline window.
+ cmdwin_result = CAR;
+ return 0;
+ }
+ if (ins_eol(s->c) && !p_im) {
+ return 0; // out of memory
+ }
+ auto_format(false, false);
+ s->inserted_space = false;
+ break;
- case Ctrl_K: /* digraph or keyword completion */
- if (ctrl_x_mode == CTRL_X_DICTIONARY) {
- if (has_compl_option(TRUE))
- goto docomplete;
- break;
+ case Ctrl_K: // digraph or keyword completion
+ if (ctrl_x_mode == CTRL_X_DICTIONARY) {
+ if (has_compl_option(true)) {
+ insert_do_complete(s);
}
- c = ins_digraph();
- if (c == NUL)
- break;
- goto normalchar;
+ break;
+ }
- case Ctrl_X: /* Enter CTRL-X mode */
- ins_ctrl_x();
+ s->c = ins_digraph();
+ if (s->c == NUL) {
break;
+ }
+ goto normalchar;
- case Ctrl_RSB: /* Tag name completion after ^X */
- if (ctrl_x_mode != CTRL_X_TAGS)
- goto normalchar;
- goto docomplete;
+ case Ctrl_X: // Enter CTRL-X mode
+ ins_ctrl_x();
+ break;
- case Ctrl_F: /* File name completion after ^X */
- if (ctrl_x_mode != CTRL_X_FILES)
- goto normalchar;
- goto docomplete;
+ case Ctrl_RSB: // Tag name completion after ^X
+ if (ctrl_x_mode != CTRL_X_TAGS) {
+ goto normalchar;
+ } else {
+ insert_do_complete(s);
+ }
+ break;
- case 's': /* Spelling completion after ^X */
- case Ctrl_S:
- if (ctrl_x_mode != CTRL_X_SPELL)
- goto normalchar;
- goto docomplete;
-
- case Ctrl_L: /* Whole line completion after ^X */
- if (ctrl_x_mode != CTRL_X_WHOLE_LINE) {
- /* CTRL-L with 'insertmode' set: Leave Insert mode */
- if (p_im) {
- if (echeck_abbr(Ctrl_L + ABBR_OFF))
- break;
- goto doESCkey;
+ case Ctrl_F: // File name completion after ^X
+ if (ctrl_x_mode != CTRL_X_FILES) {
+ goto normalchar;
+ } else {
+ insert_do_complete(s);
+ }
+ break;
+
+ case 's': // Spelling completion after ^X
+ case Ctrl_S:
+ if (ctrl_x_mode != CTRL_X_SPELL) {
+ goto normalchar;
+ } else {
+ insert_do_complete(s);
+ }
+ break;
+
+ case Ctrl_L: // Whole line completion after ^X
+ if (ctrl_x_mode != CTRL_X_WHOLE_LINE) {
+ // CTRL-L with 'insertmode' set: Leave Insert mode
+ if (p_im) {
+ if (echeck_abbr(Ctrl_L + ABBR_OFF)) {
+ break;
}
- goto normalchar;
+ return 0; // exit insert mode
}
- /* FALLTHROUGH */
+ goto normalchar;
+ }
+ // FALLTHROUGH
- case Ctrl_P: /* Do previous/next pattern completion */
- case Ctrl_N:
- /* if 'complete' is empty then plain ^P is no longer special,
- * but it is under other ^X modes */
- if (*curbuf->b_p_cpt == NUL
- && ctrl_x_mode != 0
- && !(compl_cont_status & CONT_LOCAL))
- goto normalchar;
-
-docomplete:
- compl_busy = TRUE;
- if (ins_complete(c) == FAIL)
- compl_cont_status = 0;
- compl_busy = FALSE;
- break;
+ case Ctrl_P: // Do previous/next pattern completion
+ case Ctrl_N:
+ // if 'complete' is empty then plain ^P is no longer special,
+ // but it is under other ^X modes
+ if (*curbuf->b_p_cpt == NUL
+ && ctrl_x_mode != 0
+ && !(compl_cont_status & CONT_LOCAL)) {
+ goto normalchar;
+ }
- case Ctrl_Y: /* copy from previous line or scroll down */
- case Ctrl_E: /* copy from next line or scroll up */
- c = ins_ctrl_ey(c);
- break;
+ insert_do_complete(s);
+ break;
- default:
-#ifdef UNIX
- if (c == intr_char) /* special interrupt char */
- goto do_intr;
-#endif
+ case Ctrl_Y: // copy from previous line or scroll down
+ case Ctrl_E: // copy from next line or scroll up
+ s->c = ins_ctrl_ey(s->c);
+ break;
-normalchar:
- /*
- * Insert a normal character.
- */
- if (!p_paste) {
- /* Trigger InsertCharPre. */
- char_u *str = do_insert_char_pre(c);
- char_u *p;
+ default:
- if (str != NULL) {
- if (*str != NUL && stop_arrow() != FAIL) {
- /* Insert the new value of v:char literally. */
- for (p = str; *p != NUL; mb_ptr_adv(p)) {
- c = PTR2CHAR(p);
- if (c == CAR || c == K_KENTER || c == NL)
- ins_eol(c);
- else
- ins_char(c);
+normalchar:
+ // Insert a normal character.
+ if (!p_paste) {
+ // Trigger InsertCharPre.
+ char_u *str = do_insert_char_pre(s->c);
+ char_u *p;
+
+ if (str != NULL) {
+ if (*str != NUL && stop_arrow() != FAIL) {
+ // Insert the new value of v:char literally.
+ for (p = str; *p != NUL; mb_ptr_adv(p)) {
+ s->c = PTR2CHAR(p);
+ if (s->c == CAR || s->c == K_KENTER || s->c == NL) {
+ ins_eol(s->c);
+ } else {
+ ins_char(s->c);
}
- AppendToRedobuffLit(str, -1);
}
- xfree(str);
- c = NUL;
+ AppendToRedobuffLit(str, -1);
}
+ xfree(str);
+ s->c = NUL;
+ }
- /* If the new value is already inserted or an empty string
- * then don't insert any character. */
- if (c == NUL)
- break;
+ // If the new value is already inserted or an empty string
+ // then don't insert any character.
+ if (s->c == NUL)
+ break;
+ }
+ // Try to perform smart-indenting.
+ ins_try_si(s->c);
+
+ if (s->c == ' ') {
+ s->inserted_space = true;
+ if (inindent(0)) {
+ can_cindent = false;
}
- /* Try to perform smart-indenting. */
- ins_try_si(c);
-
- if (c == ' ') {
- inserted_space = TRUE;
- if (inindent(0))
- can_cindent = FALSE;
- if (Insstart_blank_vcol == MAXCOL
- && curwin->w_cursor.lnum == Insstart.lnum)
- Insstart_blank_vcol = get_nolist_virtcol();
+ if (Insstart_blank_vcol == MAXCOL
+ && curwin->w_cursor.lnum == Insstart.lnum) {
+ Insstart_blank_vcol = get_nolist_virtcol();
}
+ }
- /* Insert a normal character and check for abbreviations on a
- * special character. Let CTRL-] expand abbreviations without
- * inserting it. */
- if (vim_iswordc(c) || (!echeck_abbr(
- /* Add ABBR_OFF for characters above 0x100, this is
- * what check_abbr() expects. */
- (has_mbyte && c >= 0x100) ? (c + ABBR_OFF) :
- c) && c != Ctrl_RSB)) {
- insert_special(c, FALSE, FALSE);
- revins_legal++;
- revins_chars++;
- }
+ // Insert a normal character and check for abbreviations on a
+ // special character. Let CTRL-] expand abbreviations without
+ // inserting it.
+ if (vim_iswordc(s->c)
+ || (!echeck_abbr(
+ // Add ABBR_OFF for characters above 0x100, this is
+ // what check_abbr() expects.
+ (has_mbyte && s->c >= 0x100) ? (s->c + ABBR_OFF) : s->c)
+ && s->c != Ctrl_RSB)) {
+ insert_special(s->c, false, false);
+ revins_legal++;
+ revins_chars++;
+ }
- auto_format(FALSE, TRUE);
+ auto_format(false, true);
- /* When inserting a character the cursor line must never be in a
- * closed fold. */
- foldOpenCursor();
- break;
- } /* end of switch (c) */
+ // When inserting a character the cursor line must never be in a
+ // closed fold.
+ foldOpenCursor();
+ break;
+ } // end of switch (s->c)
- /* If typed something may trigger CursorHoldI again. */
- if (c != K_CURSORHOLD)
- did_cursorhold = FALSE;
+ return 1; // continue
+}
- /* If the cursor was moved we didn't just insert a space */
- if (arrow_used)
- inserted_space = FALSE;
+static void insert_do_complete(InsertState *s)
+{
+ compl_busy = true;
+ if (ins_complete(s->c) == FAIL) {
+ compl_cont_status = 0;
+ }
+ compl_busy = false;
+}
- if (can_cindent && cindent_on()
- && ctrl_x_mode == 0
- ) {
-force_cindent:
- /*
- * Indent now if a key was typed that is in 'cinkeys'.
- */
- if (in_cinkeys(c, ' ', line_is_white)) {
- if (stop_arrow() == OK)
- /* re-indent the current line */
- do_c_expr_indent();
- }
+static void insert_do_cindent(InsertState *s)
+{
+ // Indent now if a key was typed that is in 'cinkeys'.
+ if (in_cinkeys(s->c, ' ', s->line_is_white)) {
+ if (stop_arrow() == OK) {
+ // re-indent the current line
+ do_c_expr_indent();
}
+ }
+}
- } /* for (;;) */
- /* NOTREACHED */
+/*
+ * edit(): Start inserting text.
+ *
+ * "cmdchar" can be:
+ * 'i' normal insert command
+ * 'a' normal append command
+ * 'R' replace command
+ * 'r' "r<CR>" command: insert one <CR>. Note: count can be > 1, for redo,
+ * but still only one <CR> is inserted. The <Esc> is not used for redo.
+ * 'g' "gI" command.
+ * 'V' "gR" command for Virtual Replace mode.
+ * 'v' "gr" command for single character Virtual Replace mode.
+ *
+ * This function is not called recursively. For CTRL-O commands, it returns
+ * and lets the caller handle the Normal-mode command.
+ *
+ * Return TRUE if a CTRL-O command caused the return (insert mode pending).
+ */
+int
+edit (
+ int cmdchar,
+ int startln, /* if set, insert at start of line */
+ long count
+)
+{
+ if (curbuf->terminal) {
+ if (ex_normal_busy) {
+ // don't enter terminal mode from `ex_normal`, which can result in all
+ // kinds of havoc(such as terminal mode recursiveness). Instead, set a
+ // flag that allow us to force-set the value of `restart_edit` before
+ // `ex_normal` returns
+ restart_edit = 'i';
+ force_restart_edit = true;
+ } else {
+ terminal_enter();
+ }
+ return false;
+ }
+
+ // Don't allow inserting in the sandbox.
+ if (sandbox != 0) {
+ EMSG(_(e_sandbox));
+ return false;
+ }
+
+ // Don't allow changes in the buffer while editing the cmdline. The
+ // caller of getcmdline() may get confused.
+ if (textlock != 0) {
+ EMSG(_(e_secure));
+ return false;
+ }
+
+ // Don't allow recursive insert mode when busy with completion.
+ if (compl_started || compl_busy || pum_visible()) {
+ EMSG(_(e_secure));
+ return false;
+ }
+
+ InsertState state, *s = &state;
+ memset(s, 0, sizeof(InsertState));
+ s->state.execute = insert_execute;
+ s->state.check = insert_check;
+ s->cmdchar = cmdchar;
+ s->startln = startln;
+ s->count = count;
+ insert_enter(s);
+ return s->c == Ctrl_O;
}
/*
@@ -7697,12 +7782,8 @@ static void ins_left(bool end_change)
if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol)
revins_legal++;
revins_chars++;
- }
- /*
- * if 'whichwrap' set for cursor in insert mode may go to
- * previous line
- */
- else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
+ } else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
+ // if 'whichwrap' set for cursor in insert mode may go to previous line.
// always break undo when moving upwards/downwards, else undo may break
start_arrow(&tpos);
--(curwin->w_cursor.lnum);