diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/edit.c | 1449 |
1 files changed, 728 insertions, 721 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index c6ebe376e2..c4ebf4cac4 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -254,52 +254,10 @@ static int did_add_space = FALSE; /* auto_format() added an extra space static int dont_sync_undo = false; // CTRL-G U prevents syncing undo // for the next left/right cursor +static linenr_T o_lnum = 0; -/* - * 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 -) +static void insert_enter(InsertState *s) { - static linenr_T o_lnum = 0; - InsertState state, *s = &state; - memset(s, 0, sizeof(InsertState)); - 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; - } - - s->cmdchar = cmdchar; - s->startln = startln; - s->count = count; s->did_backspace = true; s->old_topfill = -1; s->replaceState = REPLACE; @@ -311,34 +269,15 @@ edit ( // 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 // Trigger InsertEnter autocommands. Do not do this for "r<CR>" or "grx". - if (cmdchar != 'r' && cmdchar != 'v') { + if (s->cmdchar != 'r' && s->cmdchar != 'v') { pos_T save_cursor = curwin->w_cursor; - if (cmdchar == 'R') { + if (s->cmdchar == 'R') { s->ptr = (char_u *)"r"; - } else if (cmdchar == 'V') { + } else if (s->cmdchar == 'V') { s->ptr = (char_u *)"v"; } else { s->ptr = (char_u *)"i"; @@ -376,7 +315,7 @@ edit ( Insstart = where_paste_started; } else { Insstart = curwin->w_cursor; - if (startln) { + if (s->startln) { Insstart.col = 0; } } @@ -388,24 +327,24 @@ edit ( ai_col = 0; } - if (cmdchar != NUL && restart_edit == 0) { + if (s->cmdchar != NUL && restart_edit == 0) { ResetRedobuff(); - AppendNumberToRedobuff(count); - if (cmdchar == 'V' || cmdchar == 'v') { + 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 @@ -413,7 +352,7 @@ edit ( } else { State = REPLACE; } - } else if (cmdchar == 'V' || cmdchar == 'v') { + } else if (s->cmdchar == 'V' || s->cmdchar == 'v') { State = VREPLACE; s->replaceState = VREPLACE; orig_line_count = curbuf->b_ml.ml_line_count; @@ -525,784 +464,852 @@ edit ( old_indent = 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)); - // 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; - } + // 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 (update_Insstart_orig) { - Insstart_orig = Insstart; - } + if (s->cmdchar != 'r' && s->cmdchar != 'v') { + apply_autocmds(EVENT_INSERTLEAVE, NULL, NULL, false, curbuf); + } + did_cursorhold = false; +} - if (stop_insert_mode) { - // ":stopinsert" used or 'insertmode' reset - count = 0; - goto doESCkey; - } +static int insert_check(VimState *state) +{ + InsertState *s = (InsertState *)state; + if (!revins_legal) { + revins_scol = -1; // reset on illegal motions + } else { + revins_legal = 0; + } - // set curwin->w_curswant for next K_DOWN or K_UP - if (!arrow_used) { - curwin->w_set_curswant = true; - } + if (arrow_used) { // don't repeat insert when arrow key used + s->count = 0; + } - // 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 (update_Insstart_orig) { + Insstart_orig = Insstart; + } - // When emsg() was called msg_scroll will have been set. - msg_scroll = false; + 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; + } - // Open fold at the cursor line, according to 'foldopen'. - if (fdo_flags & FDO_INSERT) { - foldOpenCursor(); + // 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); } + } - // Close folds where the cursor isn't, according to 'foldclose' - if (!char_avail()) { - foldCheckClose(); - } + // When emsg() was called msg_scroll will have been set. + msg_scroll = false; - // 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); - } + // 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(); + } + + // 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(); - s->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(); - } + if (curwin->w_p_crb) { + do_check_cursorbind(); + } - update_curswant(); - s->old_topline = curwin->w_topline; - s->old_topfill = curwin->w_topfill; + 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. - s->lastc = s->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; +} + +static int insert_execute(VimState *state, int key) +{ + if (key == K_IGNORE) { + return -1; // get another key + } + InsertState *s = (InsertState *)state; + s->c = key; + + // Don't want K_EVENT with 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_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 ((s->c == K_BS || s->c == Ctrl_H) + && curwin->w_cursor.col > compl_col + && (s->c = ins_compl_bs()) == NUL) { + return 1; // continue } - input_enable_events(); - do { - s->c = safe_vgetc(); - } while (s->c == K_IGNORE); - input_disable_events(); - - // Don't want K_EVENT with 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_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 ((s->c == K_BS || s->c == Ctrl_H) - && curwin->w_cursor.col > compl_col - && (s->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 (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 } - // 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(); - 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; + // 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; - 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); + if (str != NULL) { + for (p = str; *p != NUL; mb_ptr_adv(p)) { + ins_compl_addleader(PTR2CHAR(p)); } - continue; + 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(); - } + // 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(s->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 (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) { - continue; - } else { - if (s->c == Ctrl_O) { - ins_ctrl_o(); - ins_at_eol = false; // cursor keeps its column - s->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; } + } - s->c = do_digraph(s->c); + s->c = do_digraph(s->c); - if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) { - goto docomplete; - } + if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) { + goto docomplete; + } - if (s->c == Ctrl_V || s->c == Ctrl_Q) { - ins_ctrl_v(); - s->c = Ctrl_V; // pretend CTRL-V is last typed character - continue; - } + 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)) { - goto force_cindent; - } + 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)) { + goto force_cindent; + } - if (can_cindent && in_cinkeys(s->c, '*', s->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 (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 (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(s->c)) { + return 1; // continue + } + + // 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 - // 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)) { - continue; + 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 } - // 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; + // 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); } - // FALLTHROUGH + break; + } + return 0; // exit insert mode - 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; - goto doESCkey; - } + 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 -#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; - } + case Ctrl_O: // execute one command + if (ctrl_x_mode == CTRL_X_OMNI) { + goto docomplete; + } - if (ins_esc(&count, cmdchar, s->nomove)) { - if (cmdchar != 'r' && cmdchar != 'v') { - apply_autocmds(EVENT_INSERTLEAVE, NULL, NULL, false, curbuf); - } - did_cursorhold = false; - return s->c == Ctrl_O; - } - continue; + if (echeck_abbr(Ctrl_O + ABBR_OFF)) { + break; + } - 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 + ins_ctrl_o(); - case Ctrl_O: // execute one command - if (ctrl_x_mode == CTRL_X_OMNI) { - goto docomplete; - } + // don't move the cursor left when 'virtualedit' has "onemore". + if (ve_flags & VE_ONEMORE) { + ins_at_eol = false; + s->nomove = true; + } - if (echeck_abbr(Ctrl_O + ABBR_OFF)) { - break; - } + s->count = 0; + return 0; // exit insert mode - ins_ctrl_o(); + case K_INS: // toggle insert/replace mode + case K_KINS: + ins_insert(s->replaceState); + break; - // don't move the cursor left when 'virtualedit' has "onemore". - if (ve_flags & VE_ONEMORE) { - ins_at_eol = false; - s->nomove = true; - } + case K_SELECT: // end of Select mode mapping - ignore + break; - count = 0; - goto doESCkey; - case K_INS: // toggle insert/replace mode - case K_KINS: - ins_insert(s->replaceState); - break; + 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 K_SELECT: // end of Select mode mapping - ignore - 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_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, (s->c == Ctrl_A)) == FAIL - && s->c != Ctrl_A && !p_im) - goto doESCkey; // quit insert mode - s->inserted_space = false; - break; + case Ctrl_R: // insert the contents of a register + ins_reg(); + auto_format(false, true); + s->inserted_space = false; + break; - case Ctrl_R: // insert the contents of a register - ins_reg(); - auto_format(false, true); - s->inserted_space = false; - break; + case Ctrl_G: // commands starting with CTRL-G + ins_ctrl_g(); + break; - case Ctrl_G: // commands starting with CTRL-G - ins_ctrl_g(); - break; + case Ctrl_HAT: // switch input mode and/or langmap + ins_ctrl_hat(); + break; - case Ctrl_HAT: // switch input mode and/or langmap - ins_ctrl_hat(); - break; + case Ctrl__: // switch between languages + if (!p_ari) { + goto normalchar; + } + ins_ctrl_(); + 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 Ctrl_D: // Make indent one shiftwidth smaller. - if (ctrl_x_mode == CTRL_X_PATH_DEFINES) { + case Ctrl_T: // Make indent one shiftwidth greater. + if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { + if (has_compl_option(false)) { goto docomplete; } - // FALLTHROUGH - - case Ctrl_T: // Make indent one shiftwidth greater. - if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { - if (has_compl_option(false)) { - goto docomplete; - } - break; - } - ins_shift(s->c, s->lastc); - auto_format(false, true); - s->inserted_space = false; - break; - - case K_DEL: // delete character under the cursor - case K_KDEL: - ins_del(); - auto_format(false, true); break; + } + ins_shift(s->c, s->lastc); + auto_format(false, true); + s->inserted_space = false; + 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_DEL: // delete character under the cursor + case K_KDEL: + ins_del(); + auto_format(false, true); + 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_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 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; - } + 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; - s->did_backspace = ins_bs(s->c, BACKSPACE_LINE, &s->inserted_space); - auto_format(false, true); - s->inserted_space = false; - 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; + } - 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; + s->did_backspace = ins_bs(s->c, BACKSPACE_LINE, &s->inserted_space); + auto_format(false, true); + s->inserted_space = false; + break; - case K_MOUSEDOWN: // Default action for scroll wheel up: scroll up - ins_mousescroll(MSCR_DOWN); - 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_MOUSEUP: // Default action for scroll wheel down: scroll down - ins_mousescroll(MSCR_UP); - break; + case K_MOUSEDOWN: // Default action for scroll wheel up: scroll up + ins_mousescroll(MSCR_DOWN); + break; - case K_MOUSELEFT: // Scroll wheel left - ins_mousescroll(MSCR_LEFT); - break; + case K_MOUSEUP: // Default action for scroll wheel down: scroll down + ins_mousescroll(MSCR_UP); + break; - case K_MOUSERIGHT: // Scroll wheel right - ins_mousescroll(MSCR_RIGHT); - break; + case K_MOUSELEFT: // Scroll wheel left + ins_mousescroll(MSCR_LEFT); + break; - case K_IGNORE: // Something mapped to nothing - break; + case K_MOUSERIGHT: // Scroll wheel right + ins_mousescroll(MSCR_RIGHT); + break; - case K_EVENT: // some event - queue_process_events(loop.events); - break; + case K_IGNORE: // Something mapped to nothing + break; - case K_HOME: // <Home> - case K_KHOME: - case K_S_HOME: - case K_C_HOME: - ins_home(s->c); - break; + case K_EVENT: // some event + queue_process_events(loop.events); + break; - case K_END: // <End> - case K_KEND: - case K_S_END: - case K_C_END: - ins_end(s->c); - break; + case K_HOME: // <Home> + case K_KHOME: + case K_S_HOME: + case K_C_HOME: + ins_home(s->c); + 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_END: // <End> + case K_KEND: + case K_S_END: + case K_C_END: + ins_end(s->c); + break; - case K_S_LEFT: // <S-Left> - case K_C_LEFT: + 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; - } + case K_S_RIGHT: // <S-Right> + case K_C_RIGHT: + ins_s_right(); + break; - if (mod_mask & MOD_MASK_SHIFT) { - ins_pageup(); - } else { - ins_up(false); - } - break; + case K_UP: // <Up> + if (pum_visible()) { + goto docomplete; + } - case K_S_UP: // <S-Up> - case K_PAGEUP: - case K_KPAGEUP: - if (pum_visible()) { - goto docomplete; - } + if (mod_mask & MOD_MASK_SHIFT) { ins_pageup(); - break; - - case K_DOWN: // <Down> - if (pum_visible()) { - goto docomplete; - } + } else { + ins_up(false); + } + break; - 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()) { + goto docomplete; + } + 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()) { + goto docomplete; + } + 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()) { + goto docomplete; + } - case K_S_TAB: // When not mapped, use like a normal TAB - s->c = TAB; - // FALLTHROUGH + ins_pagedown(); + break; - case TAB: // TAB or Complete patterns along path - if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) { - goto docomplete; - } - s->inserted_space = false; - if (ins_tab()) { - goto normalchar; // insert TAB as a normal char - } - auto_format(false, true); - break; - 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"); - } - break; - } - if (cmdwin_type != 0) { - // Execute the command in the cmdline window. - cmdwin_result = CAR; - goto doESCkey; - } - if (ins_eol(s->c) && !p_im) { - goto doESCkey; // out of memory - } - auto_format(false, false); - s->inserted_space = false; - break; + case K_S_TAB: // When not mapped, use like a normal TAB + s->c = TAB; + // FALLTHROUGH - case Ctrl_K: // digraph or keyword completion - if (ctrl_x_mode == CTRL_X_DICTIONARY) { - if (has_compl_option(true)) { - goto docomplete; - } - break; + case TAB: // TAB or Complete patterns along path + if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) { + goto docomplete; + } + s->inserted_space = false; + if (ins_tab()) { + goto normalchar; // insert TAB as a normal char + } + auto_format(false, true); + break; + + 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"); } + 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; - s->c = ins_digraph(); - if (s->c == NUL) { - break; + case Ctrl_K: // digraph or keyword completion + if (ctrl_x_mode == CTRL_X_DICTIONARY) { + if (has_compl_option(true)) { + goto docomplete; } - 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; + } + goto docomplete; - case 's': // Spelling completion after ^X - case Ctrl_S: - if (ctrl_x_mode != CTRL_X_SPELL) { - goto normalchar; - } - goto docomplete; + case Ctrl_F: // File name completion after ^X + if (ctrl_x_mode != CTRL_X_FILES) { + 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 '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 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; - } + 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(s->c) == FAIL) { - compl_cont_status = 0; - } - compl_busy = false; - break; + compl_busy = true; + if (ins_complete(s->c) == FAIL) { + compl_cont_status = 0; + } + compl_busy = false; + break; - 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; + 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; - default: -#ifdef UNIX - if (s->c == intr_char) // special interrupt char - goto do_intr; -#endif + default: 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); - } + // 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); - s->c = NUL; + AppendToRedobuffLit(str, -1); } - - // If the new value is already inserted or an empty string - // then don't insert any character. - if (s->c == NUL) - break; + xfree(str); + s->c = NUL; } - // Try to perform smart-indenting. - ins_try_si(s->c); - if (s->c == ' ') { - s->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 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); - // 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++; + if (s->c == ' ') { + s->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(); } + } - auto_format(false, true); + // 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++; + } - // When inserting a character the cursor line must never be in a - // closed fold. - foldOpenCursor(); - break; - } // end of switch (s->c) + auto_format(false, true); - // If typed something may trigger CursorHoldI again. - if (s->c != K_EVENT) { - did_cursorhold = false; - } + // When inserting a character the cursor line must never be in a + // closed fold. + foldOpenCursor(); + break; + } // end of switch (s->c) - // If the cursor was moved we didn't just insert a space */ - if (arrow_used) { - s->inserted_space = false; - } + // If typed something may trigger CursorHoldI again. + if (s->c != K_EVENT) { + did_cursorhold = false; + } - if (can_cindent && cindent_on() && ctrl_x_mode == 0) { + // 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) { force_cindent: - // 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(); - } + // 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 + } + + return 1; // continue +} + +/* + * 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; } /* |