neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

edit.c (129514B)


      1 // edit.c: functions for Insert mode
      2 
      3 #include <assert.h>
      4 #include <ctype.h>
      5 #include <inttypes.h>
      6 #include <stdbool.h>
      7 #include <stddef.h>
      8 #include <string.h>
      9 #include <uv.h>
     10 
     11 #include "klib/kvec.h"
     12 #include "nvim/api/private/defs.h"
     13 #include "nvim/ascii_defs.h"
     14 #include "nvim/autocmd.h"
     15 #include "nvim/autocmd_defs.h"
     16 #include "nvim/buffer.h"
     17 #include "nvim/buffer_defs.h"
     18 #include "nvim/change.h"
     19 #include "nvim/charset.h"
     20 #include "nvim/cursor.h"
     21 #include "nvim/decoration.h"
     22 #include "nvim/digraph.h"
     23 #include "nvim/drawscreen.h"
     24 #include "nvim/edit.h"
     25 #include "nvim/errors.h"
     26 #include "nvim/eval.h"
     27 #include "nvim/eval/typval_defs.h"
     28 #include "nvim/eval/vars.h"
     29 #include "nvim/ex_cmds_defs.h"
     30 #include "nvim/ex_docmd.h"
     31 #include "nvim/extmark.h"
     32 #include "nvim/extmark_defs.h"
     33 #include "nvim/fileio.h"
     34 #include "nvim/fold.h"
     35 #include "nvim/getchar.h"
     36 #include "nvim/gettext_defs.h"
     37 #include "nvim/globals.h"
     38 #include "nvim/grid.h"
     39 #include "nvim/grid_defs.h"
     40 #include "nvim/highlight.h"
     41 #include "nvim/highlight_defs.h"
     42 #include "nvim/highlight_group.h"
     43 #include "nvim/indent.h"
     44 #include "nvim/indent_c.h"
     45 #include "nvim/insexpand.h"
     46 #include "nvim/keycodes.h"
     47 #include "nvim/macros_defs.h"
     48 #include "nvim/mapping.h"
     49 #include "nvim/mark.h"
     50 #include "nvim/mark_defs.h"
     51 #include "nvim/marktree_defs.h"
     52 #include "nvim/mbyte.h"
     53 #include "nvim/mbyte_defs.h"
     54 #include "nvim/memline.h"
     55 #include "nvim/memline_defs.h"
     56 #include "nvim/memory.h"
     57 #include "nvim/message.h"
     58 #include "nvim/mouse.h"
     59 #include "nvim/move.h"
     60 #include "nvim/normal.h"
     61 #include "nvim/normal_defs.h"
     62 #include "nvim/ops.h"
     63 #include "nvim/option.h"
     64 #include "nvim/option_vars.h"
     65 #include "nvim/os/input.h"
     66 #include "nvim/plines.h"
     67 #include "nvim/popupmenu.h"
     68 #include "nvim/pos_defs.h"
     69 #include "nvim/register.h"
     70 #include "nvim/search.h"
     71 #include "nvim/state.h"
     72 #include "nvim/state_defs.h"
     73 #include "nvim/strings.h"
     74 #include "nvim/syntax.h"
     75 #include "nvim/terminal.h"
     76 #include "nvim/textformat.h"
     77 #include "nvim/textobject.h"
     78 #include "nvim/types_defs.h"
     79 #include "nvim/ui.h"
     80 #include "nvim/ui_defs.h"
     81 #include "nvim/undo.h"
     82 #include "nvim/vim_defs.h"
     83 #include "nvim/window.h"
     84 
     85 typedef struct {
     86  VimState state;
     87  cmdarg_T *ca;
     88  int mincol;
     89  int cmdchar;
     90  int cmdchar_todo;                  // cmdchar to handle once in init_prompt
     91  bool ins_just_started;
     92  int startln;
     93  int count;
     94  int c;
     95  int lastc;
     96  int i;
     97  bool did_backspace;                // previous char was backspace
     98  bool line_is_white;                // line is empty before insert
     99  linenr_T old_topline;              // topline before insertion
    100  int old_topfill;
    101  int inserted_space;                // just inserted a space
    102  int replaceState;
    103  int did_restart_edit;              // remember if insert mode was restarted
    104                                     // after a ctrl+o
    105  bool nomove;
    106 } InsertState;
    107 
    108 #include "edit.c.generated.h"
    109 enum {
    110  BACKSPACE_CHAR = 1,
    111  BACKSPACE_WORD = 2,
    112  BACKSPACE_WORD_NOT_SPACE = 3,
    113  BACKSPACE_LINE = 4,
    114 };
    115 
    116 /// Set when doing something for completion that may call edit() recursively,
    117 /// which is not allowed.
    118 static bool compl_busy = false;
    119 
    120 static colnr_T Insstart_textlen;        // length of line when insert started
    121 static colnr_T Insstart_blank_vcol;     // vcol for first inserted blank
    122 static bool update_Insstart_orig = true;  // set Insstart_orig to Insstart
    123 
    124 /// the text of the previous insert, K_SPECIAL is escaped
    125 static String last_insert = STRING_INIT;
    126 static int last_insert_skip;            // nr of chars in front of previous insert
    127 static int new_insert_skip;             // nr of chars in front of current insert
    128 static int did_restart_edit;            // "restart_edit" when calling edit()
    129 
    130 static bool can_cindent;                // may do cindenting on this line
    131 
    132 static bool revins_on;                  // reverse insert mode on
    133 static int revins_chars;                // how much to skip after edit
    134 static int revins_legal;                // was the last char 'legal'?
    135 static int revins_scol;                 // start column of revins session
    136 
    137 static bool ins_need_undo;              // call u_save() before inserting a
    138                                        // char.  Set when edit() is called.
    139                                        // after that arrow_used is used.
    140 
    141 static TriState dont_sync_undo = kFalse;  // CTRL-G U prevents syncing undo
    142                                          // for the next left/right cursor key
    143 
    144 static linenr_T o_lnum = 0;
    145 
    146 static kvec_t(char) replace_stack = KV_INITIAL_VALUE;
    147 
    148 #define TRIGGER_AUTOCOMPLETE() \
    149  do { \
    150    redraw_later(curwin, UPD_VALID); \
    151    update_screen();  /* Show char deletion immediately */ \
    152    ui_flush(); \
    153    ins_compl_enable_autocomplete(); \
    154    insert_do_complete(s); \
    155    break; \
    156  } while (0)
    157 
    158 #define MAY_TRIGGER_AUTOCOMPLETE(c) \
    159  do { \
    160    if (ins_compl_has_autocomplete() && !char_avail() && curwin->w_cursor.col > 0) { \
    161      (c) = char_before_cursor(); \
    162      if (vim_isprintc(c)) { \
    163        TRIGGER_AUTOCOMPLETE(); \
    164      } \
    165    } \
    166  } while (0)
    167 
    168 static void insert_enter(InsertState *s)
    169 {
    170  s->did_backspace = true;
    171  s->old_topfill = -1;
    172  s->replaceState = MODE_REPLACE;
    173  s->cmdchar_todo = s->cmdchar;
    174  s->ins_just_started = true;
    175  // Remember whether editing was restarted after CTRL-O
    176  did_restart_edit = restart_edit;
    177  // sleep before redrawing, needed for "CTRL-O :" that results in an
    178  // error message
    179  msg_check_for_delay(true);
    180  // set Insstart_orig to Insstart
    181  update_Insstart_orig = true;
    182 
    183  ins_compl_clear();        // clear stuff for CTRL-X mode
    184 
    185  // Trigger InsertEnter autocommands.  Do not do this for "r<CR>" or "grx".
    186  if (s->cmdchar != 'r' && s->cmdchar != 'v') {
    187    pos_T save_cursor = curwin->w_cursor;
    188 
    189    const char *const ptr = s->cmdchar == 'R' ? "r" : s->cmdchar == 'V' ? "v" : "i";
    190    set_vim_var_string(VV_INSERTMODE, ptr, 1);
    191    set_vim_var_string(VV_CHAR, NULL, -1);
    192    ins_apply_autocmds(EVENT_INSERTENTER);
    193 
    194    // Check for changed highlighting, e.g. for ModeMsg.
    195    if (need_highlight_changed) {
    196      highlight_changed();
    197    }
    198 
    199    // Make sure the cursor didn't move.  Do call check_cursor_col() in
    200    // case the text was modified.  Since Insert mode was not started yet
    201    // a call to check_cursor_col() may move the cursor, especially with
    202    // the "A" command, thus set State to avoid that. Also check that the
    203    // line number is still valid (lines may have been deleted).
    204    // Do not restore if v:char was set to a non-empty string.
    205    if (!equalpos(curwin->w_cursor, save_cursor)
    206        && *get_vim_var_str(VV_CHAR) == NUL
    207        && save_cursor.lnum <= curbuf->b_ml.ml_line_count) {
    208      int save_state = State;
    209 
    210      curwin->w_cursor = save_cursor;
    211      State = MODE_INSERT;
    212      check_cursor_col(curwin);
    213      State = save_state;
    214    }
    215  }
    216 
    217  // When doing a paste with the middle mouse button, Insstart is set to
    218  // where the paste started.
    219  if (where_paste_started.lnum != 0) {
    220    Insstart = where_paste_started;
    221  } else {
    222    Insstart = curwin->w_cursor;
    223    if (s->startln) {
    224      Insstart.col = 0;
    225    }
    226  }
    227 
    228  Insstart_textlen = linetabsize_str(get_cursor_line_ptr());
    229  Insstart_blank_vcol = MAXCOL;
    230 
    231  if (!did_ai) {
    232    ai_col = 0;
    233  }
    234 
    235  if (s->cmdchar != NUL && restart_edit == 0) {
    236    ResetRedobuff();
    237    AppendNumberToRedobuff(s->count);
    238    if (s->cmdchar == 'V' || s->cmdchar == 'v') {
    239      // "gR" or "gr" command
    240      AppendCharToRedobuff('g');
    241      AppendCharToRedobuff((s->cmdchar == 'v') ? 'r' : 'R');
    242    } else {
    243      AppendCharToRedobuff(s->cmdchar);
    244      if (s->cmdchar == 'g') {          // "gI" command
    245        AppendCharToRedobuff('I');
    246      } else if (s->cmdchar == 'r') {  // "r<CR>" command
    247        s->count = 1;                  // insert only one <CR>
    248      }
    249    }
    250  }
    251 
    252  if (s->cmdchar == 'R') {
    253    State = MODE_REPLACE;
    254  } else if (s->cmdchar == 'V' || s->cmdchar == 'v') {
    255    State = MODE_VREPLACE;
    256    s->replaceState = MODE_VREPLACE;
    257    orig_line_count = curbuf->b_ml.ml_line_count;
    258    vr_lines_changed = 1;
    259  } else {
    260    State = MODE_INSERT;
    261  }
    262 
    263  may_trigger_modechanged();
    264  stop_insert_mode = false;
    265 
    266  // need to position cursor again when on a TAB and
    267  // when on a char with inline virtual text
    268  if (gchar_cursor() == TAB || buf_meta_total(curbuf, kMTMetaInline) > 0) {
    269    curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
    270  }
    271 
    272  // Enable langmap or IME, indicated by 'iminsert'.
    273  // Note that IME may enabled/disabled without us noticing here, thus the
    274  // 'iminsert' value may not reflect what is actually used.  It is updated
    275  // when hitting <Esc>.
    276  if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
    277    State |= MODE_LANGMAP;
    278  }
    279 
    280  setmouse();
    281  clear_showcmd();
    282  // there is no reverse replace mode
    283  revins_on = (State == MODE_INSERT && p_ri);
    284  if (revins_on) {
    285    undisplay_dollar();
    286  }
    287  revins_chars = 0;
    288  revins_legal = 0;
    289  revins_scol = -1;
    290 
    291  // Handle restarting Insert mode.
    292  // Don't do this for "CTRL-O ." (repeat an insert): we get here with
    293  // restart_edit non-zero, and something in the stuff buffer.
    294  if (restart_edit != 0 && stuff_empty()) {
    295    // After a paste we consider text typed to be part of the insert for
    296    // the pasted text. You can backspace over the pasted text too.
    297    arrow_used = where_paste_started.lnum == 0;
    298    restart_edit = 0;
    299 
    300    // If the cursor was after the end-of-line before the CTRL-O and it is
    301    // now at the end-of-line, put it after the end-of-line (this is not
    302    // correct in very rare cases).
    303    // Also do this if curswant is greater than the current virtual
    304    // column.  Eg after "^O$" or "^O80|".
    305    validate_virtcol(curwin);
    306    update_curswant();
    307    const char *ptr;
    308    if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum)
    309         || curwin->w_curswant > curwin->w_virtcol)
    310        && *(ptr = get_cursor_line_ptr() + curwin->w_cursor.col) != NUL) {
    311      if (ptr[1] == NUL) {
    312        curwin->w_cursor.col++;
    313      } else {
    314        s->i = utfc_ptr2len(ptr);
    315        if (ptr[s->i] == NUL) {
    316          curwin->w_cursor.col += s->i;
    317        }
    318      }
    319    }
    320    ins_at_eol = false;
    321  } else {
    322    arrow_used = false;
    323  }
    324 
    325  // we are in insert mode now, don't need to start it anymore
    326  need_start_insertmode = false;
    327 
    328  // Need to save the line for undo before inserting the first char.
    329  ins_need_undo = true;
    330 
    331  where_paste_started.lnum = 0;
    332  can_cindent = true;
    333  // The cursor line is not in a closed fold, unless restarting.
    334  if (did_restart_edit == 0) {
    335    foldOpenCursor();
    336  }
    337 
    338  // If 'showmode' is set, show the current (insert/replace/..) mode.
    339  // A warning message for changing a readonly file is given here, before
    340  // actually changing anything.  It's put after the mode, if any.
    341  s->i = 0;
    342  if (p_smd && msg_silent == 0) {
    343    s->i = showmode();
    344  }
    345 
    346  if (did_restart_edit == 0) {
    347    change_warning(curbuf, s->i == 0 ? 0 : s->i + 1);
    348  }
    349 
    350  ui_cursor_shape();            // may show different cursor shape
    351  do_digraph(-1);               // clear digraphs
    352 
    353  // Get the current length of the redo buffer, those characters have to be
    354  // skipped if we want to get to the inserted characters.
    355  String inserted = get_inserted();
    356  new_insert_skip = (int)inserted.size;
    357  if (inserted.data != NULL) {
    358    xfree(inserted.data);
    359  }
    360 
    361  old_indent = 0;
    362 
    363  do {
    364    state_enter(&s->state);
    365    // If s->count != 0, `ins_esc` will prepare the redo buffer for reprocessing
    366    // and return false, causing `state_enter` to be called again.
    367  } while (!ins_esc(&s->count, s->cmdchar, s->nomove));
    368 
    369  // Always update o_lnum, so that a "CTRL-O ." that adds a line
    370  // still puts the cursor back after the inserted text.
    371  if (ins_at_eol) {
    372    o_lnum = curwin->w_cursor.lnum;
    373  }
    374 
    375  pum_check_clear();
    376 
    377  foldUpdateAfterInsert();
    378  // When CTRL-C was typed got_int will be set, with the result
    379  // that the autocommands won't be executed. When mapped got_int
    380  // is not set, but let's keep the behavior the same.
    381  if (s->cmdchar != 'r' && s->cmdchar != 'v' && s->c != Ctrl_C) {
    382    ins_apply_autocmds(EVENT_INSERTLEAVE);
    383  }
    384  did_cursorhold = false;
    385 
    386  // ins_redraw() triggers TextChangedI only when no characters
    387  // are in the typeahead buffer, so reset curbuf->b_last_changedtick
    388  // if the TextChangedI was not blocked by char_avail() (e.g. using :norm!)
    389  // and the TextChangedI autocommand has been triggered.
    390  if (!char_avail() && curbuf->b_last_changedtick_i == buf_get_changedtick(curbuf)) {
    391    curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
    392  }
    393 }
    394 
    395 static int insert_check(VimState *state)
    396 {
    397  InsertState *s = (InsertState *)state;
    398 
    399  if (!revins_legal) {
    400    revins_scol = -1;     // reset on illegal motions
    401  } else {
    402    revins_legal = 0;
    403  }
    404 
    405  if (arrow_used) {       // don't repeat insert when arrow key used
    406    s->count = 0;
    407  }
    408 
    409  if (update_Insstart_orig) {
    410    Insstart_orig = Insstart;
    411  }
    412 
    413  if (curbuf->terminal && !stop_insert_mode) {
    414    // Exit Insert mode and go to Terminal mode.
    415    stop_insert_mode = true;
    416    restart_edit = 'I';
    417    stuffcharReadbuff(K_NOP);
    418  }
    419 
    420  if (stop_insert_mode && !ins_compl_active()) {
    421    // ":stopinsert" used
    422    s->count = 0;
    423    return 0;  // exit insert mode
    424  }
    425 
    426  // set curwin->w_curswant for next K_DOWN or K_UP
    427  if (!arrow_used) {
    428    curwin->w_set_curswant = true;
    429  }
    430 
    431  // If there is no typeahead may check for timestamps (e.g., for when a
    432  // menu invoked a shell command).
    433  if (stuff_empty()) {
    434    did_check_timestamps = false;
    435    if (need_check_timestamps) {
    436      check_timestamps(false);
    437    }
    438  }
    439 
    440  // When emsg() was called msg_scroll will have been set.
    441  msg_scroll = false;
    442 
    443  // Open fold at the cursor line, according to 'foldopen'.
    444  if (fdo_flags & kOptFdoFlagInsert) {
    445    foldOpenCursor();
    446  }
    447 
    448  // Close folds where the cursor isn't, according to 'foldclose'
    449  if (!char_avail()) {
    450    foldCheckClose();
    451  }
    452 
    453  if (bt_prompt(curbuf)) {
    454    init_prompt(s->cmdchar_todo);
    455    s->cmdchar_todo = NUL;
    456  }
    457 
    458  // If we inserted a character at the last position of the last line in the
    459  // window, scroll the window one line up. This avoids an extra redraw.  This
    460  // is detected when the cursor column is smaller after inserting something.
    461  // Don't do this when the topline changed already, it has already been
    462  // adjusted (by insertchar() calling open_line())).
    463  // Also don't do this when 'smoothscroll' is set, as the window should then
    464  // be scrolled by screen lines.
    465  if (curbuf->b_mod_set
    466      && curwin->w_p_wrap
    467      && !curwin->w_p_sms
    468      && !s->did_backspace
    469      && curwin->w_topline == s->old_topline
    470      && curwin->w_topfill == s->old_topfill
    471      && s->count <= 1) {
    472    s->mincol = curwin->w_wcol;
    473    validate_cursor_col(curwin);
    474 
    475    if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(),
    476                                                curbuf->b_p_ts,
    477                                                curbuf->b_p_vts_array,
    478                                                false)
    479        && curwin->w_wrow == curwin->w_view_height - 1 - get_scrolloff_value(curwin)
    480        && (curwin->w_cursor.lnum != curwin->w_topline
    481            || curwin->w_topfill > 0)) {
    482      if (curwin->w_topfill > 0) {
    483        curwin->w_topfill--;
    484      } else if (hasFolding(curwin, curwin->w_topline, NULL, &s->old_topline)) {
    485        set_topline(curwin, s->old_topline + 1);
    486      } else {
    487        set_topline(curwin, curwin->w_topline + 1);
    488      }
    489    }
    490  }
    491 
    492  // May need to adjust w_topline to show the cursor.
    493  if (s->count <= 1) {
    494    update_topline(curwin);
    495  }
    496 
    497  s->did_backspace = false;
    498 
    499  if (s->count <= 1) {
    500    validate_cursor(curwin);  // may set must_redraw
    501  }
    502 
    503  // Redraw the display when no characters are waiting.
    504  // Also shows mode, ruler and positions cursor.
    505  ins_redraw(true);
    506 
    507  if (curwin->w_p_scb) {
    508    do_check_scrollbind(true);
    509  }
    510 
    511  if (curwin->w_p_crb) {
    512    do_check_cursorbind();
    513  }
    514 
    515  if (s->count <= 1) {
    516    update_curswant();
    517  }
    518  s->old_topline = curwin->w_topline;
    519  s->old_topfill = curwin->w_topfill;
    520 
    521  if (s->c != K_EVENT) {
    522    s->lastc = s->c;  // remember previous char for CTRL-D
    523  }
    524 
    525  // After using CTRL-G U the next cursor key will not break undo.
    526  if (dont_sync_undo == kNone) {
    527    dont_sync_undo = kTrue;
    528  } else {
    529    dont_sync_undo = kFalse;
    530  }
    531 
    532  // Trigger autocomplete when entering Insert mode, either directly
    533  // or via change commands like 'ciw', 'cw', etc., before the first
    534  // character is typed.
    535  if (s->ins_just_started) {
    536    s->ins_just_started = false;
    537    if (ins_compl_has_autocomplete() && !char_avail() && curwin->w_cursor.col > 0) {
    538      s->c = char_before_cursor();
    539      if (vim_isprintc(s->c)) {
    540        ins_compl_enable_autocomplete();
    541        ins_compl_init_get_longest();
    542        insert_do_complete(s);
    543        insert_handle_key_post(s);
    544        return 1;
    545      }
    546    }
    547  }
    548 
    549  return 1;
    550 }
    551 
    552 static int insert_execute(VimState *state, int key)
    553 {
    554  InsertState *const s = (InsertState *)state;
    555  if (stop_insert_mode) {
    556    // Insert mode ended, possibly from a callback.
    557    if (key != K_IGNORE && key != K_NOP) {
    558      vungetc(key);
    559    }
    560    s->count = 0;
    561    s->nomove = true;
    562    ins_compl_prep(ESC);
    563    return 0;
    564  }
    565 
    566  if (key == K_IGNORE || key == K_NOP) {
    567    return -1;  // get another key
    568  }
    569  s->c = key;
    570 
    571  // Don't want K_EVENT with cursorhold for the second key, e.g., after CTRL-V.
    572  if (key != K_EVENT) {
    573    did_cursorhold = true;
    574  }
    575 
    576  // Special handling of keys while the popup menu is visible or wanted
    577  // and the cursor is still in the completed word.  Only when there is
    578  // a match, skip this when no matches were found.
    579  if (ins_compl_active() && curwin->w_cursor.col >= ins_compl_col()
    580      && ins_compl_has_shown_match() && pum_wanted()) {
    581    // BS: Delete one character from "compl_leader".
    582    if ((s->c == K_BS || s->c == Ctrl_H)
    583        && curwin->w_cursor.col > ins_compl_col()
    584        && (s->c = ins_compl_bs()) == NUL) {
    585      return 1;  // continue
    586    }
    587 
    588    // When no match was selected or it was edited.
    589    if (!ins_compl_used_match()) {
    590      // CTRL-L: Add one character from the current match to
    591      // "compl_leader".  Except when at the original match and
    592      // there is nothing to add, CTRL-L works like CTRL-P then.
    593      if (s->c == Ctrl_L
    594          && (!ctrl_x_mode_line_or_eval()
    595              || ins_compl_long_shown_match())) {
    596        ins_compl_addfrommatch();
    597        return 1;  // continue
    598      }
    599 
    600      // A non-white character that fits in with the current
    601      // completion: Add to "compl_leader".
    602      if (ins_compl_accept_char(s->c)) {
    603        // Trigger InsertCharPre.
    604        char *str = do_insert_char_pre(s->c);
    605 
    606        if (str != NULL) {
    607          for (char *p = str; *p != NUL; MB_PTR_ADV(p)) {
    608            ins_compl_addleader(utf_ptr2char(p));
    609          }
    610          xfree(str);
    611        } else {
    612          ins_compl_addleader(s->c);
    613        }
    614        return 1;  // continue
    615      }
    616 
    617      // Pressing CTRL-Y selects the current match.  When
    618      // compl_enter_selects is set the Enter key does the same.
    619      if ((s->c == Ctrl_Y
    620           || (ins_compl_enter_selects()
    621               && (s->c == CAR || s->c == K_KENTER || s->c == NL)))
    622          && stop_arrow() == OK) {
    623        ins_compl_delete(false);
    624        if (ins_compl_preinsert_longest() && !ins_compl_is_match_selected()) {
    625          ins_compl_insert(false, true);
    626          ins_compl_init_get_longest();
    627          return 1;  // continue
    628        } else {
    629          ins_compl_insert(false, false);
    630        }
    631      } else if (ascii_iswhite_nl_or_nul(s->c) && ins_compl_preinsert_effect()) {
    632        // Delete preinserted text when typing special chars
    633        ins_compl_delete(false);
    634      }
    635    }
    636  }
    637 
    638  // Prepare for or stop CTRL-X mode. This doesn't do completion, but it does
    639  // fix up the text when finishing completion.
    640  ins_compl_init_get_longest();
    641  if (ins_compl_prep(s->c)) {
    642    return 1;  // continue
    643  }
    644 
    645  // CTRL-\ CTRL-N goes to Normal mode,
    646  // CTRL-\ CTRL-O is like CTRL-O but without moving the cursor
    647  if (s->c == Ctrl_BSL) {
    648    // may need to redraw when no more chars available now
    649    ins_redraw(false);
    650    no_mapping++;
    651    allow_keys++;
    652    s->c = plain_vgetc();
    653    no_mapping--;
    654    allow_keys--;
    655    if (s->c != Ctrl_N && s->c != Ctrl_G && s->c != Ctrl_O) {
    656      // it's something else
    657      vungetc(s->c);
    658      s->c = Ctrl_BSL;
    659    } else {
    660      if (s->c == Ctrl_O) {
    661        ins_ctrl_o();
    662        ins_at_eol = false;  // cursor keeps its column
    663        s->nomove = true;
    664      }
    665      s->count = 0;
    666      return 0;
    667    }
    668  }
    669 
    670  if (s->c != K_EVENT) {
    671    s->c = do_digraph(s->c);
    672  }
    673 
    674  if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode_cmdline()) {
    675    insert_do_complete(s);
    676    insert_handle_key_post(s);
    677    return 1;
    678  }
    679 
    680  if (s->c == Ctrl_V || s->c == Ctrl_Q) {
    681    ins_ctrl_v();
    682    s->c = Ctrl_V;       // pretend CTRL-V is last typed character
    683    return 1;  // continue
    684  }
    685 
    686  if (cindent_on() && ctrl_x_mode_none()) {
    687    s->line_is_white = inindent(0);
    688    // A key name preceded by a bang means this key is not to be
    689    // inserted.  Skip ahead to the re-indenting below.
    690    if (in_cinkeys(s->c, '!', s->line_is_white)
    691        && stop_arrow() == OK) {
    692      do_c_expr_indent();
    693      return 1;  // continue
    694    }
    695    // A key name preceded by a star means that indenting has to be
    696    // done before inserting the key.
    697    if (can_cindent && in_cinkeys(s->c, '*', s->line_is_white)
    698        && stop_arrow() == OK) {
    699      do_c_expr_indent();
    700    }
    701  }
    702 
    703  if (curwin->w_p_rl) {
    704    switch (s->c) {
    705    case K_LEFT:
    706      s->c = K_RIGHT; break;
    707    case K_S_LEFT:
    708      s->c = K_S_RIGHT; break;
    709    case K_C_LEFT:
    710      s->c = K_C_RIGHT; break;
    711    case K_RIGHT:
    712      s->c = K_LEFT; break;
    713    case K_S_RIGHT:
    714      s->c = K_S_LEFT; break;
    715    case K_C_RIGHT:
    716      s->c = K_C_LEFT; break;
    717    }
    718  }
    719 
    720  // If 'keymodel' contains "startsel", may start selection.  If it
    721  // does, a CTRL-O and c will be stuffed, we need to get these
    722  // characters.
    723  if (ins_start_select(s->c)) {
    724    return 1;  // continue
    725  }
    726 
    727  return insert_handle_key(s);
    728 }
    729 
    730 static int insert_handle_key(InsertState *s)
    731 {
    732  // The big switch to handle a character in insert mode.
    733  // TODO(tarruda): This could look better if a lookup table is used.
    734  // (similar to normal mode `nv_cmds[]`)
    735  switch (s->c) {
    736  case ESC:           // End input mode
    737    if (echeck_abbr(ESC + ABBR_OFF)) {
    738      break;
    739    }
    740    FALLTHROUGH;
    741 
    742  case Ctrl_C:        // End input mode
    743    if (s->c == Ctrl_C && cmdwin_type != 0) {
    744      // Close the cmdline window.
    745      cmdwin_result = K_IGNORE;
    746      got_int = false;         // don't stop executing autocommands et al
    747      s->nomove = true;
    748      return 0;  // exit insert mode
    749    }
    750    if (s->c == Ctrl_C && bt_prompt(curbuf)) {
    751      if (invoke_prompt_interrupt()) {
    752        if (!bt_prompt(curbuf)) {
    753          // buffer changed to a non-prompt buffer, get out of
    754          // Insert mode
    755          return 0;
    756        }
    757        break;
    758      }
    759    }
    760 
    761    return 0;  // exit insert mode
    762 
    763  case Ctrl_Z:
    764    goto normalchar;                // insert CTRL-Z as normal char
    765 
    766  case Ctrl_O:        // execute one command
    767    if (ctrl_x_mode_omni()) {
    768      insert_do_complete(s);
    769      break;
    770    }
    771 
    772    if (echeck_abbr(Ctrl_O + ABBR_OFF)) {
    773      break;
    774    }
    775 
    776    ins_ctrl_o();
    777 
    778    // don't move the cursor left when 'virtualedit' has "onemore".
    779    if (get_ve_flags(curwin) & kOptVeFlagOnemore) {
    780      ins_at_eol = false;
    781      s->nomove = true;
    782    }
    783 
    784    s->count = 0;
    785    return 0;  // exit insert mode
    786 
    787  case K_INS:         // toggle insert/replace mode
    788  case K_KINS:
    789    ins_insert(s->replaceState);
    790    break;
    791 
    792  case K_SELECT:      // end of Select mode mapping - ignore
    793    break;
    794 
    795  case K_HELP:        // Help key works like <ESC> <Help>
    796  case K_F1:
    797  case K_XF1:
    798    stuffcharReadbuff(K_HELP);
    799    return 0;  // exit insert mode
    800 
    801  case ' ':
    802    if (mod_mask != MOD_MASK_CTRL) {
    803      goto normalchar;
    804    }
    805    FALLTHROUGH;
    806  case K_ZERO:        // Insert the previously inserted text.
    807  case NUL:
    808  case Ctrl_A:
    809    // For ^@ the trailing ESC will end the insert, unless there is an
    810    // error.
    811    if (stuff_inserted(NUL, 1, (s->c == Ctrl_A)) == FAIL
    812        && s->c != Ctrl_A) {
    813      return 0;  // exit insert mode
    814    }
    815    s->inserted_space = false;
    816    break;
    817 
    818  case Ctrl_R:        // insert the contents of a register
    819    if (ctrl_x_mode_register() && !ins_compl_active()) {
    820      insert_do_complete(s);
    821      break;
    822    }
    823    ins_reg();
    824    auto_format(false, true);
    825    s->inserted_space = false;
    826    break;
    827 
    828  case Ctrl_G:        // commands starting with CTRL-G
    829    ins_ctrl_g();
    830    break;
    831 
    832  case Ctrl_HAT:      // switch input mode and/or langmap
    833    ins_ctrl_hat();
    834    break;
    835 
    836  case Ctrl__:        // switch between languages
    837    if (!p_ari) {
    838      goto normalchar;
    839    }
    840    ins_ctrl_();
    841    break;
    842 
    843  case Ctrl_D:        // Make indent one shiftwidth smaller.
    844    if (ctrl_x_mode_path_defines()) {
    845      insert_do_complete(s);
    846      break;
    847    }
    848    FALLTHROUGH;
    849 
    850  case Ctrl_T:        // Make indent one shiftwidth greater.
    851    if (s->c == Ctrl_T && ctrl_x_mode_thesaurus()) {
    852      if (check_compl_option(false)) {
    853        insert_do_complete(s);
    854      }
    855      break;
    856    }
    857    ins_shift(s->c, s->lastc);
    858    auto_format(false, true);
    859    s->inserted_space = false;
    860    break;
    861 
    862  case K_DEL:         // delete character under the cursor
    863  case K_KDEL:
    864    ins_del();
    865    auto_format(false, true);
    866    break;
    867 
    868  case K_BS:          // delete character before the cursor
    869  case Ctrl_H:
    870    s->did_backspace = ins_bs(s->c, BACKSPACE_CHAR, &s->inserted_space);
    871    auto_format(false, true);
    872    if (s->did_backspace) {
    873      MAY_TRIGGER_AUTOCOMPLETE(s->c);
    874    }
    875    break;
    876 
    877  case Ctrl_W:        // delete word before the cursor
    878    if (bt_prompt(curbuf) && (mod_mask & MOD_MASK_SHIFT) == 0) {
    879      // In a prompt window CTRL-W is used for window commands.
    880      // Use Shift-CTRL-W to delete a word.
    881      stuffcharReadbuff(Ctrl_W);
    882      restart_edit = 'A';
    883      s->nomove = true;
    884      s->count = 0;
    885      return 0;
    886    }
    887    s->did_backspace = ins_bs(s->c, BACKSPACE_WORD, &s->inserted_space);
    888    auto_format(false, true);
    889    if (s->did_backspace) {
    890      MAY_TRIGGER_AUTOCOMPLETE(s->c);
    891    }
    892    break;
    893 
    894  case Ctrl_U:        // delete all inserted text in current line
    895    // CTRL-X CTRL-U completes with 'completefunc'.
    896    if (ctrl_x_mode_function()) {
    897      insert_do_complete(s);
    898    } else {
    899      s->did_backspace = ins_bs(s->c, BACKSPACE_LINE, &s->inserted_space);
    900      auto_format(false, true);
    901      s->inserted_space = false;
    902      if (s->did_backspace) {
    903        MAY_TRIGGER_AUTOCOMPLETE(s->c);
    904      }
    905    }
    906    break;
    907 
    908  case K_LEFTMOUSE:     // mouse keys
    909  case K_LEFTMOUSE_NM:
    910  case K_LEFTDRAG:
    911  case K_LEFTRELEASE:
    912  case K_LEFTRELEASE_NM:
    913  case K_MOUSEMOVE:
    914  case K_MIDDLEMOUSE:
    915  case K_MIDDLEDRAG:
    916  case K_MIDDLERELEASE:
    917  case K_RIGHTMOUSE:
    918  case K_RIGHTDRAG:
    919  case K_RIGHTRELEASE:
    920  case K_X1MOUSE:
    921  case K_X1DRAG:
    922  case K_X1RELEASE:
    923  case K_X2MOUSE:
    924  case K_X2DRAG:
    925  case K_X2RELEASE:
    926    ins_mouse(s->c);
    927    break;
    928 
    929  case K_MOUSEDOWN:   // Default action for scroll wheel up: scroll up
    930    ins_mousescroll(MSCR_DOWN);
    931    break;
    932 
    933  case K_MOUSEUP:     // Default action for scroll wheel down: scroll down
    934    ins_mousescroll(MSCR_UP);
    935    break;
    936 
    937  case K_MOUSELEFT:   // Scroll wheel left
    938    ins_mousescroll(MSCR_LEFT);
    939    break;
    940 
    941  case K_MOUSERIGHT:  // Scroll wheel right
    942    ins_mousescroll(MSCR_RIGHT);
    943    break;
    944 
    945  case K_IGNORE:      // Something mapped to nothing
    946    break;
    947 
    948  case K_PASTE_START:
    949    paste_repeat(1);
    950    goto check_pum;
    951 
    952  case K_EVENT:       // some event
    953    state_handle_k_event();
    954    // If CTRL-G U was used apply it to the next typed key.
    955    if (dont_sync_undo == kTrue) {
    956      dont_sync_undo = kNone;
    957    }
    958    goto check_pum;
    959 
    960  case K_COMMAND:     // <Cmd>command<CR>
    961    do_cmdline(NULL, getcmdkeycmd, NULL, 0);
    962    goto check_pum;
    963 
    964  case K_LUA:
    965    map_execute_lua(false, false);
    966 
    967 check_pum:
    968    // nvim_select_popupmenu_item() can be called from the handling of
    969    // K_EVENT, K_COMMAND, or K_LUA.
    970    // TODO(bfredl): Not entirely sure this indirection is necessary
    971    // but doing like this ensures using nvim_select_popupmenu_item is
    972    // equivalent to selecting the item with a typed key.
    973    if (pum_want.active) {
    974      if (pum_visible()) {
    975        // Set this to NULL so that ins_complete() will update the message.
    976        edit_submode_extra = NULL;
    977        insert_do_complete(s);
    978        if (pum_want.finish) {
    979          // accept the item and stop completion
    980          ins_compl_prep(Ctrl_Y);
    981        }
    982      }
    983      pum_want.active = false;
    984    }
    985 
    986    if (curbuf->b_u_synced) {
    987      // The K_EVENT, K_COMMAND, or K_LUA caused undo to be synced.
    988      // Need to save the line for undo before inserting the next char.
    989      ins_need_undo = true;
    990    }
    991    break;
    992 
    993  case K_HOME:        // <Home>
    994  case K_KHOME:
    995  case K_S_HOME:
    996  case K_C_HOME:
    997    ins_home(s->c);
    998    break;
    999 
   1000  case K_END:         // <End>
   1001  case K_KEND:
   1002  case K_S_END:
   1003  case K_C_END:
   1004    ins_end(s->c);
   1005    break;
   1006 
   1007  case K_LEFT:        // <Left>
   1008    if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) {
   1009      ins_s_left();
   1010    } else {
   1011      ins_left();
   1012    }
   1013    break;
   1014 
   1015  case K_S_LEFT:      // <S-Left>
   1016  case K_C_LEFT:
   1017    ins_s_left();
   1018    break;
   1019 
   1020  case K_RIGHT:       // <Right>
   1021    if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) {
   1022      ins_s_right();
   1023    } else {
   1024      ins_right();
   1025    }
   1026    break;
   1027 
   1028  case K_S_RIGHT:     // <S-Right>
   1029  case K_C_RIGHT:
   1030    ins_s_right();
   1031    break;
   1032 
   1033  case K_UP:          // <Up>
   1034    if (pum_visible()) {
   1035      insert_do_complete(s);
   1036    } else if (mod_mask & MOD_MASK_SHIFT) {
   1037      ins_pageup();
   1038    } else {
   1039      ins_up(false);
   1040    }
   1041    break;
   1042 
   1043  case K_S_UP:        // <S-Up>
   1044  case K_PAGEUP:
   1045  case K_KPAGEUP:
   1046    if (pum_visible()) {
   1047      insert_do_complete(s);
   1048    } else {
   1049      ins_pageup();
   1050    }
   1051    break;
   1052 
   1053  case K_DOWN:        // <Down>
   1054    if (pum_visible()) {
   1055      insert_do_complete(s);
   1056    } else if (mod_mask & MOD_MASK_SHIFT) {
   1057      ins_pagedown();
   1058    } else {
   1059      ins_down(false);
   1060    }
   1061    break;
   1062 
   1063  case K_S_DOWN:      // <S-Down>
   1064  case K_PAGEDOWN:
   1065  case K_KPAGEDOWN:
   1066    if (pum_visible()) {
   1067      insert_do_complete(s);
   1068    } else {
   1069      ins_pagedown();
   1070    }
   1071    break;
   1072 
   1073  case K_S_TAB:       // When not mapped, use like a normal TAB
   1074    s->c = TAB;
   1075    FALLTHROUGH;
   1076 
   1077  case TAB:           // TAB or Complete patterns along path
   1078    if (ctrl_x_mode_path_patterns()) {
   1079      insert_do_complete(s);
   1080      break;
   1081    }
   1082    s->inserted_space = false;
   1083    if (ins_tab()) {
   1084      goto normalchar;                // insert TAB as a normal char
   1085    }
   1086    auto_format(false, true);
   1087    break;
   1088 
   1089  case K_KENTER:      // <Enter>
   1090    s->c = CAR;
   1091    FALLTHROUGH;
   1092  case CAR:
   1093  case NL:
   1094    // In a quickfix window a <CR> jumps to the error under the
   1095    // cursor.
   1096    if (bt_quickfix(curbuf) && s->c == CAR) {
   1097      if (curwin->w_llist_ref == NULL) {          // quickfix window
   1098        do_cmdline_cmd(".cc");
   1099      } else {                                    // location list window
   1100        do_cmdline_cmd(".ll");
   1101      }
   1102      break;
   1103    }
   1104    if (cmdwin_type != 0) {
   1105      // Execute the command in the cmdline window.
   1106      cmdwin_result = CAR;
   1107      return 0;
   1108    }
   1109    if ((mod_mask & MOD_MASK_SHIFT) == 0 && bt_prompt(curbuf)) {
   1110      prompt_invoke_callback();
   1111      if (!bt_prompt(curbuf)) {
   1112        // buffer changed to a non-prompt buffer, get out of
   1113        // Insert mode
   1114        return 0;
   1115      }
   1116      break;
   1117    }
   1118    if (!ins_eol(s->c)) {
   1119      return 0;  // out of memory
   1120    }
   1121    auto_format(false, false);
   1122    s->inserted_space = false;
   1123    break;
   1124 
   1125  case Ctrl_K:        // digraph or keyword completion
   1126    if (ctrl_x_mode_dictionary()) {
   1127      if (check_compl_option(true)) {
   1128        insert_do_complete(s);
   1129      }
   1130      break;
   1131    }
   1132 
   1133    s->c = ins_digraph();
   1134    if (s->c == NUL) {
   1135      break;
   1136    }
   1137    goto normalchar;
   1138 
   1139  case Ctrl_X:        // Enter CTRL-X mode
   1140    ins_ctrl_x();
   1141    break;
   1142 
   1143  case Ctrl_RSB:      // Tag name completion after ^X
   1144    if (!ctrl_x_mode_tags()) {
   1145      goto normalchar;
   1146    } else {
   1147      insert_do_complete(s);
   1148    }
   1149    break;
   1150 
   1151  case Ctrl_F:        // File name completion after ^X
   1152    if (!ctrl_x_mode_files()) {
   1153      goto normalchar;
   1154    } else {
   1155      insert_do_complete(s);
   1156    }
   1157    break;
   1158 
   1159  case 's':           // Spelling completion after ^X
   1160  case Ctrl_S:
   1161    if (!ctrl_x_mode_spell()) {
   1162      goto normalchar;
   1163    } else {
   1164      insert_do_complete(s);
   1165    }
   1166    break;
   1167 
   1168  case Ctrl_L:        // Whole line completion after ^X
   1169    if (!ctrl_x_mode_whole_line()) {
   1170      goto normalchar;
   1171    }
   1172    FALLTHROUGH;
   1173 
   1174  case Ctrl_P:        // Do previous/next pattern completion
   1175  case Ctrl_N:
   1176    // if 'complete' is empty then plain ^P is no longer special,
   1177    // but it is under other ^X modes
   1178    if (*curbuf->b_p_cpt == NUL
   1179        && (ctrl_x_mode_normal() || ctrl_x_mode_whole_line())
   1180        && !compl_status_local()) {
   1181      goto normalchar;
   1182    }
   1183 
   1184    insert_do_complete(s);
   1185    break;
   1186 
   1187  case Ctrl_Y:        // copy from previous line or scroll down
   1188  case Ctrl_E:        // copy from next line or scroll up
   1189    s->c = ins_ctrl_ey(s->c);
   1190    break;
   1191 
   1192  default:
   1193 
   1194 normalchar:
   1195    // Insert a normal character.
   1196 
   1197    if (!p_paste) {
   1198      // Trigger InsertCharPre.
   1199      char *str = do_insert_char_pre(s->c);
   1200 
   1201      if (str != NULL) {
   1202        if (*str != NUL && stop_arrow() != FAIL) {
   1203          // Insert the new value of v:char literally.
   1204          for (char *p = str; *p != NUL; MB_PTR_ADV(p)) {
   1205            s->c = utf_ptr2char(p);
   1206            if (s->c == CAR || s->c == K_KENTER || s->c == NL) {
   1207              ins_eol(s->c);
   1208            } else {
   1209              ins_char(s->c);
   1210            }
   1211          }
   1212          AppendToRedobuffLit(str, -1);
   1213        }
   1214        xfree(str);
   1215        s->c = NUL;
   1216      }
   1217 
   1218      // If the new value is already inserted or an empty string
   1219      // then don't insert any character.
   1220      if (s->c == NUL) {
   1221        break;
   1222      }
   1223    }
   1224    // Try to perform smart-indenting.
   1225    ins_try_si(s->c);
   1226 
   1227    if (s->c == ' ') {
   1228      s->inserted_space = true;
   1229      if (inindent(0)) {
   1230        can_cindent = false;
   1231      }
   1232      if (Insstart_blank_vcol == MAXCOL
   1233          && curwin->w_cursor.lnum == Insstart.lnum) {
   1234        Insstart_blank_vcol = get_nolist_virtcol();
   1235      }
   1236    }
   1237 
   1238    // Insert a normal character and check for abbreviations on a
   1239    // special character.  Let CTRL-] expand abbreviations without
   1240    // inserting it.
   1241    if (vim_iswordc(s->c)
   1242        // Add ABBR_OFF for characters above 0x100, this is
   1243        // what check_abbr() expects.
   1244        || (!echeck_abbr((s->c >= 0x100) ? (s->c + ABBR_OFF) : s->c)
   1245            && s->c != Ctrl_RSB)) {
   1246      insert_special(s->c, false, false);
   1247      revins_legal++;
   1248      revins_chars++;
   1249    }
   1250 
   1251    auto_format(false, true);
   1252 
   1253    // When inserting a character the cursor line must never be in a
   1254    // closed fold.
   1255    foldOpenCursor();
   1256    // Trigger autocompletion
   1257    if (ins_compl_has_autocomplete() && !char_avail() && vim_isprintc(s->c)) {
   1258      TRIGGER_AUTOCOMPLETE();
   1259    }
   1260 
   1261    break;
   1262  }       // end of switch (s->c)
   1263 
   1264  insert_handle_key_post(s);
   1265  return 1;  // continue
   1266 }
   1267 
   1268 static void insert_do_complete(InsertState *s)
   1269 {
   1270  compl_busy = true;
   1271  disable_fold_update++;  // don't redraw folds here
   1272  if (ins_complete(s->c, true) == FAIL) {
   1273    compl_status_clear();
   1274  }
   1275  disable_fold_update--;
   1276  compl_busy = false;
   1277  can_si = may_do_si();  // allow smartindenting
   1278 }
   1279 
   1280 static void insert_do_cindent(InsertState *s)
   1281 {
   1282  // Indent now if a key was typed that is in 'cinkeys'.
   1283  if (in_cinkeys(s->c, ' ', s->line_is_white)) {
   1284    if (stop_arrow() == OK) {
   1285      // re-indent the current line
   1286      do_c_expr_indent();
   1287    }
   1288  }
   1289 }
   1290 
   1291 static void insert_handle_key_post(InsertState *s)
   1292 {
   1293  // If typed something may trigger CursorHoldI again.
   1294  if (s->c != K_EVENT
   1295      // but not in CTRL-X mode, a script can't restore the state
   1296      && ctrl_x_mode_normal()) {
   1297    did_cursorhold = false;
   1298  }
   1299 
   1300  // Check if we need to cancel completion mode because the window
   1301  // or tab page was changed
   1302  if (ins_compl_active() && !ins_compl_win_active(curwin)) {
   1303    ins_compl_cancel();
   1304  }
   1305 
   1306  // If the cursor was moved we didn't just insert a space
   1307  if (arrow_used) {
   1308    s->inserted_space = false;
   1309  }
   1310 
   1311  if (can_cindent && cindent_on() && ctrl_x_mode_normal()) {
   1312    insert_do_cindent(s);
   1313  }
   1314 }
   1315 
   1316 /// edit(): Start inserting text.
   1317 ///
   1318 /// "cmdchar" can be:
   1319 /// 'i' normal insert command
   1320 /// 'a' normal append command
   1321 /// 'R' replace command
   1322 /// 'r' "r<CR>" command: insert one <CR>.
   1323 ///     Note: count can be > 1, for redo, but still only one <CR> is inserted.
   1324 ///           <Esc> is not used for redo.
   1325 /// 'g' "gI" command.
   1326 /// 'V' "gR" command for Virtual Replace mode.
   1327 /// 'v' "gr" command for single character Virtual Replace mode.
   1328 ///
   1329 /// This function is not called recursively.  For CTRL-O commands, it returns
   1330 /// and lets the caller handle the Normal-mode command.
   1331 ///
   1332 /// @param  cmdchar  command that started the insert
   1333 /// @param  startln  if true, insert at start of line
   1334 /// @param  count    repeat count for the command
   1335 ///
   1336 /// @return true if a CTRL-O command caused the return (insert mode pending).
   1337 bool edit(int cmdchar, bool startln, int count)
   1338 {
   1339  if (curbuf->terminal) {
   1340    if (ex_normal_busy) {
   1341      // Do not enter terminal mode from ex_normal(), which would cause havoc
   1342      // (such as terminal-mode recursiveness). Instead set a flag to force-set
   1343      // the value of `restart_edit` before `ex_normal` returns.
   1344      restart_edit = 'i';
   1345      force_restart_edit = true;
   1346      return false;
   1347    }
   1348    return terminal_enter();
   1349  }
   1350 
   1351  // Don't allow inserting in the sandbox.
   1352  if (sandbox != 0) {
   1353    emsg(_(e_sandbox));
   1354    return false;
   1355  }
   1356 
   1357  // Don't allow changes in the buffer while editing the cmdline.  The
   1358  // caller of getcmdline() may get confused.
   1359  // Don't allow recursive insert mode when busy with completion.
   1360  // Allow in dummy buffers since they are only used internally
   1361  if (textlock != 0 || ins_compl_active() || compl_busy || pum_visible()
   1362      || expr_map_locked()) {
   1363    emsg(_(e_textlock));
   1364    return false;
   1365  }
   1366 
   1367  InsertState s[1];
   1368  memset(s, 0, sizeof(InsertState));
   1369  s->state.execute = insert_execute;
   1370  s->state.check = insert_check;
   1371  s->cmdchar = cmdchar;
   1372  s->startln = startln;
   1373  s->count = count;
   1374  insert_enter(s);
   1375  return s->c == Ctrl_O;
   1376 }
   1377 
   1378 bool ins_need_undo_get(void)
   1379 {
   1380  return ins_need_undo;
   1381 }
   1382 
   1383 /// Redraw for Insert mode.
   1384 /// This is postponed until getting the next character to make '$' in the 'cpo'
   1385 /// option work correctly.
   1386 /// Only redraw when there are no characters available.  This speeds up
   1387 /// inserting sequences of characters (e.g., for CTRL-R).
   1388 ///
   1389 /// @param ready  not busy with something
   1390 void ins_redraw(bool ready)
   1391 {
   1392  if (char_avail()) {
   1393    return;
   1394  }
   1395 
   1396  // Trigger CursorMoved if the cursor moved.  Not when the popup menu is
   1397  // visible, the command might delete it.
   1398  if (ready && has_event(EVENT_CURSORMOVEDI)
   1399      && (last_cursormoved_win != curwin
   1400          || !equalpos(last_cursormoved, curwin->w_cursor))
   1401      && !pum_visible()) {
   1402    // Need to update the screen first, to make sure syntax
   1403    // highlighting is correct after making a change (e.g., inserting
   1404    // a "(".  The autocommand may also require a redraw, so it's done
   1405    // again below, unfortunately.
   1406    if (syntax_present(curwin) && must_redraw) {
   1407      update_screen();
   1408    }
   1409    // Make sure curswant is correct, an autocommand may call
   1410    // getcurpos()
   1411    update_curswant();
   1412    ins_apply_autocmds(EVENT_CURSORMOVEDI);
   1413    last_cursormoved_win = curwin;
   1414    last_cursormoved = curwin->w_cursor;
   1415  }
   1416 
   1417  // Trigger TextChangedI if changedtick_i differs.
   1418  if (ready && has_event(EVENT_TEXTCHANGEDI)
   1419      && curbuf->b_last_changedtick_i != buf_get_changedtick(curbuf)
   1420      && !pum_visible()) {
   1421    aco_save_T aco;
   1422    varnumber_T tick = buf_get_changedtick(curbuf);
   1423 
   1424    // save and restore curwin and curbuf, in case the autocmd changes them
   1425    aucmd_prepbuf(&aco, curbuf);
   1426    apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, false, curbuf);
   1427    aucmd_restbuf(&aco);
   1428    curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf);
   1429    if (tick != buf_get_changedtick(curbuf)) {  // see ins_apply_autocmds()
   1430      u_save(curwin->w_cursor.lnum,
   1431             (linenr_T)(curwin->w_cursor.lnum + 1));
   1432    }
   1433  }
   1434 
   1435  // Trigger TextChangedP if changedtick_pum differs. When the popupmenu
   1436  // closes TextChangedI will need to trigger for backwards compatibility,
   1437  // thus use different b_last_changedtick* variables.
   1438  if (ready && has_event(EVENT_TEXTCHANGEDP)
   1439      && curbuf->b_last_changedtick_pum != buf_get_changedtick(curbuf)
   1440      && pum_visible()) {
   1441    aco_save_T aco;
   1442    varnumber_T tick = buf_get_changedtick(curbuf);
   1443 
   1444    // save and restore curwin and curbuf, in case the autocmd changes them
   1445    aucmd_prepbuf(&aco, curbuf);
   1446    apply_autocmds(EVENT_TEXTCHANGEDP, NULL, NULL, false, curbuf);
   1447    aucmd_restbuf(&aco);
   1448    curbuf->b_last_changedtick_pum = buf_get_changedtick(curbuf);
   1449    if (tick != buf_get_changedtick(curbuf)) {  // see ins_apply_autocmds()
   1450      u_save(curwin->w_cursor.lnum,
   1451             (linenr_T)(curwin->w_cursor.lnum + 1));
   1452    }
   1453  }
   1454 
   1455  if (ready) {
   1456    may_trigger_win_scrolled_resized();
   1457  }
   1458 
   1459  // Trigger BufModified if b_changed_invalid is set.
   1460  if (ready && has_event(EVENT_BUFMODIFIEDSET)
   1461      && curbuf->b_changed_invalid == true
   1462      && !pum_visible()) {
   1463    apply_autocmds(EVENT_BUFMODIFIEDSET, NULL, NULL, false, curbuf);
   1464    curbuf->b_changed_invalid = false;
   1465  }
   1466 
   1467  // Trigger SafeState if nothing is pending.
   1468  may_trigger_safestate(ready
   1469                        && !ins_compl_active()
   1470                        && !pum_visible());
   1471 
   1472  pum_check_clear();
   1473  show_cursor_info_later(false);
   1474  if (must_redraw) {
   1475    update_screen();
   1476  } else {
   1477    redraw_statuslines();
   1478    if (clear_cmdline || redraw_cmdline || redraw_mode) {
   1479      showmode();  // clear cmdline and show mode
   1480    }
   1481  }
   1482  setcursor();
   1483  emsg_on_display = false;      // may remove error message now
   1484 }
   1485 
   1486 // Handle a CTRL-V or CTRL-Q typed in Insert mode.
   1487 static void ins_ctrl_v(void)
   1488 {
   1489  bool did_putchar = false;
   1490 
   1491  // may need to redraw when no more chars available now
   1492  ins_redraw(false);
   1493 
   1494  if (redrawing() && !char_avail()) {
   1495    edit_putchar('^', true);
   1496    did_putchar = true;
   1497  }
   1498  AppendToRedobuff(CTRL_V_STR);
   1499 
   1500  add_to_showcmd_c(Ctrl_V);
   1501 
   1502  // Do not include modifiers into the key for CTRL-SHIFT-V.
   1503  int c = get_literal(mod_mask & MOD_MASK_SHIFT);
   1504  if (did_putchar) {
   1505    // when the line fits in 'columns' the '^' is at the start of the next
   1506    // line and will not removed by the redraw
   1507    edit_unputchar();
   1508  }
   1509  clear_showcmd();
   1510  insert_special(c, true, true);
   1511  revins_chars++;
   1512  revins_legal++;
   1513 }
   1514 
   1515 // Put a character directly onto the screen.  It's not stored in a buffer.
   1516 // Used while handling CTRL-K, CTRL-V, etc. in Insert mode.
   1517 static int pc_status;
   1518 #define PC_STATUS_UNSET 0  // nothing was put on screen
   1519 #define PC_STATUS_RIGHT 1  // right half of double-wide char
   1520 #define PC_STATUS_LEFT  2  // left half of double-wide char
   1521 #define PC_STATUS_SET   3  // pc_schar was filled
   1522 static schar_T pc_schar;   // saved char
   1523 static int pc_attr;
   1524 static int pc_row;
   1525 static int pc_col;
   1526 
   1527 void edit_putchar(int c, bool highlight)
   1528 {
   1529  if (curwin->w_grid_alloc.chars == NULL && default_grid.chars == NULL) {
   1530    return;
   1531  }
   1532 
   1533  int attr;
   1534  update_topline(curwin);  // just in case w_topline isn't valid
   1535  validate_cursor(curwin);
   1536  if (highlight) {
   1537    attr = HL_ATTR(HLF_8);
   1538  } else {
   1539    attr = 0;
   1540  }
   1541  pc_row = curwin->w_wrow;
   1542  pc_status = PC_STATUS_UNSET;
   1543  grid_line_start(&curwin->w_grid, pc_row);
   1544  if (curwin->w_p_rl) {
   1545    pc_col = curwin->w_view_width - 1 - curwin->w_wcol;
   1546 
   1547    if (grid_line_getchar(pc_col, NULL) == NUL) {
   1548      grid_line_put_schar(pc_col - 1, schar_from_ascii(' '), attr);
   1549      curwin->w_wcol--;
   1550      pc_status = PC_STATUS_RIGHT;
   1551    }
   1552  } else {
   1553    pc_col = curwin->w_wcol;
   1554 
   1555    if (grid_line_getchar(pc_col + 1, NULL) == NUL) {
   1556      // pc_col is the left half of a double-width char
   1557      pc_status = PC_STATUS_LEFT;
   1558    }
   1559  }
   1560 
   1561  // save the character to be able to put it back
   1562  if (pc_status == PC_STATUS_UNSET) {
   1563    pc_schar = grid_line_getchar(pc_col, &pc_attr);
   1564    pc_status = PC_STATUS_SET;
   1565  }
   1566 
   1567  char buf[MB_MAXCHAR + 1];
   1568  grid_line_puts(pc_col, buf, utf_char2bytes(c, buf), attr);
   1569  grid_line_flush();
   1570 }
   1571 
   1572 /// @return    the effective prompt for the specified buffer.
   1573 char *buf_prompt_text(const buf_T *const buf)
   1574  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
   1575 {
   1576  if (buf->b_prompt_text == NULL) {
   1577    return "% ";
   1578  }
   1579  return buf->b_prompt_text;
   1580 }
   1581 
   1582 /// @return  the effective prompt for the current buffer.
   1583 char *prompt_text(void)
   1584  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
   1585 {
   1586  return buf_prompt_text(curbuf);
   1587 }
   1588 
   1589 // Prepare for prompt mode: Make sure the last line has the prompt text.
   1590 // Move the cursor to this line.
   1591 static void init_prompt(int cmdchar_todo)
   1592 {
   1593  char *prompt = prompt_text();
   1594  int prompt_len = (int)strlen(prompt);
   1595 
   1596  // In case the mark is set to a nonexistent line.
   1597  curbuf->b_prompt_start.mark.lnum = MAX(1, MIN(curbuf->b_prompt_start.mark.lnum,
   1598                                                curbuf->b_ml.ml_line_count));
   1599 
   1600  curwin->w_cursor.lnum = MAX(curwin->w_cursor.lnum, curbuf->b_prompt_start.mark.lnum);
   1601  char *text = ml_get(curbuf->b_prompt_start.mark.lnum);
   1602  colnr_T text_len = ml_get_len(curbuf->b_prompt_start.mark.lnum);
   1603 
   1604  if ((curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum
   1605       && (curbuf->b_prompt_start.mark.col < prompt_len
   1606           || curbuf->b_prompt_start.mark.col > text_len
   1607           || !strnequal(text + curbuf->b_prompt_start.mark.col - prompt_len, prompt,
   1608                         (size_t)prompt_len)))) {
   1609    // prompt is missing, insert it or append a line with it
   1610    if (*text == NUL) {
   1611      ml_replace(curbuf->b_prompt_start.mark.lnum, prompt, true);
   1612      inserted_bytes(curbuf->b_prompt_start.mark.lnum, 0, 0, prompt_len);
   1613    } else {
   1614      const linenr_T lnum = curbuf->b_ml.ml_line_count;
   1615      ml_append(lnum, prompt, 0, false);
   1616      appended_lines_mark(lnum, 1);
   1617      curbuf->b_prompt_start.mark.lnum = curbuf->b_ml.ml_line_count;
   1618      // Like submitting, undo history was relevant to the old prompt.
   1619      u_clearallandblockfree(curbuf);
   1620    }
   1621    curbuf->b_prompt_start.mark.col = prompt_len;
   1622    curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
   1623    coladvance(curwin, MAXCOL);
   1624  }
   1625 
   1626  // Insert always starts after the prompt, allow editing text after it.
   1627  if (Insstart_orig.lnum != curbuf->b_prompt_start.mark.lnum
   1628      || Insstart_orig.col != curbuf->b_prompt_start.mark.col) {
   1629    Insstart.lnum = curbuf->b_prompt_start.mark.lnum;
   1630    Insstart.col = curbuf->b_prompt_start.mark.col;
   1631    Insstart_orig = Insstart;
   1632    Insstart_textlen = Insstart.col;
   1633    Insstart_blank_vcol = MAXCOL;
   1634    arrow_used = false;
   1635  }
   1636 
   1637  if (cmdchar_todo == 'A') {
   1638    coladvance(curwin, MAXCOL);
   1639  }
   1640  if (curbuf->b_prompt_start.mark.lnum == curwin->w_cursor.lnum) {
   1641    curwin->w_cursor.col = MAX(curwin->w_cursor.col, curbuf->b_prompt_start.mark.col);
   1642  }
   1643  // Make sure the cursor is in a valid position.
   1644  check_cursor(curwin);
   1645 }
   1646 
   1647 /// @return  true if the cursor is in the editable position of the prompt line.
   1648 bool prompt_curpos_editable(void)
   1649  FUNC_ATTR_PURE
   1650 {
   1651  return curwin->w_cursor.lnum > curbuf->b_prompt_start.mark.lnum
   1652         || (curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum
   1653             && curwin->w_cursor.col >= curbuf->b_prompt_start.mark.col);
   1654 }
   1655 
   1656 // Undo the previous edit_putchar().
   1657 void edit_unputchar(void)
   1658 {
   1659  if (pc_status != PC_STATUS_UNSET) {
   1660    if (pc_status == PC_STATUS_RIGHT) {
   1661      curwin->w_wcol++;
   1662    }
   1663    if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) {
   1664      redrawWinline(curwin, curwin->w_cursor.lnum);
   1665    } else {
   1666      // TODO(bfredl): this could be smarter and also handle the dubyawidth case
   1667      grid_line_start(&curwin->w_grid, pc_row);
   1668      grid_line_put_schar(pc_col, pc_schar, pc_attr);
   1669      grid_line_flush();
   1670    }
   1671  }
   1672 }
   1673 
   1674 /// Called when "$" is in 'cpoptions': display a '$' at the end of the changed
   1675 /// text.  Only works when cursor is in the line that changes.
   1676 void display_dollar(colnr_T col_arg)
   1677 {
   1678  colnr_T col = MAX(col_arg, 0);
   1679 
   1680  if (!redrawing()) {
   1681    return;
   1682  }
   1683 
   1684  colnr_T save_col = curwin->w_cursor.col;
   1685  curwin->w_cursor.col = col;
   1686 
   1687  // If on the last byte of a multi-byte move to the first byte.
   1688  char *p = get_cursor_line_ptr();
   1689  curwin->w_cursor.col -= utf_head_off(p, p + col);
   1690  curs_columns(curwin, false);              // Recompute w_wrow and w_wcol
   1691  if (curwin->w_wcol < curwin->w_view_width) {
   1692    edit_putchar('$', false);
   1693    dollar_vcol = curwin->w_virtcol;
   1694  }
   1695  curwin->w_cursor.col = save_col;
   1696 }
   1697 
   1698 // Call this function before moving the cursor from the normal insert position
   1699 // in insert mode.
   1700 void undisplay_dollar(void)
   1701 {
   1702  if (dollar_vcol < 0) {
   1703    return;
   1704  }
   1705 
   1706  dollar_vcol = -1;
   1707  redrawWinline(curwin, curwin->w_cursor.lnum);
   1708 }
   1709 
   1710 /// Truncate the space at the end of a line.  This is to be used only in an
   1711 /// insert mode.  It handles fixing the replace stack for MODE_REPLACE and
   1712 /// MODE_VREPLACE modes.
   1713 void truncate_spaces(char *line, size_t len)
   1714 {
   1715  int i;
   1716 
   1717  // find start of trailing white space
   1718  for (i = (int)len - 1; i >= 0 && ascii_iswhite(line[i]); i--) {
   1719    if (State & REPLACE_FLAG) {
   1720      replace_join(0);              // remove a NUL from the replace stack
   1721    }
   1722  }
   1723  line[i + 1] = NUL;
   1724 }
   1725 
   1726 /// Backspace the cursor until the given column.  Handles MODE_REPLACE and
   1727 /// MODE_VREPLACE modes correctly.  May also be used when not in insert mode at
   1728 /// all.  Will attempt not to go before "col" even when there is a composing
   1729 /// character.
   1730 void backspace_until_column(int col)
   1731 {
   1732  while ((int)curwin->w_cursor.col > col) {
   1733    curwin->w_cursor.col--;
   1734    if (State & REPLACE_FLAG) {
   1735      replace_do_bs(col);
   1736    } else if (!del_char_after_col(col)) {
   1737      break;
   1738    }
   1739  }
   1740 }
   1741 
   1742 /// Like del_char(), but make sure not to go before column "limit_col".
   1743 /// Only matters when there are composing characters.
   1744 ///
   1745 /// @param  limit_col  only delete the character if it is after this column
   1746 //
   1747 /// @return true when something was deleted.
   1748 static bool del_char_after_col(int limit_col)
   1749 {
   1750  if (limit_col >= 0) {
   1751    colnr_T ecol = curwin->w_cursor.col + 1;
   1752 
   1753    // Make sure the cursor is at the start of a character, but
   1754    // skip forward again when going too far back because of a
   1755    // composing character.
   1756    mb_adjust_cursor();
   1757    while (curwin->w_cursor.col < (colnr_T)limit_col) {
   1758      int l = utf_ptr2len(get_cursor_pos_ptr());
   1759 
   1760      if (l == 0) {  // end of line
   1761        break;
   1762      }
   1763      curwin->w_cursor.col += l;
   1764    }
   1765    if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) {
   1766      return false;
   1767    }
   1768    del_bytes(ecol - curwin->w_cursor.col, false, true);
   1769  } else {
   1770    del_char(false);
   1771  }
   1772  return true;
   1773 }
   1774 
   1775 /// Next character is interpreted literally.
   1776 /// A one, two or three digit decimal number is interpreted as its byte value.
   1777 /// If one or two digits are entered, the next character is given to vungetc().
   1778 /// For Unicode a character > 255 may be returned.
   1779 ///
   1780 /// @param  no_simplify  do not include modifiers into the key
   1781 int get_literal(bool no_simplify)
   1782 {
   1783  int nc;
   1784  bool hex = false;
   1785  bool octal = false;
   1786  int unicode = 0;
   1787 
   1788  if (got_int) {
   1789    return Ctrl_C;
   1790  }
   1791 
   1792  no_mapping++;                 // don't map the next key hits
   1793  int cc = 0;
   1794  int i = 0;
   1795  while (true) {
   1796    nc = plain_vgetc();
   1797    if (!no_simplify) {
   1798      nc = merge_modifiers(nc, &mod_mask);
   1799    }
   1800    if ((mod_mask & ~MOD_MASK_SHIFT) != 0) {
   1801      // A character with non-Shift modifiers should not be a valid
   1802      // character for i_CTRL-V_digit.
   1803      break;
   1804    }
   1805    if ((State & MODE_CMDLINE) == 0 && MB_BYTE2LEN_CHECK(nc) == 1) {
   1806      add_to_showcmd(nc);
   1807    }
   1808    if (nc == 'x' || nc == 'X') {
   1809      hex = true;
   1810    } else if (nc == 'o' || nc == 'O') {
   1811      octal = true;
   1812    } else if (nc == 'u' || nc == 'U') {
   1813      unicode = nc;
   1814    } else {
   1815      if (hex
   1816          || unicode != 0) {
   1817        if (!ascii_isxdigit(nc)) {
   1818          break;
   1819        }
   1820        cc = cc * 16 + hex2nr(nc);
   1821      } else if (octal) {
   1822        if (nc < '0' || nc > '7') {
   1823          break;
   1824        }
   1825        cc = cc * 8 + nc - '0';
   1826      } else {
   1827        if (!ascii_isdigit(nc)) {
   1828          break;
   1829        }
   1830        cc = cc * 10 + nc - '0';
   1831      }
   1832 
   1833      i++;
   1834    }
   1835 
   1836    if (cc > 255
   1837        && unicode == 0) {
   1838      cc = 255;                 // limit range to 0-255
   1839    }
   1840    nc = 0;
   1841 
   1842    if (hex) {                  // hex: up to two chars
   1843      if (i >= 2) {
   1844        break;
   1845      }
   1846    } else if (unicode) {     // Unicode: up to four or eight chars
   1847      if ((unicode == 'u' && i >= 4) || (unicode == 'U' && i >= 8)) {
   1848        break;
   1849      }
   1850    } else if (i >= 3) {        // decimal or octal: up to three chars
   1851      break;
   1852    }
   1853  }
   1854  if (i == 0) {     // no number entered
   1855    if (nc == K_ZERO) {     // NUL is stored as NL
   1856      cc = '\n';
   1857      nc = 0;
   1858    } else {
   1859      cc = nc;
   1860      nc = 0;
   1861    }
   1862  }
   1863 
   1864  if (cc == 0) {        // NUL is stored as NL
   1865    cc = '\n';
   1866  }
   1867 
   1868  no_mapping--;
   1869  if (nc) {
   1870    vungetc(nc);
   1871    // A character typed with i_CTRL-V_digit cannot have modifiers.
   1872    mod_mask = 0;
   1873  }
   1874  got_int = false;          // CTRL-C typed after CTRL-V is not an interrupt
   1875  return cc;
   1876 }
   1877 
   1878 /// Insert character, taking care of special keys and mod_mask
   1879 ///
   1880 /// @param ctrlv `c` was typed after CTRL-V
   1881 static void insert_special(int c, int allow_modmask, int ctrlv)
   1882 {
   1883  // Special function key, translate into "<Key>". Up to the last '>' is
   1884  // inserted with ins_str(), so as not to replace characters in replace
   1885  // mode.
   1886  // Only use mod_mask for special keys, to avoid things like <S-Space>,
   1887  // unless 'allow_modmask' is true.
   1888  if (mod_mask & MOD_MASK_CMD) {  // Command-key never produces a normal key.
   1889    allow_modmask = true;
   1890  }
   1891  if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) {
   1892    char *p = get_special_key_name(c, mod_mask);
   1893    int len = (int)strlen(p);
   1894    c = (uint8_t)p[len - 1];
   1895    if (len > 2) {
   1896      if (stop_arrow() == FAIL) {
   1897        return;
   1898      }
   1899      p[len - 1] = NUL;
   1900      ins_str(p, (size_t)(len - 1));
   1901      AppendToRedobuffLit(p, -1);
   1902      ctrlv = false;
   1903    }
   1904  }
   1905  if (stop_arrow() == OK) {
   1906    insertchar(c, ctrlv ? INSCHAR_CTRLV : 0, -1);
   1907  }
   1908 }
   1909 
   1910 // Special characters in this context are those that need processing other
   1911 // than the simple insertion that can be performed here. This includes ESC
   1912 // which terminates the insert, and CR/NL which need special processing to
   1913 // open up a new line. This routine tries to optimize insertions performed by
   1914 // the "redo", "undo" or "put" commands, so it needs to know when it should
   1915 // stop and defer processing to the "normal" mechanism.
   1916 // '0' and '^' are special, because they can be followed by CTRL-D.
   1917 #define ISSPECIAL(c)   ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^')
   1918 
   1919 /// "flags": INSCHAR_FORMAT - force formatting
   1920 ///          INSCHAR_CTRLV  - char typed just after CTRL-V
   1921 ///          INSCHAR_NO_FEX - don't use 'formatexpr'
   1922 ///
   1923 ///   NOTE: passes the flags value straight through to internal_format() which,
   1924 ///         beside INSCHAR_FORMAT (above), is also looking for these:
   1925 ///          INSCHAR_DO_COM   - format comments
   1926 ///          INSCHAR_COM_LIST - format comments with num list or 2nd line indent
   1927 ///
   1928 /// @param c              character to insert or NUL
   1929 /// @param flags          INSCHAR_FORMAT, etc.
   1930 /// @param second_indent  indent for second line if >= 0
   1931 void insertchar(int c, int flags, int second_indent)
   1932 {
   1933  char *p;
   1934  int force_format = flags & INSCHAR_FORMAT;
   1935 
   1936  const int textwidth = comp_textwidth(force_format);
   1937  const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
   1938 
   1939  // Try to break the line in two or more pieces when:
   1940  // - Always do this if we have been called to do formatting only.
   1941  // - Always do this when 'formatoptions' has the 'a' flag and the line
   1942  //   ends in white space.
   1943  // - Otherwise:
   1944  //     - Don't do this if inserting a blank
   1945  //     - Don't do this if an existing character is being replaced, unless
   1946  //       we're in MODE_VREPLACE state.
   1947  //     - Do this if the cursor is not on the line where insert started
   1948  //     or - 'formatoptions' doesn't have 'l' or the line was not too long
   1949  //           before the insert.
   1950  //        - 'formatoptions' doesn't have 'b' or a blank was inserted at or
   1951  //          before 'textwidth'
   1952  if (textwidth > 0
   1953      && (force_format
   1954          || (!ascii_iswhite(c)
   1955              && !((State & REPLACE_FLAG)
   1956                   && !(State & VREPLACE_FLAG)
   1957                   && *get_cursor_pos_ptr() != NUL)
   1958              && (curwin->w_cursor.lnum != Insstart.lnum
   1959                  || ((!has_format_option(FO_INS_LONG)
   1960                       || Insstart_textlen <= (colnr_T)textwidth)
   1961                      && (!fo_ins_blank
   1962                          || Insstart_blank_vcol <= (colnr_T)textwidth)))))) {
   1963    // Format with 'formatexpr' when it's set.  Use internal formatting
   1964    // when 'formatexpr' isn't set or it returns non-zero.
   1965    bool do_internal = true;
   1966    colnr_T virtcol = get_nolist_virtcol()
   1967                      + char2cells(c != NUL ? c : gchar_cursor());
   1968 
   1969    if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0
   1970        && (force_format || virtcol > (colnr_T)textwidth)) {
   1971      do_internal = (fex_format(curwin->w_cursor.lnum, 1, c) != 0);
   1972      // It may be required to save for undo again, e.g. when setline()
   1973      // was called.
   1974      ins_need_undo = true;
   1975    }
   1976    if (do_internal) {
   1977      internal_format(textwidth, second_indent, flags, c == NUL, c);
   1978    }
   1979  }
   1980 
   1981  if (c == NUL) {           // only formatting was wanted
   1982    return;
   1983  }
   1984 
   1985  // Check whether this character should end a comment.
   1986  if (did_ai && c == end_comment_pending) {
   1987    char lead_end[COM_MAX_LEN];  // end-comment string
   1988 
   1989    // Need to remove existing (middle) comment leader and insert end
   1990    // comment leader.  First, check what comment leader we can find.
   1991    char *line = get_cursor_line_ptr();
   1992    int i = get_leader_len(line, &p, false, true);
   1993    if (i > 0 && vim_strchr(p, COM_MIDDLE) != NULL) {  // Just checking
   1994      // Skip middle-comment string
   1995      while (*p && p[-1] != ':') {  // find end of middle flags
   1996        p++;
   1997      }
   1998      int middle_len = (int)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
   1999      // Don't count trailing white space for middle_len
   2000      while (middle_len > 0 && ascii_iswhite(lead_end[middle_len - 1])) {
   2001        middle_len--;
   2002      }
   2003 
   2004      // Find the end-comment string
   2005      while (*p && p[-1] != ':') {  // find end of end flags
   2006        p++;
   2007      }
   2008      int end_len = (int)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
   2009 
   2010      // Skip white space before the cursor
   2011      i = curwin->w_cursor.col;
   2012      while (--i >= 0 && ascii_iswhite(line[i])) {}
   2013      i++;
   2014 
   2015      // Skip to before the middle leader
   2016      i -= middle_len;
   2017 
   2018      // Check some expected things before we go on
   2019      if (i >= 0 && end_len > 0
   2020          && (uint8_t)lead_end[end_len - 1] == end_comment_pending) {
   2021        // Backspace over all the stuff we want to replace
   2022        backspace_until_column(i);
   2023 
   2024        // Insert the end-comment string, except for the last
   2025        // character, which will get inserted as normal later.
   2026        ins_bytes_len(lead_end, (size_t)(end_len - 1));
   2027      }
   2028    }
   2029  }
   2030  end_comment_pending = NUL;
   2031 
   2032  did_ai = false;
   2033  did_si = false;
   2034  can_si = false;
   2035  can_si_back = false;
   2036 
   2037  // If there's any pending input, grab up to INPUT_BUFLEN at once.
   2038  // This speeds up normal text input considerably.
   2039  // Don't do this when 'cindent' or 'indentexpr' is set, because we might
   2040  // need to re-indent at a ':', or any other character (but not what
   2041  // 'paste' is set)..
   2042  // Don't do this when there an InsertCharPre autocommand is defined,
   2043  // because we need to fire the event for every character.
   2044  // Do the check for InsertCharPre before the call to vpeekc() because the
   2045  // InsertCharPre autocommand could change the input buffer.
   2046  if (!ISSPECIAL(c)
   2047      && (utf_char2len(c) == 1)
   2048      && !has_event(EVENT_INSERTCHARPRE)
   2049      && !test_disable_char_avail
   2050      && vpeekc() != NUL
   2051      && !(State & REPLACE_FLAG)
   2052      && !cindent_on()
   2053      && !p_ri) {
   2054 #define INPUT_BUFLEN 100
   2055    char buf[INPUT_BUFLEN + 1];
   2056    colnr_T virtcol = 0;
   2057 
   2058    buf[0] = (char)c;
   2059    int i = 1;
   2060    if (textwidth > 0) {
   2061      virtcol = get_nolist_virtcol();
   2062    }
   2063    // Stop the string when:
   2064    // - no more chars available
   2065    // - finding a special character (command key)
   2066    // - buffer is full
   2067    // - running into the 'textwidth' boundary
   2068    // - need to check for abbreviation: A non-word char after a word-char
   2069    while ((c = vpeekc()) != NUL
   2070           && !ISSPECIAL(c)
   2071           && MB_BYTE2LEN(c) == 1
   2072           && i < INPUT_BUFLEN
   2073           && (textwidth == 0
   2074               || (virtcol += byte2cells((uint8_t)buf[i - 1])) < (colnr_T)textwidth)
   2075           && !(!no_abbr && !vim_iswordc(c) && vim_iswordc((uint8_t)buf[i - 1]))) {
   2076      c = vgetc();
   2077      buf[i++] = (char)c;
   2078    }
   2079 
   2080    do_digraph(-1);                     // clear digraphs
   2081    do_digraph((uint8_t)buf[i - 1]);               // may be the start of a digraph
   2082    buf[i] = NUL;
   2083    ins_str(buf, (size_t)i);
   2084    if (flags & INSCHAR_CTRLV) {
   2085      redo_literal((uint8_t)(*buf));
   2086      i = 1;
   2087    } else {
   2088      i = 0;
   2089    }
   2090    if (buf[i] != NUL) {
   2091      AppendToRedobuffLit(buf + i, -1);
   2092    }
   2093  } else {
   2094    int cc;
   2095 
   2096    if ((cc = utf_char2len(c)) > 1) {
   2097      char buf[MB_MAXCHAR + 1];
   2098 
   2099      utf_char2bytes(c, buf);
   2100      buf[cc] = NUL;
   2101      ins_char_bytes(buf, (size_t)cc);
   2102      AppendCharToRedobuff(c);
   2103    } else {
   2104      ins_char(c);
   2105      if (flags & INSCHAR_CTRLV) {
   2106        redo_literal(c);
   2107      } else {
   2108        AppendCharToRedobuff(c);
   2109      }
   2110    }
   2111  }
   2112 }
   2113 
   2114 // Put a character in the redo buffer, for when just after a CTRL-V.
   2115 static void redo_literal(int c)
   2116 {
   2117  char buf[10];
   2118 
   2119  // Only digits need special treatment.  Translate them into a string of
   2120  // three digits.
   2121  if (ascii_isdigit(c)) {
   2122    vim_snprintf(buf, sizeof(buf), "%03d", c);
   2123    AppendToRedobuff(buf);
   2124  } else {
   2125    AppendCharToRedobuff(c);
   2126  }
   2127 }
   2128 
   2129 /// start_arrow() is called when an arrow key is used in insert mode.
   2130 /// For undo/redo it resembles hitting the <ESC> key.
   2131 ///
   2132 /// @param end_insert_pos  can be NULL
   2133 void start_arrow(pos_T *end_insert_pos)
   2134 {
   2135  start_arrow_common(end_insert_pos, true);
   2136 }
   2137 
   2138 /// Like start_arrow() but with end_change argument.
   2139 /// Will prepare for redo of CTRL-G U if "end_change" is false.
   2140 ///
   2141 /// @param end_insert_pos  can be NULL
   2142 /// @param end_change      end undoable change
   2143 static void start_arrow_with_change(pos_T *end_insert_pos, bool end_change)
   2144 {
   2145  start_arrow_common(end_insert_pos, end_change);
   2146  if (!end_change) {
   2147    AppendCharToRedobuff(Ctrl_G);
   2148    AppendCharToRedobuff('U');
   2149  }
   2150 }
   2151 
   2152 /// @param end_insert_pos  can be NULL
   2153 /// @param end_change      end undoable change
   2154 static void start_arrow_common(pos_T *end_insert_pos, bool end_change)
   2155 {
   2156  if (!arrow_used && end_change) {  // something has been inserted
   2157    AppendToRedobuff(ESC_STR);
   2158    stop_insert(end_insert_pos, false, false);
   2159    arrow_used = true;  // This means we stopped the current insert.
   2160  }
   2161  check_spell_redraw();
   2162 }
   2163 
   2164 // If we skipped highlighting word at cursor, do it now.
   2165 // It may be skipped again, thus reset spell_redraw_lnum first.
   2166 static void check_spell_redraw(void)
   2167 {
   2168  if (spell_redraw_lnum != 0) {
   2169    linenr_T lnum = spell_redraw_lnum;
   2170 
   2171    spell_redraw_lnum = 0;
   2172    redrawWinline(curwin, lnum);
   2173  }
   2174 }
   2175 
   2176 // stop_arrow() is called before a change is made in insert mode.
   2177 // If an arrow key has been used, start a new insertion.
   2178 // Returns FAIL if undo is impossible, shouldn't insert then.
   2179 int stop_arrow(void)
   2180 {
   2181  if (arrow_used) {
   2182    Insstart = curwin->w_cursor;  // new insertion starts here
   2183    if (Insstart.col > Insstart_orig.col && !ins_need_undo) {
   2184      // Don't update the original insert position when moved to the
   2185      // right, except when nothing was inserted yet.
   2186      update_Insstart_orig = false;
   2187    }
   2188    Insstart_textlen = linetabsize_str(get_cursor_line_ptr());
   2189 
   2190    if (u_save_cursor() == OK) {
   2191      arrow_used = false;
   2192      ins_need_undo = false;
   2193    }
   2194    ai_col = 0;
   2195    if (State & VREPLACE_FLAG) {
   2196      orig_line_count = curbuf->b_ml.ml_line_count;
   2197      vr_lines_changed = 1;
   2198    }
   2199    ResetRedobuff();
   2200    AppendToRedobuff("1i");  // Pretend we start an insertion.
   2201    new_insert_skip = 2;
   2202  } else if (ins_need_undo) {
   2203    if (u_save_cursor() == OK) {
   2204      ins_need_undo = false;
   2205    }
   2206  }
   2207 
   2208  // Always open fold at the cursor line when inserting something.
   2209  foldOpenCursor();
   2210 
   2211  return arrow_used || ins_need_undo ? FAIL : OK;
   2212 }
   2213 
   2214 /// Do a few things to stop inserting.
   2215 /// "end_insert_pos" is where insert ended.  It is NULL when we already jumped
   2216 /// to another window/buffer.
   2217 ///
   2218 /// @param esc     called by ins_esc()
   2219 /// @param nomove  <c-\><c-o>, don't move cursor
   2220 static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
   2221 {
   2222  stop_redo_ins();
   2223  kv_destroy(replace_stack);  // abandon replace stack (reinitializes)
   2224 
   2225  // Save the inserted text for later redo with ^@ and CTRL-A.
   2226  // Don't do it when "restart_edit" was set and nothing was inserted,
   2227  // otherwise CTRL-O w and then <Left> will clear "last_insert".
   2228  String inserted = get_inserted();
   2229  int added = inserted.data == NULL ? 0 : (int)inserted.size - new_insert_skip;
   2230  if (did_restart_edit == 0 || added > 0) {
   2231    xfree(last_insert.data);
   2232    last_insert = inserted;  // structure copy
   2233    last_insert_skip = added < 0 ? 0 : new_insert_skip;
   2234  } else {
   2235    xfree(inserted.data);
   2236  }
   2237 
   2238  if (!arrow_used && end_insert_pos != NULL) {
   2239    int cc;
   2240    // Auto-format now.  It may seem strange to do this when stopping an
   2241    // insertion (or moving the cursor), but it's required when appending
   2242    // a line and having it end in a space.  But only do it when something
   2243    // was actually inserted, otherwise undo won't work.
   2244    if (!ins_need_undo && has_format_option(FO_AUTO)) {
   2245      pos_T tpos = curwin->w_cursor;
   2246 
   2247      // When the cursor is at the end of the line after a space the
   2248      // formatting will move it to the following word.  Avoid that by
   2249      // moving the cursor onto the space.
   2250      cc = 'x';
   2251      if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL) {
   2252        dec_cursor();
   2253        cc = gchar_cursor();
   2254        if (!ascii_iswhite(cc)) {
   2255          curwin->w_cursor = tpos;
   2256        }
   2257      }
   2258 
   2259      auto_format(true, false);
   2260 
   2261      if (ascii_iswhite(cc)) {
   2262        if (gchar_cursor() != NUL) {
   2263          inc_cursor();
   2264        }
   2265        // If the cursor is still at the same character, also keep
   2266        // the "coladd".
   2267        if (gchar_cursor() == NUL
   2268            && curwin->w_cursor.lnum == tpos.lnum
   2269            && curwin->w_cursor.col == tpos.col) {
   2270          curwin->w_cursor.coladd = tpos.coladd;
   2271        }
   2272      }
   2273    }
   2274 
   2275    // If a space was inserted for auto-formatting, remove it now.
   2276    check_auto_format(true);
   2277 
   2278    // If we just did an auto-indent, remove the white space from the end
   2279    // of the line, and put the cursor back.
   2280    // Do this when ESC was used or moving the cursor up/down.
   2281    // Check for the old position still being valid, just in case the text
   2282    // got changed unexpectedly.
   2283    if (!nomove && did_ai && (esc || (vim_strchr(p_cpo, CPO_INDENT) == NULL
   2284                                      && curwin->w_cursor.lnum !=
   2285                                      end_insert_pos->lnum))
   2286        && end_insert_pos->lnum <= curbuf->b_ml.ml_line_count) {
   2287      pos_T tpos = curwin->w_cursor;
   2288      colnr_T prev_col = end_insert_pos->col;
   2289 
   2290      curwin->w_cursor = *end_insert_pos;
   2291      check_cursor_col(curwin);        // make sure it is not past the line
   2292      while (true) {
   2293        if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) {
   2294          curwin->w_cursor.col--;
   2295        }
   2296        cc = gchar_cursor();
   2297        if (!ascii_iswhite(cc)) {
   2298          break;
   2299        }
   2300        if (del_char(true) == FAIL) {
   2301          break;            // should not happen
   2302        }
   2303      }
   2304      if (curwin->w_cursor.lnum != tpos.lnum) {
   2305        curwin->w_cursor = tpos;
   2306      } else if (curwin->w_cursor.col < prev_col) {
   2307        // reset tpos, could have been invalidated in the loop above
   2308        tpos = curwin->w_cursor;
   2309        tpos.col++;
   2310        if (cc != NUL && gchar_pos(&tpos) == NUL) {
   2311          curwin->w_cursor.col++;         // put cursor back on the NUL
   2312        }
   2313      }
   2314 
   2315      // <C-S-Right> may have started Visual mode, adjust the position for
   2316      // deleted characters.
   2317      if (VIsual_active) {
   2318        check_visual_pos();
   2319      }
   2320    }
   2321  }
   2322  did_ai = false;
   2323  did_si = false;
   2324  can_si = false;
   2325  can_si_back = false;
   2326 
   2327  // Set '[ and '] to the inserted text.  When end_insert_pos is NULL we are
   2328  // now in a different buffer.
   2329  if (end_insert_pos != NULL) {
   2330    curbuf->b_op_start = Insstart;
   2331    curbuf->b_op_start_orig = Insstart_orig;
   2332    curbuf->b_op_end = *end_insert_pos;
   2333  }
   2334 }
   2335 
   2336 // Set the last inserted text to a single character.
   2337 // Used for the replace command.
   2338 void set_last_insert(int c)
   2339 {
   2340  xfree(last_insert.data);
   2341  last_insert.data = xmalloc(MB_MAXBYTES * 3 + 5);
   2342  char *s = last_insert.data;
   2343  // Use the CTRL-V only when entering a special char
   2344  if (c < ' ' || c == DEL) {
   2345    *s++ = Ctrl_V;
   2346  }
   2347  s = add_char2buf(c, s);
   2348  *s++ = ESC;
   2349  *s = NUL;
   2350  last_insert.size = (size_t)(s - last_insert.data);
   2351  last_insert_skip = 0;
   2352 }
   2353 
   2354 #if defined(EXITFREE)
   2355 void free_last_insert(void)
   2356 {
   2357  API_CLEAR_STRING(last_insert);
   2358 }
   2359 #endif
   2360 
   2361 // move cursor to start of line
   2362 // if flags & BL_WHITE  move to first non-white
   2363 // if flags & BL_SOL    move to first non-white if startofline is set,
   2364 //                          otherwise keep "curswant" column
   2365 // if flags & BL_FIX    don't leave the cursor on a NUL.
   2366 void beginline(int flags)
   2367 {
   2368  if ((flags & BL_SOL) && !p_sol) {
   2369    coladvance(curwin, curwin->w_curswant);
   2370  } else {
   2371    curwin->w_cursor.col = 0;
   2372    curwin->w_cursor.coladd = 0;
   2373 
   2374    if (flags & (BL_WHITE | BL_SOL)) {
   2375      for (char *ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr)
   2376           && !((flags & BL_FIX) && ptr[1] == NUL); ptr++) {
   2377        curwin->w_cursor.col++;
   2378      }
   2379    }
   2380    curwin->w_set_curswant = true;
   2381  }
   2382  adjust_skipcol();
   2383 }
   2384 
   2385 // oneright oneleft cursor_down cursor_up
   2386 //
   2387 // Move one char {right,left,down,up}.
   2388 // Doesn't move onto the NUL past the end of the line, unless it is allowed.
   2389 // Return OK when successful, FAIL when we hit a line of file boundary.
   2390 
   2391 int oneright(void)
   2392 {
   2393  char *ptr;
   2394 
   2395  if (virtual_active(curwin)) {
   2396    pos_T prevpos = curwin->w_cursor;
   2397 
   2398    // Adjust for multi-wide char (excluding TAB)
   2399    ptr = get_cursor_pos_ptr();
   2400    coladvance(curwin, getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr)))
   2401                                      ? ptr2cells(ptr) : 1));
   2402    curwin->w_set_curswant = true;
   2403    // Return OK if the cursor moved, FAIL otherwise (at window edge).
   2404    return (prevpos.col != curwin->w_cursor.col
   2405            || prevpos.coladd != curwin->w_cursor.coladd) ? OK : FAIL;
   2406  }
   2407 
   2408  ptr = get_cursor_pos_ptr();
   2409  if (*ptr == NUL) {
   2410    return FAIL;            // already at the very end
   2411  }
   2412 
   2413  int l = utfc_ptr2len(ptr);
   2414 
   2415  // move "l" bytes right, but don't end up on the NUL, unless 'virtualedit'
   2416  // contains "onemore".
   2417  if (ptr[l] == NUL && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
   2418    return FAIL;
   2419  }
   2420  curwin->w_cursor.col += l;
   2421 
   2422  curwin->w_set_curswant = true;
   2423  adjust_skipcol();
   2424  return OK;
   2425 }
   2426 
   2427 int oneleft(void)
   2428 {
   2429  if (virtual_active(curwin)) {
   2430    int v = getviscol();
   2431 
   2432    if (v == 0) {
   2433      return FAIL;
   2434    }
   2435 
   2436    // We might get stuck on 'showbreak', skip over it.
   2437    int width = 1;
   2438    while (true) {
   2439      coladvance(curwin, v - width);
   2440      // getviscol() is slow, skip it when 'showbreak' is empty,
   2441      // 'breakindent' is not set and there are no multi-byte
   2442      // characters
   2443      if (getviscol() < v) {
   2444        break;
   2445      }
   2446      width++;
   2447    }
   2448 
   2449    if (curwin->w_cursor.coladd == 1) {
   2450      // Adjust for multi-wide char (not a TAB)
   2451      char *ptr = get_cursor_pos_ptr();
   2452      if (*ptr != TAB && vim_isprintc(utf_ptr2char(ptr)) && ptr2cells(ptr) > 1) {
   2453        curwin->w_cursor.coladd = 0;
   2454      }
   2455    }
   2456 
   2457    curwin->w_set_curswant = true;
   2458    adjust_skipcol();
   2459    return OK;
   2460  }
   2461 
   2462  if (curwin->w_cursor.col == 0) {
   2463    return FAIL;
   2464  }
   2465 
   2466  curwin->w_set_curswant = true;
   2467  curwin->w_cursor.col--;
   2468 
   2469  // if the character on the left of the current cursor is a multi-byte
   2470  // character, move to its first byte
   2471  mb_adjust_cursor();
   2472  adjust_skipcol();
   2473  return OK;
   2474 }
   2475 
   2476 /// Move the cursor up "n" lines in window "wp". Takes care of closed folds.
   2477 /// Skips over concealed lines when "skip_conceal" is true.
   2478 void cursor_up_inner(win_T *wp, linenr_T n, bool skip_conceal)
   2479 {
   2480  linenr_T lnum = wp->w_cursor.lnum;
   2481 
   2482  if (n >= lnum) {
   2483    lnum = 1;
   2484  } else if (win_lines_concealed(wp)) {
   2485    // Count each sequence of folded lines as one logical line.
   2486 
   2487    // go to the start of the current fold
   2488    hasFolding(wp, lnum, &lnum, NULL);
   2489 
   2490    while (n--) {
   2491      // move up one line
   2492      lnum--;
   2493      if (lnum <= 1) {
   2494        break;
   2495      }
   2496      n += skip_conceal && decor_conceal_line(wp, lnum - 1, true);
   2497      // If we entered a fold, move to the beginning, unless in
   2498      // Insert mode or when 'foldopen' contains "all": it will open
   2499      // in a moment.
   2500      if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & kOptFdoFlagAll))) {
   2501        hasFolding(wp, lnum, &lnum, NULL);
   2502      }
   2503    }
   2504    lnum = MAX(lnum, 1);
   2505  } else {
   2506    lnum -= n;
   2507  }
   2508 
   2509  wp->w_cursor.lnum = lnum;
   2510 }
   2511 
   2512 /// @param upd_topline  When true: update topline
   2513 int cursor_up(linenr_T n, bool upd_topline)
   2514 {
   2515  // This fails if the cursor is already in the first line.
   2516  if (n > 0 && curwin->w_cursor.lnum <= 1) {
   2517    return FAIL;
   2518  }
   2519  cursor_up_inner(curwin, n, false);
   2520 
   2521  // try to advance to the column we want to be at
   2522  coladvance(curwin, curwin->w_curswant);
   2523 
   2524  if (upd_topline) {
   2525    update_topline(curwin);  // make sure curwin->w_topline is valid
   2526  }
   2527 
   2528  return OK;
   2529 }
   2530 
   2531 /// Move the cursor down "n" lines in window "wp". Takes care of closed folds.
   2532 /// Skips over concealed lines when "skip_conceal" is true.
   2533 void cursor_down_inner(win_T *wp, int n, bool skip_conceal)
   2534 {
   2535  linenr_T lnum = wp->w_cursor.lnum;
   2536  linenr_T line_count = wp->w_buffer->b_ml.ml_line_count;
   2537 
   2538  if (lnum + n >= line_count) {
   2539    lnum = line_count;
   2540  } else if (win_lines_concealed(wp)) {
   2541    linenr_T last;
   2542 
   2543    // count each sequence of folded lines as one logical line
   2544    while (n--) {
   2545      if (hasFoldingWin(wp, lnum, NULL, &last, true, NULL)) {
   2546        lnum = last + 1;
   2547      } else {
   2548        lnum++;
   2549      }
   2550      if (lnum >= line_count) {
   2551        break;
   2552      }
   2553      n += skip_conceal && decor_conceal_line(wp, lnum - 1, true);
   2554    }
   2555    lnum = MIN(lnum, line_count);
   2556  } else {
   2557    lnum += (linenr_T)n;
   2558  }
   2559 
   2560  wp->w_cursor.lnum = lnum;
   2561 }
   2562 
   2563 /// @param upd_topline  When true: update topline
   2564 int cursor_down(int n, bool upd_topline)
   2565 {
   2566  linenr_T lnum = curwin->w_cursor.lnum;
   2567  // This fails if the cursor is already in the last (folded) line.
   2568  hasFoldingWin(curwin, lnum, NULL, &lnum, true, NULL);
   2569  if (n > 0 && lnum >= curwin->w_buffer->b_ml.ml_line_count) {
   2570    return FAIL;
   2571  }
   2572  cursor_down_inner(curwin, n, false);
   2573 
   2574  // try to advance to the column we want to be at
   2575  coladvance(curwin, curwin->w_curswant);
   2576 
   2577  if (upd_topline) {
   2578    update_topline(curwin);           // make sure curwin->w_topline is valid
   2579  }
   2580 
   2581  return OK;
   2582 }
   2583 
   2584 /// Stuff the last inserted text in the read buffer.
   2585 /// Last_insert actually is a copy of the redo buffer, so we
   2586 /// first have to remove the command.
   2587 ///
   2588 /// @param c       Command character to be inserted
   2589 /// @param count   Repeat this many times
   2590 /// @param no_esc  Don't add an ESC at the end
   2591 int stuff_inserted(int c, int count, int no_esc)
   2592 {
   2593  char last = NUL;
   2594 
   2595  String insert = get_last_insert();  // text to be inserted
   2596  if (insert.data == NULL) {
   2597    emsg(_(e_noinstext));
   2598    return FAIL;
   2599  }
   2600 
   2601  // may want to stuff the command character, to start Insert mode
   2602  if (c != NUL) {
   2603    stuffcharReadbuff(c);
   2604  }
   2605 
   2606  if (insert.size > 0) {
   2607    // look for the last ESC in 'insert'
   2608    for (char *p = insert.data + insert.size - 1; p >= insert.data; p--) {
   2609      if (*p == ESC) {
   2610        insert.size = (size_t)(p - insert.data);
   2611        break;
   2612      }
   2613    }
   2614  }
   2615 
   2616  if (insert.size > 0) {
   2617    char *p = insert.data + insert.size - 1;
   2618    // when the last char is either "0" or "^" it will be quoted if no ESC
   2619    // comes after it OR if it will inserted more than once and "ptr"
   2620    // starts with ^D.  -- Acevedo
   2621    if ((*p == '0' || *p == '^')
   2622        && (no_esc || (*insert.data == Ctrl_D && count > 1))) {
   2623      last = *p;
   2624      insert.size--;
   2625    }
   2626  }
   2627 
   2628  do {
   2629    stuffReadbuffLen(insert.data, (ptrdiff_t)insert.size);
   2630    // A trailing "0" is inserted as "<C-V>048", "^" as "<C-V>^".
   2631    switch (last) {
   2632    case '0':
   2633      stuffReadbuffLen(S_LEN("\026\060\064\070"));
   2634      break;
   2635    case '^':
   2636      stuffReadbuffLen(S_LEN("\026^"));
   2637      break;
   2638    default:
   2639      break;
   2640    }
   2641  } while (--count > 0);
   2642 
   2643  // may want to stuff a trailing ESC, to get out of Insert mode
   2644  if (!no_esc) {
   2645    stuffcharReadbuff(ESC);
   2646  }
   2647 
   2648  return OK;
   2649 }
   2650 
   2651 String get_last_insert(void)
   2652  FUNC_ATTR_PURE
   2653 {
   2654  return last_insert.data == NULL ? NULL_STRING : (String){
   2655    .data = last_insert.data + last_insert_skip,
   2656    .size = last_insert.size - (size_t)last_insert_skip,
   2657  };
   2658 }
   2659 
   2660 // Get last inserted string, and remove trailing <Esc>.
   2661 // Returns pointer to allocated memory (must be freed) or NULL.
   2662 char *get_last_insert_save(void)
   2663 {
   2664  String insert = get_last_insert();
   2665 
   2666  if (insert.data == NULL) {
   2667    return NULL;
   2668  }
   2669 
   2670  char *s = xmemdupz(insert.data, insert.size);
   2671  if (insert.size > 0 && s[insert.size - 1] == ESC) {  // remain trailing ESC
   2672    s[--insert.size] = NUL;
   2673  }
   2674  return s;
   2675 }
   2676 
   2677 /// Check the word in front of the cursor for an abbreviation.
   2678 /// Called when the non-id character "c" has been entered.
   2679 /// When an abbreviation is recognized it is removed from the text and
   2680 /// the replacement string is inserted in typebuf.tb_buf[], followed by "c".
   2681 ///
   2682 /// @param  c  character
   2683 ///
   2684 /// @return true if the word is a known abbreviation.
   2685 static bool echeck_abbr(int c)
   2686  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   2687 {
   2688  // Don't check for abbreviation in paste mode, when disabled and just
   2689  // after moving around with cursor keys.
   2690  if (p_paste || no_abbr || arrow_used) {
   2691    return false;
   2692  }
   2693 
   2694  return check_abbr(c, get_cursor_line_ptr(), curwin->w_cursor.col,
   2695                    curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0);
   2696 }
   2697 
   2698 // replace-stack functions
   2699 //
   2700 // When replacing characters, the replaced characters are remembered for each
   2701 // new character.  This is used to re-insert the old text when backspacing.
   2702 //
   2703 // There is a NUL headed list of characters for each character that is
   2704 // currently in the file after the insertion point.  When BS is used, one NUL
   2705 // headed list is put back for the deleted character.
   2706 //
   2707 // For a newline, there are two NUL headed lists.  One contains the characters
   2708 // that the NL replaced.  The extra one stores the characters after the cursor
   2709 // that were deleted (always white space).
   2710 
   2711 /// Push character that is replaced onto the replace stack.
   2712 ///
   2713 /// replace_offset is normally 0, in which case replace_push will add a new
   2714 /// character at the end of the stack.  If replace_offset is not 0, that many
   2715 /// characters will be left on the stack above the newly inserted character.
   2716 ///
   2717 /// @param str character that is replaced (NUL is none)
   2718 /// @param len length of character in bytes
   2719 void replace_push(char *str, size_t len)
   2720 {
   2721  // TODO(bfredl): replace_offset is suss af, if we don't need it, this
   2722  // function is just kv_concat() :p
   2723  if (kv_size(replace_stack) < (size_t)replace_offset) {  // nothing to do
   2724    return;
   2725  }
   2726 
   2727  kv_ensure_space(replace_stack, len);
   2728 
   2729  char *p = replace_stack.items + kv_size(replace_stack) - replace_offset;
   2730  if (replace_offset) {
   2731    memmove(p + len, p, (size_t)replace_offset);
   2732  }
   2733  memcpy(p, str, len);
   2734  kv_size(replace_stack) += len;
   2735 }
   2736 
   2737 /// push NUL as separator between entries in the stack
   2738 void replace_push_nul(void)
   2739 {
   2740  replace_push("", 1);
   2741 }
   2742 
   2743 /// Check top of replace stack, pop it if it was NUL
   2744 ///
   2745 /// when a non-NUL byte is found, use mb_replace_pop_ins() to
   2746 /// pop one complete multibyte character.
   2747 ///
   2748 /// @return -1 if stack is empty, last byte of char or NUL otherwise
   2749 static int replace_pop_if_nul(void)
   2750 {
   2751  int ch = (kv_size(replace_stack)) ? (uint8_t)kv_A(replace_stack, kv_size(replace_stack) - 1) : -1;
   2752  if (ch == NUL) {
   2753    kv_size(replace_stack)--;
   2754  }
   2755  return ch;
   2756 }
   2757 
   2758 /// Join the top two items on the replace stack.  This removes to "off"'th NUL
   2759 /// encountered.
   2760 ///
   2761 /// @param off  offset for which NUL to remove
   2762 void replace_join(int off)
   2763 {
   2764  for (ssize_t i = (ssize_t)kv_size(replace_stack); --i >= 0;) {
   2765    if (kv_A(replace_stack, i) == NUL && off-- <= 0) {
   2766      kv_size(replace_stack)--;
   2767      memmove(&kv_A(replace_stack, i), &kv_A(replace_stack, i + 1),
   2768              (kv_size(replace_stack) - (size_t)i));
   2769      return;
   2770    }
   2771  }
   2772 }
   2773 
   2774 /// Pop bytes from the replace stack until a NUL is found, and insert them
   2775 /// before the cursor.  Can only be used in MODE_REPLACE or MODE_VREPLACE state.
   2776 static void replace_pop_ins(void)
   2777 {
   2778  int oldState = State;
   2779 
   2780  State = MODE_NORMAL;                       // don't want MODE_REPLACE here
   2781  while ((replace_pop_if_nul()) > 0) {
   2782    mb_replace_pop_ins();
   2783    dec_cursor();
   2784  }
   2785  State = oldState;
   2786 }
   2787 
   2788 /// Insert multibyte char popped from the replace stack.
   2789 ///
   2790 /// caller must already have checked the top of the stack is not NUL!!
   2791 static void mb_replace_pop_ins(void)
   2792 {
   2793  int len = utf_head_off(&kv_A(replace_stack, 0),
   2794                         &kv_A(replace_stack, kv_size(replace_stack) - 1)) + 1;
   2795  kv_size(replace_stack) -= (size_t)len;
   2796  ins_bytes_len(&kv_A(replace_stack, kv_size(replace_stack)), (size_t)len);
   2797 }
   2798 
   2799 // Handle doing a BS for one character.
   2800 // cc < 0: replace stack empty, just move cursor
   2801 // cc == 0: character was inserted, delete it
   2802 // cc > 0: character was replaced, put cc (first byte of original char) back
   2803 // and check for more characters to be put back
   2804 // When "limit_col" is >= 0, don't delete before this column.  Matters when
   2805 // using composing characters, use del_char_after_col() instead of del_char().
   2806 static void replace_do_bs(int limit_col)
   2807 {
   2808  colnr_T start_vcol;
   2809  const int l_State = State;
   2810 
   2811  int cc = replace_pop_if_nul();
   2812  if (cc > 0) {
   2813    int orig_len = 0;
   2814    int orig_vcols = 0;
   2815    if (l_State & VREPLACE_FLAG) {
   2816      // Get the number of screen cells used by the character we are
   2817      // going to delete.
   2818      getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL);
   2819      orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol);
   2820    }
   2821    del_char_after_col(limit_col);
   2822    if (l_State & VREPLACE_FLAG) {
   2823      orig_len = get_cursor_pos_len();
   2824    }
   2825    replace_pop_ins();
   2826 
   2827    if (l_State & VREPLACE_FLAG) {
   2828      // Get the number of screen cells used by the inserted characters
   2829      char *p = get_cursor_pos_ptr();
   2830      int ins_len = get_cursor_pos_len() - orig_len;
   2831      int vcol = start_vcol;
   2832      for (int i = 0; i < ins_len; i++) {
   2833        vcol += win_chartabsize(curwin, p + i, vcol);
   2834        i += utfc_ptr2len(p) - 1;
   2835      }
   2836      vcol -= start_vcol;
   2837 
   2838      // Delete spaces that were inserted after the cursor to keep the
   2839      // text aligned.
   2840      curwin->w_cursor.col += ins_len;
   2841      while (vcol > orig_vcols && gchar_cursor() == ' ') {
   2842        del_char(false);
   2843        orig_vcols++;
   2844      }
   2845      curwin->w_cursor.col -= ins_len;
   2846    }
   2847 
   2848    // mark the buffer as changed and prepare for displaying
   2849    changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
   2850  } else if (cc == 0) {
   2851    del_char_after_col(limit_col);
   2852  }
   2853 }
   2854 
   2855 static void ins_reg(void)
   2856 {
   2857  bool need_redraw = false;
   2858  int literally = 0;
   2859  int vis_active = VIsual_active;
   2860 
   2861  // If we are going to wait for a character, show a '"'.
   2862  pc_status = PC_STATUS_UNSET;
   2863  if (redrawing() && !char_avail()) {
   2864    // may need to redraw when no more chars available now
   2865    ins_redraw(false);
   2866 
   2867    edit_putchar('"', true);
   2868    add_to_showcmd_c(Ctrl_R);
   2869  }
   2870 
   2871  // Don't map the register name. This also prevents the mode message to be
   2872  // deleted when ESC is hit.
   2873  no_mapping++;
   2874  allow_keys++;
   2875  int regname = plain_vgetc();
   2876  LANGMAP_ADJUST(regname, true);
   2877  if (regname == Ctrl_R || regname == Ctrl_O || regname == Ctrl_P) {
   2878    // Get a third key for literal register insertion
   2879    literally = regname;
   2880    add_to_showcmd_c(literally);
   2881    regname = plain_vgetc();
   2882    LANGMAP_ADJUST(regname, true);
   2883  }
   2884  no_mapping--;
   2885  allow_keys--;
   2886 
   2887  // Don't call u_sync() while typing the expression or giving an error
   2888  // message for it. Only call it explicitly.
   2889  no_u_sync++;
   2890  if (regname == '=') {
   2891    pos_T curpos = curwin->w_cursor;
   2892 
   2893    // Sync undo when evaluating the expression calls setline() or
   2894    // append(), so that it can be undone separately.
   2895    u_sync_once = 2;
   2896 
   2897    regname = get_expr_register();
   2898 
   2899    // Cursor may be moved back a column.
   2900    curwin->w_cursor = curpos;
   2901    check_cursor(curwin);
   2902  }
   2903  if (regname == NUL || !valid_yank_reg(regname, false)) {
   2904    vim_beep(kOptBoFlagRegister);
   2905    need_redraw = true;  // remove the '"'
   2906  } else {
   2907    yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
   2908 
   2909    if (literally == Ctrl_O || literally == Ctrl_P) {
   2910      // Append the command to the redo buffer.
   2911      AppendCharToRedobuff(Ctrl_R);
   2912      AppendCharToRedobuff(literally);
   2913      AppendCharToRedobuff(regname);
   2914 
   2915      do_put(regname, NULL, BACKWARD, 1,
   2916             (literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND);
   2917    } else if (reg->y_size > 1 && is_literal_register(regname)) {
   2918      AppendCharToRedobuff(Ctrl_R);
   2919      AppendCharToRedobuff(regname);
   2920      do_put(regname, NULL, BACKWARD, 1, PUT_CURSEND);
   2921    } else if (insert_reg(regname, NULL, !!literally) == FAIL) {
   2922      vim_beep(kOptBoFlagRegister);
   2923      need_redraw = true;  // remove the '"'
   2924    } else if (stop_insert_mode) {
   2925      // When the '=' register was used and a function was invoked that
   2926      // did ":stopinsert" then stuff_empty() returns false but we won't
   2927      // insert anything, need to remove the '"'
   2928      need_redraw = true;
   2929    }
   2930  }
   2931  no_u_sync--;
   2932  if (u_sync_once == 1) {
   2933    ins_need_undo = true;
   2934  }
   2935  u_sync_once = 0;
   2936 
   2937  // If the inserted register is empty, we need to remove the '"'. Do this before
   2938  // clearing showcmd, which emits an event that can also update the screen.
   2939  if (need_redraw || stuff_empty()) {
   2940    edit_unputchar();
   2941  }
   2942  clear_showcmd();
   2943 
   2944  // Disallow starting Visual mode here, would get a weird mode.
   2945  if (!vis_active && VIsual_active) {
   2946    end_visual_mode();
   2947  }
   2948 }
   2949 
   2950 // CTRL-G commands in Insert mode.
   2951 static void ins_ctrl_g(void)
   2952 {
   2953  // Right after CTRL-X the cursor will be after the ruler.
   2954  setcursor();
   2955 
   2956  // Don't map the second key. This also prevents the mode message to be
   2957  // deleted when ESC is hit.
   2958  no_mapping++;
   2959  allow_keys++;
   2960  int c = plain_vgetc();
   2961  no_mapping--;
   2962  allow_keys--;
   2963  switch (c) {
   2964  // CTRL-G k and CTRL-G <Up>: cursor up to Insstart.col
   2965  case K_UP:
   2966  case Ctrl_K:
   2967  case 'k':
   2968    ins_up(true);
   2969    break;
   2970 
   2971  // CTRL-G j and CTRL-G <Down>: cursor down to Insstart.col
   2972  case K_DOWN:
   2973  case Ctrl_J:
   2974  case 'j':
   2975    ins_down(true);
   2976    break;
   2977 
   2978  // CTRL-G u: start new undoable edit
   2979  case 'u':
   2980    u_sync(true);
   2981    ins_need_undo = true;
   2982 
   2983    // Need to reset Insstart, esp. because a BS that joins
   2984    // a line to the previous one must save for undo.
   2985    update_Insstart_orig = false;
   2986    Insstart = curwin->w_cursor;
   2987    break;
   2988 
   2989  // CTRL-G U: do not break undo with the next char.
   2990  case 'U':
   2991    // Allow one left/right cursor movement with the next char,
   2992    // without breaking undo.
   2993    dont_sync_undo = kNone;
   2994    break;
   2995 
   2996  case ESC:
   2997    // Esc after CTRL-G cancels it.
   2998    break;
   2999 
   3000  // Unknown CTRL-G command, reserved for future expansion.
   3001  default:
   3002    vim_beep(kOptBoFlagCtrlg);
   3003  }
   3004 }
   3005 
   3006 // CTRL-^ in Insert mode.
   3007 static void ins_ctrl_hat(void)
   3008 {
   3009  if (map_to_exists_mode("", MODE_LANGMAP, false)) {
   3010    // ":lmap" mappings exists, Toggle use of ":lmap" mappings.
   3011    if (State & MODE_LANGMAP) {
   3012      curbuf->b_p_iminsert = B_IMODE_NONE;
   3013      State &= ~MODE_LANGMAP;
   3014    } else {
   3015      curbuf->b_p_iminsert = B_IMODE_LMAP;
   3016      State |= MODE_LANGMAP;
   3017    }
   3018  }
   3019  set_iminsert_global(curbuf);
   3020  showmode();
   3021  // Show/unshow value of 'keymap' in status lines.
   3022  status_redraw_curbuf();
   3023 }
   3024 
   3025 /// Handle ESC in insert mode.
   3026 ///
   3027 /// @param[in,out]  count    repeat count of the insert command
   3028 /// @param          cmdchar  command that started the insert
   3029 /// @param          nomove   when true, don't move the cursor
   3030 ///
   3031 /// @return true when leaving insert mode, false when repeating the insert.
   3032 static bool ins_esc(int *count, int cmdchar, bool nomove)
   3033  FUNC_ATTR_NONNULL_ARG(1)
   3034 {
   3035  static bool disabled_redraw = false;
   3036 
   3037  check_spell_redraw();
   3038 
   3039  int temp = curwin->w_cursor.col;
   3040  if (disabled_redraw) {
   3041    RedrawingDisabled--;
   3042    disabled_redraw = false;
   3043  }
   3044  if (!arrow_used) {
   3045    // Don't append the ESC for "r<CR>" and "grx".
   3046    if (cmdchar != 'r' && cmdchar != 'v') {
   3047      AppendToRedobuff(ESC_STR);
   3048    }
   3049 
   3050    // Repeating insert may take a long time.  Check for
   3051    // interrupt now and then.
   3052    if (*count > 0) {
   3053      line_breakcheck();
   3054      if (got_int) {
   3055        *count = 0;
   3056      }
   3057    }
   3058 
   3059    if (--*count > 0) {         // repeat what was typed
   3060      // Vi repeats the insert without replacing characters.
   3061      if (vim_strchr(p_cpo, CPO_REPLCNT) != NULL) {
   3062        State &= ~REPLACE_FLAG;
   3063      }
   3064 
   3065      start_redo_ins();
   3066      if (cmdchar == 'r' || cmdchar == 'v') {
   3067        stuffRedoReadbuff(ESC_STR);  // No ESC in redo buffer
   3068      }
   3069      RedrawingDisabled++;
   3070      disabled_redraw = true;
   3071      // Repeat the insert
   3072      return false;
   3073    }
   3074    stop_insert(&curwin->w_cursor, true, nomove);
   3075    undisplay_dollar();
   3076  }
   3077 
   3078  if (cmdchar != 'r' && cmdchar != 'v') {
   3079    ins_apply_autocmds(EVENT_INSERTLEAVEPRE);
   3080  }
   3081 
   3082  // When an autoindent was removed, curswant stays after the
   3083  // indent
   3084  if (restart_edit == NUL && (colnr_T)temp == curwin->w_cursor.col) {
   3085    curwin->w_set_curswant = true;
   3086  }
   3087 
   3088  // Remember the last Insert position in the '^ mark.
   3089  if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) {
   3090    fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor);
   3091    RESET_FMARK(&curbuf->b_last_insert, curwin->w_cursor, curbuf->b_fnum, view);
   3092  }
   3093 
   3094  // The cursor should end up on the last inserted character.
   3095  // Don't do it for CTRL-O, unless past the end of the line.
   3096  if (!nomove
   3097      && (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0)
   3098      && (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active))
   3099      && !revins_on) {
   3100    if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == kOptVeFlagAll) {
   3101      oneleft();
   3102      if (restart_edit != NUL) {
   3103        curwin->w_cursor.coladd++;
   3104      }
   3105    } else {
   3106      curwin->w_cursor.col--;
   3107      curwin->w_valid &= ~(VALID_WCOL|VALID_VIRTCOL);
   3108      // Correct cursor for multi-byte character.
   3109      mb_adjust_cursor();
   3110    }
   3111  }
   3112 
   3113  State = MODE_NORMAL;
   3114  may_trigger_modechanged();
   3115  // need to position cursor again when on a TAB and
   3116  // when on a char with inline virtual text
   3117  if (gchar_cursor() == TAB || buf_meta_total(curbuf, kMTMetaInline) > 0) {
   3118    curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
   3119  }
   3120 
   3121  setmouse();
   3122  ui_cursor_shape();            // may show different cursor shape
   3123 
   3124  // When recording or for CTRL-O, need to display the new mode.
   3125  // Otherwise remove the mode message.
   3126  if (reg_recording != 0 || restart_edit != NUL) {
   3127    showmode();
   3128  } else if (p_smd && (got_int || !skip_showmode())
   3129             && !(p_ch == 0 && !ui_has(kUIMessages))) {
   3130    unshowmode(false);
   3131  }
   3132  // Exit Insert mode
   3133  return true;
   3134 }
   3135 
   3136 // Toggle language: revins_on.
   3137 // Move to end of reverse inserted text.
   3138 static void ins_ctrl_(void)
   3139 {
   3140  if (revins_on && revins_chars && revins_scol >= 0) {
   3141    while (gchar_cursor() != NUL && revins_chars--) {
   3142      curwin->w_cursor.col++;
   3143    }
   3144  }
   3145  p_ri = !p_ri;
   3146  revins_on = (State == MODE_INSERT && p_ri);
   3147  if (revins_on) {
   3148    revins_scol = curwin->w_cursor.col;
   3149    revins_legal++;
   3150    revins_chars = 0;
   3151    undisplay_dollar();
   3152  } else {
   3153    revins_scol = -1;
   3154  }
   3155  showmode();
   3156 }
   3157 
   3158 /// If 'keymodel' contains "startsel", may start selection.
   3159 ///
   3160 /// @param  c  character to check
   3161 //
   3162 /// @return true when a CTRL-O and other keys stuffed.
   3163 static bool ins_start_select(int c)
   3164  FUNC_ATTR_WARN_UNUSED_RESULT
   3165 {
   3166  if (!km_startsel) {
   3167    return false;
   3168  }
   3169  switch (c) {
   3170  case K_KHOME:
   3171  case K_KEND:
   3172  case K_PAGEUP:
   3173  case K_KPAGEUP:
   3174  case K_PAGEDOWN:
   3175  case K_KPAGEDOWN:
   3176    if (!(mod_mask & MOD_MASK_SHIFT)) {
   3177      break;
   3178    }
   3179    FALLTHROUGH;
   3180  case K_S_LEFT:
   3181  case K_S_RIGHT:
   3182  case K_S_UP:
   3183  case K_S_DOWN:
   3184  case K_S_END:
   3185  case K_S_HOME:
   3186    // Start selection right away, the cursor can move with CTRL-O when
   3187    // beyond the end of the line.
   3188    start_selection();
   3189 
   3190    // Execute the key in (insert) Select mode.
   3191    stuffcharReadbuff(Ctrl_O);
   3192    if (mod_mask) {
   3193      const char buf[] = { (char)K_SPECIAL, (char)KS_MODIFIER,
   3194                           (char)(uint8_t)mod_mask, NUL };
   3195      stuffReadbuffLen(buf, 3);
   3196    }
   3197    stuffcharReadbuff(c);
   3198    return true;
   3199  }
   3200  return false;
   3201 }
   3202 
   3203 // <Insert> key in Insert mode: toggle insert/replace mode.
   3204 static void ins_insert(int replaceState)
   3205 {
   3206  set_vim_var_string(VV_INSERTMODE, ((State & REPLACE_FLAG)
   3207                                     ? "i"
   3208                                     : replaceState == MODE_VREPLACE ? "v" : "r"), 1);
   3209  ins_apply_autocmds(EVENT_INSERTCHANGE);
   3210  if (State & REPLACE_FLAG) {
   3211    State = MODE_INSERT | (State & MODE_LANGMAP);
   3212  } else {
   3213    State = replaceState | (State & MODE_LANGMAP);
   3214  }
   3215  may_trigger_modechanged();
   3216  AppendCharToRedobuff(K_INS);
   3217  showmode();
   3218  ui_cursor_shape();            // may show different cursor shape
   3219 }
   3220 
   3221 // Pressed CTRL-O in Insert mode.
   3222 static void ins_ctrl_o(void)
   3223 {
   3224  restart_VIsual_select = 0;
   3225  if (State & VREPLACE_FLAG) {
   3226    restart_edit = 'V';
   3227  } else if (State & REPLACE_FLAG) {
   3228    restart_edit = 'R';
   3229  } else {
   3230    restart_edit = 'I';
   3231  }
   3232  if (virtual_active(curwin)) {
   3233    ins_at_eol = false;         // cursor always keeps its column
   3234  } else {
   3235    ins_at_eol = (gchar_cursor() == NUL);
   3236  }
   3237 }
   3238 
   3239 // If the cursor is on an indent, ^T/^D insert/delete one
   3240 // shiftwidth.  Otherwise ^T/^D behave like a "<<" or ">>".
   3241 // Always round the indent to 'shiftwidth', this is compatible
   3242 // with vi.  But vi only supports ^T and ^D after an
   3243 // autoindent, we support it everywhere.
   3244 static void ins_shift(int c, int lastc)
   3245 {
   3246  if (stop_arrow() == FAIL) {
   3247    return;
   3248  }
   3249  AppendCharToRedobuff(c);
   3250 
   3251  // 0^D and ^^D: remove all indent.
   3252  if (c == Ctrl_D && (lastc == '0' || lastc == '^')
   3253      && curwin->w_cursor.col > 0) {
   3254    curwin->w_cursor.col--;
   3255    del_char(false);              // delete the '^' or '0'
   3256    // In Replace mode, restore the characters that '^' or '0' replaced.
   3257    if (State & REPLACE_FLAG) {
   3258      replace_pop_ins();
   3259    }
   3260    if (lastc == '^') {
   3261      old_indent = get_indent();        // remember curr. indent
   3262    }
   3263    change_indent(INDENT_SET, 0, true, true);
   3264  } else {
   3265    change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, true, true);
   3266  }
   3267 
   3268  if (did_ai && *skipwhite(get_cursor_line_ptr()) != NUL) {
   3269    did_ai = false;
   3270  }
   3271  did_si = false;
   3272  can_si = false;
   3273  can_si_back = false;
   3274  can_cindent = false;          // no cindenting after ^D or ^T
   3275 }
   3276 
   3277 static void ins_del(void)
   3278 {
   3279  if (stop_arrow() == FAIL) {
   3280    return;
   3281  }
   3282  if (gchar_cursor() == NUL) {          // delete newline
   3283    const int temp = curwin->w_cursor.col;
   3284    if (!can_bs(BS_EOL)  // only if "eol" included
   3285        || do_join(2, false, true, false, false) == FAIL) {
   3286      vim_beep(kOptBoFlagBackspace);
   3287    } else {
   3288      curwin->w_cursor.col = temp;
   3289      // Adjust orig_line_count in case more lines have been deleted than
   3290      // have been added. That makes sure, that open_line() later
   3291      // can access all buffer lines correctly
   3292      if (State & VREPLACE_FLAG
   3293          && orig_line_count > curbuf->b_ml.ml_line_count) {
   3294        orig_line_count = curbuf->b_ml.ml_line_count;
   3295      }
   3296    }
   3297  } else if (del_char(false) == FAIL) {  // delete char under cursor
   3298    vim_beep(kOptBoFlagBackspace);
   3299  }
   3300  did_ai = false;
   3301  did_si = false;
   3302  can_si = false;
   3303  can_si_back = false;
   3304  AppendCharToRedobuff(K_DEL);
   3305 }
   3306 
   3307 /// Handle Backspace, delete-word and delete-line in Insert mode.
   3308 ///
   3309 /// @param          c                 character that was typed
   3310 /// @param          mode              backspace mode to use
   3311 /// @param[in,out]  inserted_space_p  whether a space was the last
   3312 //                                    character inserted
   3313 ///
   3314 /// @return true when backspace was actually used.
   3315 static bool ins_bs(int c, int mode, int *inserted_space_p)
   3316  FUNC_ATTR_NONNULL_ARG(3)
   3317 {
   3318  int cc;
   3319  int temp = 0;                     // init for GCC
   3320  bool did_backspace = false;
   3321  bool call_fix_indent = false;
   3322 
   3323  // can't delete anything in an empty file
   3324  // can't backup past first character in buffer
   3325  // can't backup past starting point unless 'backspace' > 1
   3326  // can backup to a previous line if 'backspace' == 0
   3327  if (buf_is_empty(curbuf)
   3328      || (!revins_on
   3329          && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0)
   3330              || (!can_bs(BS_START)
   3331                  && ((arrow_used && !bt_prompt(curbuf))
   3332                      || (curwin->w_cursor.lnum == Insstart_orig.lnum
   3333                          && curwin->w_cursor.col <= Insstart_orig.col)))
   3334              || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0
   3335                  && curwin->w_cursor.col <= ai_col)
   3336              || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) {
   3337    vim_beep(kOptBoFlagBackspace);
   3338    return false;
   3339  }
   3340 
   3341  if (stop_arrow() == FAIL) {
   3342    return false;
   3343  }
   3344  bool in_indent = inindent(0);
   3345  if (in_indent) {
   3346    can_cindent = false;
   3347  }
   3348  end_comment_pending = NUL;  // After BS, don't auto-end comment
   3349  if (revins_on) {            // put cursor after last inserted char
   3350    inc_cursor();
   3351  }
   3352  // Virtualedit:
   3353  //    BACKSPACE_CHAR eats a virtual space
   3354  //    BACKSPACE_WORD eats all coladd
   3355  //    BACKSPACE_LINE eats all coladd and keeps going
   3356  if (curwin->w_cursor.coladd > 0) {
   3357    if (mode == BACKSPACE_CHAR) {
   3358      curwin->w_cursor.coladd--;
   3359      return true;
   3360    }
   3361    if (mode == BACKSPACE_WORD) {
   3362      curwin->w_cursor.coladd = 0;
   3363      return true;
   3364    }
   3365    curwin->w_cursor.coladd = 0;
   3366  }
   3367 
   3368  // Delete newline!
   3369  if (curwin->w_cursor.col == 0) {
   3370    linenr_T lnum = Insstart.lnum;
   3371    if (curwin->w_cursor.lnum == lnum || revins_on) {
   3372      if (u_save((linenr_T)(curwin->w_cursor.lnum - 2),
   3373                 (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) {
   3374        return false;
   3375      }
   3376      Insstart.lnum--;
   3377      Insstart.col = ml_get_len(Insstart.lnum);
   3378    }
   3379    // In replace mode:
   3380    // cc < 0: NL was inserted, delete it
   3381    // cc >= 0: NL was replaced, put original characters back
   3382    cc = -1;
   3383    if (State & REPLACE_FLAG) {
   3384      cc = replace_pop_if_nul();  // returns -1 if NL was inserted
   3385    }
   3386    // In replace mode, in the line we started replacing, we only move the
   3387    // cursor.
   3388    if ((State & REPLACE_FLAG) && curwin->w_cursor.lnum <= lnum) {
   3389      dec_cursor();
   3390    } else {
   3391      if (!(State & VREPLACE_FLAG)
   3392          || curwin->w_cursor.lnum > orig_line_count) {
   3393        temp = gchar_cursor();          // remember current char
   3394        curwin->w_cursor.lnum--;
   3395 
   3396        // When "aw" is in 'formatoptions' we must delete the space at
   3397        // the end of the line, otherwise the line will be broken
   3398        // again when auto-formatting.
   3399        if (has_format_option(FO_AUTO)
   3400            && has_format_option(FO_WHITE_PAR)) {
   3401          const char *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum);
   3402          int len = get_cursor_line_len();
   3403          if (len > 0 && ptr[len - 1] == ' ') {
   3404            char *newp = xmemdupz(ptr, (size_t)(len - 1));
   3405 
   3406            if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) {
   3407              xfree(curbuf->b_ml.ml_line_ptr);
   3408            }
   3409            curbuf->b_ml.ml_line_ptr = newp;
   3410            curbuf->b_ml.ml_line_textlen--;
   3411            curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
   3412          }
   3413        }
   3414 
   3415        do_join(2, false, false, false, false);
   3416        if (temp == NUL && gchar_cursor() != NUL) {
   3417          inc_cursor();
   3418        }
   3419      } else {
   3420        dec_cursor();
   3421      }
   3422 
   3423      // In MODE_REPLACE mode we have to put back the text that was
   3424      // replaced by the NL. On the replace stack is first a
   3425      // NUL-terminated sequence of characters that were deleted and then
   3426      // the characters that NL replaced.
   3427      if (State & REPLACE_FLAG) {
   3428        // Do the next ins_char() in MODE_NORMAL state, to
   3429        // prevent ins_char() from replacing characters and
   3430        // avoiding showmatch().
   3431        int oldState = State;
   3432        State = MODE_NORMAL;
   3433        // restore characters (blanks) deleted after cursor
   3434        while (cc > 0) {
   3435          colnr_T save_col = curwin->w_cursor.col;
   3436          mb_replace_pop_ins();
   3437          curwin->w_cursor.col = save_col;
   3438          cc = replace_pop_if_nul();
   3439        }
   3440        // restore the characters that NL replaced
   3441        replace_pop_ins();
   3442        State = oldState;
   3443      }
   3444    }
   3445    did_ai = false;
   3446  } else {
   3447    // Delete character(s) before the cursor.
   3448    if (revins_on) {            // put cursor on last inserted char
   3449      dec_cursor();
   3450    }
   3451    colnr_T mincol = 0;
   3452    // keep indent
   3453    if (mode == BACKSPACE_LINE
   3454        && (curbuf->b_p_ai || cindent_on())
   3455        && !revins_on) {
   3456      colnr_T save_col = curwin->w_cursor.col;
   3457      beginline(BL_WHITE);
   3458      if (curwin->w_cursor.col < save_col) {
   3459        mincol = curwin->w_cursor.col;
   3460        // should now fix the indent to match with the previous line
   3461        call_fix_indent = true;
   3462      }
   3463      curwin->w_cursor.col = save_col;
   3464    }
   3465 
   3466    // Handle deleting one 'shiftwidth' or 'softtabstop'.
   3467    if (mode == BACKSPACE_CHAR
   3468        && ((p_sta && in_indent)
   3469            || ((get_sts_value() != 0 || tabstop_count(curbuf->b_p_vsts_array))
   3470                && curwin->w_cursor.col > 0
   3471                && (*(get_cursor_pos_ptr() - 1) == TAB
   3472                    || (*(get_cursor_pos_ptr() - 1) == ' '
   3473                        && (!*inserted_space_p || arrow_used)))))) {
   3474      *inserted_space_p = false;
   3475 
   3476      bool const use_ts = !curwin->w_p_list || curwin->w_p_lcs_chars.tab1;
   3477      char *const line = get_cursor_line_ptr();
   3478      char *const cursor_ptr = line + curwin->w_cursor.col;
   3479 
   3480      colnr_T vcol = 0;
   3481      colnr_T space_vcol = 0;
   3482      StrCharInfo sci = utf_ptr2StrCharInfo(line);
   3483      StrCharInfo space_sci = sci;
   3484      bool prev_space = false;
   3485 
   3486      // Compute virtual column of cursor position, and find the last
   3487      // whitespace before cursor that is preceded by non-whitespace.
   3488      // Use charsize_nowrap() so that virtual text and wrapping are ignored.
   3489      while (sci.ptr < cursor_ptr) {
   3490        bool cur_space = ascii_iswhite(sci.chr.value);
   3491        if (!prev_space && cur_space) {
   3492          space_sci = sci;
   3493          space_vcol = vcol;
   3494        }
   3495        vcol += charsize_nowrap(curbuf, sci.ptr, use_ts, vcol, sci.chr.value);
   3496        sci = utfc_next(sci);
   3497        prev_space = cur_space;
   3498      }
   3499 
   3500      // Compute the virtual column where we want to be.
   3501      colnr_T want_vcol = vcol > 0 ? vcol - 1 : 0;
   3502      if (p_sta && in_indent) {
   3503        want_vcol -= want_vcol % get_sw_value(curbuf);
   3504      } else {
   3505        want_vcol = tabstop_start(want_vcol, get_sts_value(), curbuf->b_p_vsts_array);
   3506      }
   3507 
   3508      // Find the position to stop backspacing.
   3509      // Use charsize_nowrap() so that virtual text and wrapping are ignored.
   3510      while (true) {
   3511        int size = charsize_nowrap(curbuf, space_sci.ptr, use_ts, space_vcol, space_sci.chr.value);
   3512        if (space_vcol + size > want_vcol) {
   3513          break;
   3514        }
   3515        space_vcol += size;
   3516        space_sci = utfc_next(space_sci);
   3517      }
   3518      colnr_T const want_col = (int)(space_sci.ptr - line);
   3519 
   3520      // Delete characters until we are at or before want_col.
   3521      while (curwin->w_cursor.col > want_col) {
   3522        dec_cursor();
   3523        if (State & REPLACE_FLAG) {
   3524          // Don't delete characters before the insert point when in Replace mode.
   3525          if (curwin->w_cursor.lnum != Insstart.lnum
   3526              || curwin->w_cursor.col >= Insstart.col) {
   3527            replace_do_bs(-1);
   3528          }
   3529        } else {
   3530          del_char(false);
   3531        }
   3532      }
   3533 
   3534      // Insert extra spaces until we are at want_vcol.
   3535      for (; space_vcol < want_vcol; space_vcol++) {
   3536        // Remember the first char we inserted.
   3537        if (curwin->w_cursor.lnum == Insstart_orig.lnum
   3538            && curwin->w_cursor.col < Insstart_orig.col) {
   3539          Insstart_orig.col = curwin->w_cursor.col;
   3540        }
   3541 
   3542        if (State & VREPLACE_FLAG) {
   3543          ins_char(' ');
   3544        } else {
   3545          ins_str(S_LEN(" "));
   3546          if ((State & REPLACE_FLAG)) {
   3547            replace_push_nul();
   3548          }
   3549        }
   3550      }
   3551    } else {
   3552      // Delete up to starting point, start of line or previous word.
   3553 
   3554      int cclass = mb_get_class(get_cursor_pos_ptr());
   3555      do {
   3556        if (!revins_on) {   // put cursor on char to be deleted
   3557          dec_cursor();
   3558        }
   3559        cc = gchar_cursor();
   3560        // look multi-byte character class
   3561        int prev_cclass = cclass;
   3562        cclass = mb_get_class(get_cursor_pos_ptr());
   3563        if (mode == BACKSPACE_WORD && !ascii_isspace(cc)) {   // start of word?
   3564          mode = BACKSPACE_WORD_NOT_SPACE;
   3565          temp = vim_iswordc(cc);
   3566        } else if (mode == BACKSPACE_WORD_NOT_SPACE
   3567                   && ((ascii_isspace(cc) || vim_iswordc(cc) != temp)
   3568                       || prev_cclass != cclass)) {   // end of word?
   3569          if (!revins_on) {
   3570            inc_cursor();
   3571          } else if (State & REPLACE_FLAG) {
   3572            dec_cursor();
   3573          }
   3574          break;
   3575        }
   3576        if (State & REPLACE_FLAG) {
   3577          replace_do_bs(-1);
   3578        } else {
   3579          bool has_composing = false;
   3580          if (p_deco) {
   3581            char *p0 = get_cursor_pos_ptr();
   3582            has_composing = utf_composinglike(p0, p0 + utf_ptr2len(p0), NULL);
   3583          }
   3584          del_char(false);
   3585          // If there are combining characters and 'delcombine' is set
   3586          // move the cursor back.  Don't back up before the base character.
   3587          if (has_composing) {
   3588            inc_cursor();
   3589          }
   3590          if (revins_chars) {
   3591            revins_chars--;
   3592            revins_legal++;
   3593          }
   3594          if (revins_on && gchar_cursor() == NUL) {
   3595            break;
   3596          }
   3597        }
   3598        // Just a single backspace?:
   3599        if (mode == BACKSPACE_CHAR) {
   3600          break;
   3601        }
   3602      } while (revins_on
   3603               || (curwin->w_cursor.col > mincol
   3604                   && (can_bs(BS_NOSTOP)
   3605                       || (curwin->w_cursor.lnum != Insstart_orig.lnum
   3606                           || curwin->w_cursor.col != Insstart_orig.col))));
   3607    }
   3608    did_backspace = true;
   3609  }
   3610  did_si = false;
   3611  can_si = false;
   3612  can_si_back = false;
   3613  if (curwin->w_cursor.col <= 1) {
   3614    did_ai = false;
   3615  }
   3616 
   3617  if (call_fix_indent) {
   3618    fix_indent();
   3619  }
   3620 
   3621  // It's a little strange to put backspaces into the redo
   3622  // buffer, but it makes auto-indent a lot easier to deal
   3623  // with.
   3624  AppendCharToRedobuff(c);
   3625 
   3626  // If deleted before the insertion point, adjust it
   3627  if (curwin->w_cursor.lnum == Insstart_orig.lnum
   3628      && curwin->w_cursor.col < Insstart_orig.col) {
   3629    Insstart_orig.col = curwin->w_cursor.col;
   3630  }
   3631 
   3632  // vi behaviour: the cursor moves backward but the character that
   3633  //               was there remains visible
   3634  // Vim behaviour: the cursor moves backward and the character that
   3635  //                was there is erased from the screen.
   3636  // We can emulate the vi behaviour by pretending there is a dollar
   3637  // displayed even when there isn't.
   3638  //  --pkv Sun Jan 19 01:56:40 EST 2003
   3639  if (vim_strchr(p_cpo, CPO_BACKSPACE) != NULL && dollar_vcol == -1) {
   3640    dollar_vcol = curwin->w_virtcol;
   3641  }
   3642 
   3643  // When deleting a char the cursor line must never be in a closed fold.
   3644  // E.g., when 'foldmethod' is indent and deleting the first non-white
   3645  // char before a Tab.
   3646  if (did_backspace) {
   3647    foldOpenCursor();
   3648  }
   3649  return did_backspace;
   3650 }
   3651 
   3652 static void ins_left(void)
   3653 {
   3654  const bool end_change = dont_sync_undo == kFalse;  // end undoable change
   3655 
   3656  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
   3657    foldOpenCursor();
   3658  }
   3659  undisplay_dollar();
   3660  pos_T tpos = curwin->w_cursor;
   3661  if (oneleft() == OK) {
   3662    start_arrow_with_change(&tpos, end_change);
   3663    if (!end_change) {
   3664      AppendCharToRedobuff(K_LEFT);
   3665    }
   3666    // If exit reversed string, position is fixed
   3667    if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol) {
   3668      revins_legal++;
   3669    }
   3670    revins_chars++;
   3671  } else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
   3672    // if 'whichwrap' set for cursor in insert mode may go to previous line.
   3673    // always break undo when moving upwards/downwards, else undo may break
   3674    start_arrow(&tpos);
   3675    curwin->w_cursor.lnum--;
   3676    coladvance(curwin, MAXCOL);
   3677    curwin->w_set_curswant = true;  // so we stay at the end
   3678  } else {
   3679    vim_beep(kOptBoFlagCursor);
   3680  }
   3681  dont_sync_undo = kFalse;
   3682 }
   3683 
   3684 static void ins_home(int c)
   3685 {
   3686  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
   3687    foldOpenCursor();
   3688  }
   3689  undisplay_dollar();
   3690  pos_T tpos = curwin->w_cursor;
   3691  if (c == K_C_HOME) {
   3692    curwin->w_cursor.lnum = 1;
   3693  }
   3694  curwin->w_cursor.col = 0;
   3695  curwin->w_cursor.coladd = 0;
   3696  curwin->w_curswant = 0;
   3697  start_arrow(&tpos);
   3698 }
   3699 
   3700 static void ins_end(int c)
   3701 {
   3702  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
   3703    foldOpenCursor();
   3704  }
   3705  undisplay_dollar();
   3706  pos_T tpos = curwin->w_cursor;
   3707  if (c == K_C_END) {
   3708    curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
   3709  }
   3710  coladvance(curwin, MAXCOL);
   3711  curwin->w_curswant = MAXCOL;
   3712 
   3713  start_arrow(&tpos);
   3714 }
   3715 
   3716 static void ins_s_left(void)
   3717 {
   3718  const bool end_change = dont_sync_undo == kFalse;  // end undoable change
   3719  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
   3720    foldOpenCursor();
   3721  }
   3722  undisplay_dollar();
   3723  if (curwin->w_cursor.lnum > 1 || curwin->w_cursor.col > 0) {
   3724    start_arrow_with_change(&curwin->w_cursor, end_change);
   3725    if (!end_change) {
   3726      AppendCharToRedobuff(K_S_LEFT);
   3727    }
   3728    bck_word(1, false, false);
   3729    curwin->w_set_curswant = true;
   3730  } else {
   3731    vim_beep(kOptBoFlagCursor);
   3732  }
   3733  dont_sync_undo = kFalse;
   3734 }
   3735 
   3736 /// @param end_change      end undoable change
   3737 static void ins_right(void)
   3738 {
   3739  const bool end_change = dont_sync_undo == kFalse;  // end undoable change
   3740  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
   3741    foldOpenCursor();
   3742  }
   3743  undisplay_dollar();
   3744  if (gchar_cursor() != NUL || virtual_active(curwin)) {
   3745    start_arrow_with_change(&curwin->w_cursor, end_change);
   3746    if (!end_change) {
   3747      AppendCharToRedobuff(K_RIGHT);
   3748    }
   3749    curwin->w_set_curswant = true;
   3750    if (virtual_active(curwin)) {
   3751      oneright();
   3752    } else {
   3753      curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
   3754    }
   3755 
   3756    revins_legal++;
   3757    if (revins_chars) {
   3758      revins_chars--;
   3759    }
   3760  } else if (vim_strchr(p_ww, ']') != NULL
   3761             && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
   3762    // if 'whichwrap' set for cursor in insert mode, may move the
   3763    // cursor to the next line
   3764    start_arrow(&curwin->w_cursor);
   3765    curwin->w_set_curswant = true;
   3766    curwin->w_cursor.lnum++;
   3767    curwin->w_cursor.col = 0;
   3768  } else {
   3769    vim_beep(kOptBoFlagCursor);
   3770  }
   3771  dont_sync_undo = kFalse;
   3772 }
   3773 
   3774 static void ins_s_right(void)
   3775 {
   3776  const bool end_change = dont_sync_undo == kFalse;  // end undoable change
   3777  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
   3778    foldOpenCursor();
   3779  }
   3780  undisplay_dollar();
   3781  if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count
   3782      || gchar_cursor() != NUL) {
   3783    start_arrow_with_change(&curwin->w_cursor, end_change);
   3784    if (!end_change) {
   3785      AppendCharToRedobuff(K_S_RIGHT);
   3786    }
   3787    fwd_word(1, false, 0);
   3788    curwin->w_set_curswant = true;
   3789  } else {
   3790    vim_beep(kOptBoFlagCursor);
   3791  }
   3792  dont_sync_undo = kFalse;
   3793 }
   3794 
   3795 /// @param startcol  when true move to Insstart.col
   3796 static void ins_up(bool startcol)
   3797 {
   3798  linenr_T old_topline = curwin->w_topline;
   3799  int old_topfill = curwin->w_topfill;
   3800 
   3801  undisplay_dollar();
   3802  pos_T tpos = curwin->w_cursor;
   3803  if (cursor_up(1, true) == OK) {
   3804    if (startcol) {
   3805      coladvance(curwin, getvcol_nolist(&Insstart));
   3806    }
   3807    if (old_topline != curwin->w_topline
   3808        || old_topfill != curwin->w_topfill) {
   3809      redraw_later(curwin, UPD_VALID);
   3810    }
   3811    start_arrow(&tpos);
   3812    can_cindent = true;
   3813  } else {
   3814    vim_beep(kOptBoFlagCursor);
   3815  }
   3816 }
   3817 
   3818 static void ins_pageup(void)
   3819 {
   3820  undisplay_dollar();
   3821 
   3822  if (mod_mask & MOD_MASK_CTRL) {
   3823    // <C-PageUp>: tab page back
   3824    if (first_tabpage->tp_next != NULL) {
   3825      start_arrow(&curwin->w_cursor);
   3826      goto_tabpage(-1);
   3827    }
   3828    return;
   3829  }
   3830 
   3831  pos_T tpos = curwin->w_cursor;
   3832  if (pagescroll(BACKWARD, 1, false) == OK) {
   3833    start_arrow(&tpos);
   3834    can_cindent = true;
   3835  } else {
   3836    vim_beep(kOptBoFlagCursor);
   3837  }
   3838 }
   3839 
   3840 /// @param startcol  when true move to Insstart.col
   3841 static void ins_down(bool startcol)
   3842 {
   3843  linenr_T old_topline = curwin->w_topline;
   3844  int old_topfill = curwin->w_topfill;
   3845 
   3846  undisplay_dollar();
   3847  pos_T tpos = curwin->w_cursor;
   3848  if (cursor_down(1, true) == OK) {
   3849    if (startcol) {
   3850      coladvance(curwin, getvcol_nolist(&Insstart));
   3851    }
   3852    if (old_topline != curwin->w_topline
   3853        || old_topfill != curwin->w_topfill) {
   3854      redraw_later(curwin, UPD_VALID);
   3855    }
   3856    start_arrow(&tpos);
   3857    can_cindent = true;
   3858  } else {
   3859    vim_beep(kOptBoFlagCursor);
   3860  }
   3861 }
   3862 
   3863 static void ins_pagedown(void)
   3864 {
   3865  undisplay_dollar();
   3866 
   3867  if (mod_mask & MOD_MASK_CTRL) {
   3868    // <C-PageDown>: tab page forward
   3869    if (first_tabpage->tp_next != NULL) {
   3870      start_arrow(&curwin->w_cursor);
   3871      goto_tabpage(0);
   3872    }
   3873    return;
   3874  }
   3875 
   3876  pos_T tpos = curwin->w_cursor;
   3877  if (pagescroll(FORWARD, 1, false) == OK) {
   3878    start_arrow(&tpos);
   3879    can_cindent = true;
   3880  } else {
   3881    vim_beep(kOptBoFlagCursor);
   3882  }
   3883 }
   3884 
   3885 /// Handle TAB in Insert or Replace mode.
   3886 ///
   3887 /// @return true when the TAB needs to be inserted like a normal character.
   3888 static bool ins_tab(void)
   3889  FUNC_ATTR_WARN_UNUSED_RESULT
   3890 {
   3891  int temp;
   3892 
   3893  if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) {
   3894    Insstart_blank_vcol = get_nolist_virtcol();
   3895  }
   3896  if (echeck_abbr(TAB + ABBR_OFF)) {
   3897    return false;
   3898  }
   3899 
   3900  bool ind = inindent(0);
   3901  if (ind) {
   3902    can_cindent = false;
   3903  }
   3904 
   3905  // When nothing special, insert TAB like a normal character.
   3906  if (!curbuf->b_p_et
   3907      && !(
   3908           p_sta
   3909           && ind
   3910           // These five lines mean 'tabstop' != 'shiftwidth'
   3911           && ((tabstop_count(curbuf->b_p_vts_array) > 1)
   3912               || (tabstop_count(curbuf->b_p_vts_array) == 1
   3913                   && tabstop_first(curbuf->b_p_vts_array)
   3914                   != get_sw_value(curbuf))
   3915               || (tabstop_count(curbuf->b_p_vts_array) == 0
   3916                   && curbuf->b_p_ts != get_sw_value(curbuf))))
   3917      && tabstop_count(curbuf->b_p_vsts_array) == 0 && get_sts_value() == 0) {
   3918    return true;
   3919  }
   3920 
   3921  if (stop_arrow() == FAIL) {
   3922    return true;
   3923  }
   3924 
   3925  did_ai = false;
   3926  did_si = false;
   3927  can_si = false;
   3928  can_si_back = false;
   3929  AppendToRedobuff("\t");
   3930 
   3931  if (p_sta && ind) {  // insert tab in indent, use 'shiftwidth'
   3932    temp = get_sw_value(curbuf);
   3933    temp -= get_nolist_virtcol() % temp;
   3934  } else if (tabstop_count(curbuf->b_p_vsts_array) > 0
   3935             || curbuf->b_p_sts != 0) {
   3936    // use 'softtabstop' when set
   3937    temp = tabstop_padding(get_nolist_virtcol(),
   3938                           get_sts_value(),
   3939                           curbuf->b_p_vsts_array);
   3940  } else {
   3941    // otherwise use 'tabstop'
   3942    temp = tabstop_padding(get_nolist_virtcol(),
   3943                           curbuf->b_p_ts,
   3944                           curbuf->b_p_vts_array);
   3945  }
   3946 
   3947  // Insert the first space with ins_char().    It will delete one char in
   3948  // replace mode.  Insert the rest with ins_str(); it will not delete any
   3949  // chars.  For MODE_VREPLACE state, we use ins_char() for all characters.
   3950  ins_char(' ');
   3951  while (--temp > 0) {
   3952    if (State & VREPLACE_FLAG) {
   3953      ins_char(' ');
   3954    } else {
   3955      ins_str(S_LEN(" "));
   3956      if (State & REPLACE_FLAG) {            // no char replaced
   3957        replace_push_nul();
   3958      }
   3959    }
   3960  }
   3961 
   3962  // When 'expandtab' not set: Replace spaces by TABs where possible.
   3963  if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0
   3964                          || get_sts_value() > 0
   3965                          || (p_sta && ind))) {
   3966    char *ptr;
   3967    char *saved_line = NULL;         // init for GCC
   3968    pos_T pos;
   3969    pos_T *cursor;
   3970    colnr_T want_vcol, vcol;
   3971    int change_col = -1;
   3972    int save_list = curwin->w_p_list;
   3973 
   3974    // Get the current line.  For MODE_VREPLACE state, don't make real
   3975    // changes yet, just work on a copy of the line.
   3976    if (State & VREPLACE_FLAG) {
   3977      pos = curwin->w_cursor;
   3978      cursor = &pos;
   3979      saved_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
   3980      ptr = saved_line + pos.col;
   3981    } else {
   3982      ptr = get_cursor_pos_ptr();
   3983      cursor = &curwin->w_cursor;
   3984    }
   3985 
   3986    // When 'L' is not in 'cpoptions' a tab always takes up 'ts' spaces.
   3987    if (vim_strchr(p_cpo, CPO_LISTWM) == NULL) {
   3988      curwin->w_p_list = false;
   3989    }
   3990 
   3991    // Find first white before the cursor
   3992    pos_T fpos = curwin->w_cursor;
   3993    while (fpos.col > 0 && ascii_iswhite(ptr[-1])) {
   3994      fpos.col--;
   3995      ptr--;
   3996    }
   3997 
   3998    // In Replace mode, don't change characters before the insert point.
   3999    if ((State & REPLACE_FLAG)
   4000        && fpos.lnum == Insstart.lnum
   4001        && fpos.col < Insstart.col) {
   4002      ptr += Insstart.col - fpos.col;
   4003      fpos.col = Insstart.col;
   4004    }
   4005 
   4006    // compute virtual column numbers of first white and cursor
   4007    getvcol(curwin, &fpos, &vcol, NULL, NULL);
   4008    getvcol(curwin, cursor, &want_vcol, NULL, NULL);
   4009 
   4010    char *tab = "\t";
   4011    int32_t tab_v = (uint8_t)(*tab);
   4012 
   4013    CharsizeArg csarg;
   4014    CSType cstype = init_charsize_arg(&csarg, curwin, 0, tab);
   4015 
   4016    // Use as many TABs as possible.  Beware of 'breakindent', 'showbreak'
   4017    // and 'linebreak' adding extra virtual columns.
   4018    while (ascii_iswhite(*ptr)) {
   4019      int i = win_charsize(cstype, vcol, tab, tab_v, &csarg).width;
   4020      if (vcol + i > want_vcol) {
   4021        break;
   4022      }
   4023      if (*ptr != TAB) {
   4024        *ptr = TAB;
   4025        if (change_col < 0) {
   4026          change_col = fpos.col;            // Column of first change
   4027          // May have to adjust Insstart
   4028          if (fpos.lnum == Insstart.lnum && fpos.col < Insstart.col) {
   4029            Insstart.col = fpos.col;
   4030          }
   4031        }
   4032      }
   4033      fpos.col++;
   4034      ptr++;
   4035      vcol += i;
   4036    }
   4037 
   4038    if (change_col >= 0) {
   4039      int repl_off = 0;
   4040      // Skip over the spaces we need.
   4041      cstype = init_charsize_arg(&csarg, curwin, 0, ptr);
   4042      while (vcol < want_vcol && *ptr == ' ') {
   4043        vcol += win_charsize(cstype, vcol, ptr, (uint8_t)(' '), &csarg).width;
   4044        ptr++;
   4045        repl_off++;
   4046      }
   4047 
   4048      if (vcol > want_vcol) {
   4049        // Must have a char with 'showbreak' just before it.
   4050        ptr--;
   4051        repl_off--;
   4052      }
   4053      fpos.col += repl_off;
   4054 
   4055      // Delete following spaces.
   4056      int i = cursor->col - fpos.col;
   4057      if (i > 0) {
   4058        if (!(State & VREPLACE_FLAG)) {
   4059          const colnr_T newp_len = curbuf->b_ml.ml_line_textlen - i;
   4060          char *newp = xmalloc((size_t)newp_len);
   4061          ptrdiff_t col = ptr - curbuf->b_ml.ml_line_ptr;
   4062          if (col > 0) {
   4063            memmove(newp, ptr - col, (size_t)col);
   4064          }
   4065          memmove(newp + col, ptr + i, (size_t)(newp_len - col));
   4066          if (curbuf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) {
   4067            xfree(curbuf->b_ml.ml_line_ptr);
   4068          }
   4069          curbuf->b_ml.ml_line_ptr = newp;
   4070          curbuf->b_ml.ml_line_textlen = newp_len;
   4071          curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
   4072          inserted_bytes(fpos.lnum, change_col,
   4073                         cursor->col - change_col, fpos.col - change_col);
   4074        } else {
   4075          STRMOVE(ptr, ptr + i);
   4076        }
   4077        // correct replace stack.
   4078        if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) {
   4079          for (temp = i; --temp >= 0;) {
   4080            replace_join(repl_off);
   4081          }
   4082        }
   4083      }
   4084      cursor->col -= i;
   4085 
   4086      // In MODE_VREPLACE state, we haven't changed anything yet.  Do it
   4087      // now by backspacing over the changed spacing and then inserting
   4088      // the new spacing.
   4089      if (State & VREPLACE_FLAG) {
   4090        // Backspace from real cursor to change_col
   4091        backspace_until_column(change_col);
   4092 
   4093        // Insert each char in saved_line from changed_col to
   4094        // ptr-cursor
   4095        ins_bytes_len(saved_line + change_col, (size_t)(cursor->col - change_col));
   4096      }
   4097    }
   4098 
   4099    if (State & VREPLACE_FLAG) {
   4100      xfree(saved_line);
   4101    }
   4102    curwin->w_p_list = save_list;
   4103  }
   4104 
   4105  return false;
   4106 }
   4107 
   4108 /// Handle CR or NL in insert mode.
   4109 ///
   4110 /// @return false when it can't undo.
   4111 bool ins_eol(int c)
   4112 {
   4113  if (echeck_abbr(c + ABBR_OFF)) {
   4114    return true;
   4115  }
   4116  if (stop_arrow() == FAIL) {
   4117    return false;
   4118  }
   4119  undisplay_dollar();
   4120 
   4121  // Strange Vi behaviour: In Replace mode, typing a NL will not delete the
   4122  // character under the cursor.  Only push a NUL on the replace stack,
   4123  // nothing to put back when the NL is deleted.
   4124  if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) {
   4125    replace_push_nul();
   4126  }
   4127 
   4128  // In MODE_VREPLACE state, a NL replaces the rest of the line, and starts
   4129  // replacing the next line, so we push all of the characters left on the
   4130  // line onto the replace stack.  This is not done here though, it is done
   4131  // in open_line().
   4132 
   4133  // Put cursor on NUL if on the last char and coladd is 1 (happens after
   4134  // CTRL-O).
   4135  if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) {
   4136    coladvance(curwin, getviscol());
   4137  }
   4138 
   4139  // NL in reverse insert will always start in the end of current line.
   4140  if (revins_on) {
   4141    curwin->w_cursor.col += get_cursor_pos_len();
   4142  }
   4143 
   4144  AppendToRedobuff(NL_STR);
   4145  bool i = open_line(FORWARD,
   4146                     has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0,
   4147                     old_indent, NULL);
   4148  old_indent = 0;
   4149  can_cindent = true;
   4150  // When inserting a line the cursor line must never be in a closed fold.
   4151  foldOpenCursor();
   4152 
   4153  return i;
   4154 }
   4155 
   4156 // Handle digraph in insert mode.
   4157 // Returns character still to be inserted, or NUL when nothing remaining to be
   4158 // done.
   4159 static int ins_digraph(void)
   4160 {
   4161  bool did_putchar = false;
   4162 
   4163  pc_status = PC_STATUS_UNSET;
   4164  if (redrawing() && !char_avail()) {
   4165    // may need to redraw when no more chars available now
   4166    ins_redraw(false);
   4167 
   4168    edit_putchar('?', true);
   4169    did_putchar = true;
   4170    add_to_showcmd_c(Ctrl_K);
   4171  }
   4172 
   4173  // don't map the digraph chars. This also prevents the
   4174  // mode message to be deleted when ESC is hit
   4175  no_mapping++;
   4176  allow_keys++;
   4177  int c = plain_vgetc();
   4178  no_mapping--;
   4179  allow_keys--;
   4180  if (did_putchar) {
   4181    // when the line fits in 'columns' the '?' is at the start of the next
   4182    // line and will not be removed by the redraw
   4183    edit_unputchar();
   4184  }
   4185 
   4186  if (IS_SPECIAL(c) || mod_mask) {          // special key
   4187    clear_showcmd();
   4188    insert_special(c, true, false);
   4189    return NUL;
   4190  }
   4191  if (c != ESC) {
   4192    did_putchar = false;
   4193    if (redrawing() && !char_avail()) {
   4194      // may need to redraw when no more chars available now
   4195      ins_redraw(false);
   4196 
   4197      if (char2cells(c) == 1) {
   4198        ins_redraw(false);
   4199        edit_putchar(c, true);
   4200        did_putchar = true;
   4201      }
   4202      add_to_showcmd_c(c);
   4203    }
   4204    no_mapping++;
   4205    allow_keys++;
   4206    int cc = plain_vgetc();
   4207    no_mapping--;
   4208    allow_keys--;
   4209    if (did_putchar) {
   4210      // when the line fits in 'columns' the '?' is at the start of the
   4211      // next line and will not be removed by a redraw
   4212      edit_unputchar();
   4213    }
   4214    if (cc != ESC) {
   4215      AppendToRedobuff(CTRL_V_STR);
   4216      c = digraph_get(c, cc, true);
   4217      clear_showcmd();
   4218      return c;
   4219    }
   4220  }
   4221  clear_showcmd();
   4222  return NUL;
   4223 }
   4224 
   4225 // Handle CTRL-E and CTRL-Y in Insert mode: copy char from other line.
   4226 // Returns the char to be inserted, or NUL if none found.
   4227 int ins_copychar(linenr_T lnum)
   4228 {
   4229  if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
   4230    vim_beep(kOptBoFlagCopy);
   4231    return NUL;
   4232  }
   4233 
   4234  // try to advance to the cursor column
   4235  validate_virtcol(curwin);
   4236  int const end_vcol = curwin->w_virtcol;
   4237  char *line = ml_get(lnum);
   4238 
   4239  CharsizeArg csarg;
   4240  CSType cstype = init_charsize_arg(&csarg, curwin, lnum, line);
   4241  StrCharInfo ci = utf_ptr2StrCharInfo(line);
   4242  int vcol = 0;
   4243  while (vcol < end_vcol && *ci.ptr != NUL) {
   4244    vcol += win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width;
   4245    if (vcol > end_vcol) {
   4246      break;
   4247    }
   4248    ci = utfc_next(ci);
   4249  }
   4250 
   4251  int c = ci.chr.value < 0 ? (uint8_t)(*ci.ptr) : ci.chr.value;
   4252  if (c == NUL) {
   4253    vim_beep(kOptBoFlagCopy);
   4254  }
   4255  return c;
   4256 }
   4257 
   4258 // CTRL-Y or CTRL-E typed in Insert mode.
   4259 static int ins_ctrl_ey(int tc)
   4260 {
   4261  int c = tc;
   4262 
   4263  if (ctrl_x_mode_scroll()) {
   4264    if (c == Ctrl_Y) {
   4265      scrolldown_clamp();
   4266    } else {
   4267      scrollup_clamp();
   4268    }
   4269    redraw_later(curwin, UPD_VALID);
   4270  } else {
   4271    c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1));
   4272    if (c != NUL) {
   4273      // The character must be taken literally, insert like it
   4274      // was typed after a CTRL-V, and pretend 'textwidth'
   4275      // wasn't set.  Digits, 'o' and 'x' are special after a
   4276      // CTRL-V, don't use it for these.
   4277      if (c < 256 && !isalnum(c)) {
   4278        AppendToRedobuff(CTRL_V_STR);
   4279      }
   4280      OptInt tw_save = curbuf->b_p_tw;
   4281      curbuf->b_p_tw = -1;
   4282      insert_special(c, true, false);
   4283      curbuf->b_p_tw = tw_save;
   4284      revins_chars++;
   4285      revins_legal++;
   4286      c = Ctrl_V;       // pretend CTRL-V is last character
   4287      auto_format(false, true);
   4288    }
   4289  }
   4290  return c;
   4291 }
   4292 
   4293 // Get the value that w_virtcol would have when 'list' is off.
   4294 // Unless 'cpo' contains the 'L' flag.
   4295 colnr_T get_nolist_virtcol(void)
   4296 {
   4297  // check validity of cursor in current buffer
   4298  if (curwin->w_buffer == NULL || curwin->w_buffer->b_ml.ml_mfp == NULL
   4299      || curwin->w_cursor.lnum > curwin->w_buffer->b_ml.ml_line_count) {
   4300    return 0;
   4301  }
   4302  if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) {
   4303    return getvcol_nolist(&curwin->w_cursor);
   4304  }
   4305  validate_virtcol(curwin);
   4306  return curwin->w_virtcol;
   4307 }
   4308 
   4309 // Handle the InsertCharPre autocommand.
   4310 // "c" is the character that was typed.
   4311 // Return a pointer to allocated memory with the replacement string.
   4312 // Return NULL to continue inserting "c".
   4313 static char *do_insert_char_pre(int c)
   4314 {
   4315  char buf[MB_MAXBYTES + 1];
   4316  const int save_State = State;
   4317 
   4318  if (c == Ctrl_RSB) {
   4319    return NULL;
   4320  }
   4321 
   4322  // Return quickly when there is nothing to do.
   4323  if (!has_event(EVENT_INSERTCHARPRE)) {
   4324    return NULL;
   4325  }
   4326  size_t buflen = (size_t)utf_char2bytes(c, buf);
   4327  buf[buflen] = NUL;
   4328 
   4329  // Lock the text to avoid weird things from happening.
   4330  textlock++;
   4331  set_vim_var_string(VV_CHAR, buf, (ptrdiff_t)buflen);  // set v:char
   4332 
   4333  char *res = NULL;
   4334  if (ins_apply_autocmds(EVENT_INSERTCHARPRE)) {
   4335    // Get the value of v:char.  It may be empty or more than one
   4336    // character.  Only use it when changed, otherwise continue with the
   4337    // original character to avoid breaking autoindent.
   4338    if (strcmp(buf, get_vim_var_str(VV_CHAR)) != 0) {
   4339      res = xstrdup(get_vim_var_str(VV_CHAR));
   4340    }
   4341  }
   4342 
   4343  set_vim_var_string(VV_CHAR, NULL, -1);
   4344  textlock--;
   4345 
   4346  // Restore the State, it may have been changed.
   4347  State = save_State;
   4348 
   4349  return res;
   4350 }
   4351 
   4352 bool get_can_cindent(void)
   4353 {
   4354  return can_cindent;
   4355 }
   4356 
   4357 void set_can_cindent(bool val)
   4358 {
   4359  can_cindent = val;
   4360 }
   4361 
   4362 /// Trigger "event" and take care of fixing undo.
   4363 int ins_apply_autocmds(event_T event)
   4364 {
   4365  varnumber_T tick = buf_get_changedtick(curbuf);
   4366 
   4367  int r = apply_autocmds(event, NULL, NULL, false, curbuf);
   4368 
   4369  // If u_savesub() was called then we are not prepared to start
   4370  // a new line.  Call u_save() with no contents to fix that.
   4371  // Except when leaving Insert mode.
   4372  if (event != EVENT_INSERTLEAVE && tick != buf_get_changedtick(curbuf)) {
   4373    u_save(curwin->w_cursor.lnum, (linenr_T)(curwin->w_cursor.lnum + 1));
   4374  }
   4375 
   4376  return r;
   4377 }