neovim

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

normal.c (201292B)


      1 //
      2 // normal.c:    Contains the main routine for processing characters in command
      3 //              mode.  Communicates closely with the code in ops.c to handle
      4 //              the operators.
      5 //
      6 
      7 #include <assert.h>
      8 #include <ctype.h>
      9 #include <inttypes.h>
     10 #include <limits.h>
     11 #include <stdbool.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <time.h>
     16 
     17 #include "nvim/api/private/helpers.h"
     18 #include "nvim/ascii_defs.h"
     19 #include "nvim/autocmd.h"
     20 #include "nvim/autocmd_defs.h"
     21 #include "nvim/buffer.h"
     22 #include "nvim/buffer_defs.h"
     23 #include "nvim/change.h"
     24 #include "nvim/charset.h"
     25 #include "nvim/cmdhist.h"
     26 #include "nvim/cursor.h"
     27 #include "nvim/decoration.h"
     28 #include "nvim/diff.h"
     29 #include "nvim/digraph.h"
     30 #include "nvim/drawscreen.h"
     31 #include "nvim/edit.h"
     32 #include "nvim/errors.h"
     33 #include "nvim/eval.h"
     34 #include "nvim/eval/vars.h"
     35 #include "nvim/ex_cmds.h"
     36 #include "nvim/ex_cmds2.h"
     37 #include "nvim/ex_docmd.h"
     38 #include "nvim/ex_eval.h"
     39 #include "nvim/ex_getln.h"
     40 #include "nvim/file_search.h"
     41 #include "nvim/fileio.h"
     42 #include "nvim/fold.h"
     43 #include "nvim/getchar.h"
     44 #include "nvim/gettext_defs.h"
     45 #include "nvim/globals.h"
     46 #include "nvim/grid.h"
     47 #include "nvim/help.h"
     48 #include "nvim/highlight.h"
     49 #include "nvim/highlight_defs.h"
     50 #include "nvim/keycodes.h"
     51 #include "nvim/macros_defs.h"
     52 #include "nvim/mapping.h"
     53 #include "nvim/mark.h"
     54 #include "nvim/mark_defs.h"
     55 #include "nvim/math.h"
     56 #include "nvim/mbyte.h"
     57 #include "nvim/mbyte_defs.h"
     58 #include "nvim/memline.h"
     59 #include "nvim/memline_defs.h"
     60 #include "nvim/memory.h"
     61 #include "nvim/message.h"
     62 #include "nvim/mouse.h"
     63 #include "nvim/move.h"
     64 #include "nvim/normal.h"
     65 #include "nvim/ops.h"
     66 #include "nvim/option.h"
     67 #include "nvim/option_vars.h"
     68 #include "nvim/os/input.h"
     69 #include "nvim/os/time.h"
     70 #include "nvim/plines.h"
     71 #include "nvim/profile.h"
     72 #include "nvim/quickfix.h"
     73 #include "nvim/register.h"
     74 #include "nvim/search.h"
     75 #include "nvim/spell.h"
     76 #include "nvim/spell_defs.h"
     77 #include "nvim/spellfile.h"
     78 #include "nvim/spellsuggest.h"
     79 #include "nvim/state.h"
     80 #include "nvim/state_defs.h"
     81 #include "nvim/statusline.h"
     82 #include "nvim/strings.h"
     83 #include "nvim/syntax.h"
     84 #include "nvim/tag.h"
     85 #include "nvim/terminal.h"
     86 #include "nvim/textformat.h"
     87 #include "nvim/textobject.h"
     88 #include "nvim/types_defs.h"
     89 #include "nvim/ui.h"
     90 #include "nvim/ui_defs.h"
     91 #include "nvim/undo.h"
     92 #include "nvim/vim_defs.h"
     93 #include "nvim/window.h"
     94 
     95 typedef struct {
     96  VimState state;
     97  bool command_finished;
     98  bool ctrl_w;
     99  bool need_flushbuf;
    100  bool set_prevcount;
    101  bool previous_got_int;             // `got_int` was true
    102  bool cmdwin;                       // command-line window normal mode
    103  bool noexmode;                     // true if the normal mode was pushed from
    104                                     // ex mode (:global or :visual for example)
    105  bool toplevel;                     // top-level normal mode
    106  oparg_T oa;                        // operator arguments
    107  cmdarg_T ca;                       // command arguments
    108  int mapped_len;
    109  int old_mapped_len;
    110  int idx;
    111  int c;
    112  int old_col;
    113  pos_T old_pos;
    114 } NormalState;
    115 
    116 static int VIsual_mode_orig = NUL;              // saved Visual mode
    117 
    118 #include "normal.c.generated.h"
    119 
    120 static const char e_changelist_is_empty[] = N_("E664: Changelist is empty");
    121 static const char e_cmdline_window_already_open[]
    122  = N_("E1292: Command-line window is already open");
    123 
    124 static inline void normal_state_init(NormalState *s)
    125 {
    126  memset(s, 0, sizeof(NormalState));
    127  s->state.check = normal_check;
    128  s->state.execute = normal_execute;
    129 }
    130 
    131 // nv_*(): functions called to handle Normal and Visual mode commands.
    132 // n_*(): functions called to handle Normal mode commands.
    133 // v_*(): functions called to handle Visual mode commands.
    134 
    135 static const char *e_noident = N_("E349: No identifier under cursor");
    136 
    137 /// Function to be called for a Normal or Visual mode command.
    138 /// The argument is a cmdarg_T.
    139 typedef void (*nv_func_T)(cmdarg_T *cap);
    140 
    141 // Values for cmd_flags.
    142 #define NV_NCH      0x01          // may need to get a second char
    143 #define NV_NCH_NOP  (0x02|NV_NCH)  // get second char when no operator pending
    144 #define NV_NCH_ALW  (0x04|NV_NCH)  // always get a second char
    145 #define NV_LANG     0x08        // second char needs language adjustment
    146 
    147 #define NV_SS       0x10        // may start selection
    148 #define NV_SSS      0x20        // may start selection with shift modifier
    149 #define NV_STS      0x40        // may stop selection without shift modif.
    150 #define NV_RL       0x80        // 'rightleft' modifies command
    151 #define NV_KEEPREG  0x100       // don't clear regname
    152 #define NV_NCW      0x200       // not allowed in command-line window
    153 
    154 // Generally speaking, every Normal mode command should either clear any
    155 // pending operator (with *clearop*()), or set the motion type variable
    156 // oap->motion_type.
    157 //
    158 // When a cursor motion command is made, it is marked as being a character or
    159 // line oriented motion.  Then, if an operator is in effect, the operation
    160 // becomes character or line oriented accordingly.
    161 
    162 /// This table contains one entry for every Normal or Visual mode command.
    163 /// The order doesn't matter, init_normal_cmds() will create a sorted index.
    164 /// It is faster when all keys from zero to '~' are present.
    165 static const struct nv_cmd {
    166  int cmd_char;                 ///< (first) command character
    167  nv_func_T cmd_func;           ///< function for this command
    168  uint16_t cmd_flags;           ///< NV_ flags
    169  int16_t cmd_arg;              ///< value for ca.arg
    170 } nv_cmds[] = {
    171  { NUL,       nv_error,       0,                      0 },
    172  { Ctrl_A,    nv_addsub,      0,                      0 },
    173  { Ctrl_B,    nv_page,        NV_STS,                 BACKWARD },
    174  { Ctrl_C,    nv_esc,         0,                      true },
    175  { Ctrl_D,    nv_halfpage,    0,                      0 },
    176  { Ctrl_E,    nv_scroll_line, 0,                      true },
    177  { Ctrl_F,    nv_page,        NV_STS,                 FORWARD },
    178  { Ctrl_G,    nv_ctrlg,       0,                      0 },
    179  { Ctrl_H,    nv_ctrlh,       0,                      0 },
    180  { Ctrl_I,    nv_pcmark,      0,                      0 },
    181  { NL,        nv_down,        0,                      false },
    182  { Ctrl_K,    nv_error,       0,                      0 },
    183  { Ctrl_L,    nv_clear,       0,                      0 },
    184  { CAR,       nv_down,        0,                      true },
    185  { Ctrl_N,    nv_down,        NV_STS,                 false },
    186  { Ctrl_O,    nv_ctrlo,       0,                      0 },
    187  { Ctrl_P,    nv_up,          NV_STS,                 false },
    188  { Ctrl_Q,    nv_visual,      0,                      false },
    189  { Ctrl_R,    nv_redo_or_register, 0,                      0 },
    190  { Ctrl_S,    nv_ignore,      0,                      0 },
    191  { Ctrl_T,    nv_tagpop,      NV_NCW,                 0 },
    192  { Ctrl_U,    nv_halfpage,    0,                      0 },
    193  { Ctrl_V,    nv_visual,      0,                      false },
    194  { 'V',       nv_visual,      0,                      false },
    195  { 'v',       nv_visual,      0,                      false },
    196  { Ctrl_W,    nv_window,      0,                      0 },
    197  { Ctrl_X,    nv_addsub,      0,                      0 },
    198  { Ctrl_Y,    nv_scroll_line, 0,                      false },
    199  { Ctrl_Z,    nv_suspend,     0,                      0 },
    200  { ESC,       nv_esc,         0,                      false },
    201  { Ctrl_BSL,  nv_normal,      NV_NCH_ALW,             0 },
    202  { Ctrl_RSB,  nv_ident,       NV_NCW,                 0 },
    203  { Ctrl_HAT,  nv_hat,         NV_NCW,                 0 },
    204  { Ctrl__,    nv_error,       0,                      0 },
    205  { ' ',       nv_right,       0,                      0 },
    206  { '!',       nv_operator,    0,                      0 },
    207  { '"',       nv_regname,     NV_NCH_NOP|NV_KEEPREG,  0 },
    208  { '#',       nv_ident,       0,                      0 },
    209  { '$',       nv_dollar,      0,                      0 },
    210  { '%',       nv_percent,     0,                      0 },
    211  { '&',       nv_optrans,     0,                      0 },
    212  { '\'',      nv_gomark,      NV_NCH_ALW,             true },
    213  { '(',       nv_brace,       0,                      BACKWARD },
    214  { ')',       nv_brace,       0,                      FORWARD },
    215  { '*',       nv_ident,       0,                      0 },
    216  { '+',       nv_down,        0,                      true },
    217  { ',',       nv_csearch,     0,                      true },
    218  { '-',       nv_up,          0,                      true },
    219  { '.',       nv_dot,         NV_KEEPREG,             0 },
    220  { '/',       nv_search,      0,                      false },
    221  { '0',       nv_beginline,   0,                      0 },
    222  { '1',       nv_ignore,      0,                      0 },
    223  { '2',       nv_ignore,      0,                      0 },
    224  { '3',       nv_ignore,      0,                      0 },
    225  { '4',       nv_ignore,      0,                      0 },
    226  { '5',       nv_ignore,      0,                      0 },
    227  { '6',       nv_ignore,      0,                      0 },
    228  { '7',       nv_ignore,      0,                      0 },
    229  { '8',       nv_ignore,      0,                      0 },
    230  { '9',       nv_ignore,      0,                      0 },
    231  { ':',       nv_colon,       0,                      0 },
    232  { ';',       nv_csearch,     0,                      false },
    233  { '<',       nv_operator,    NV_RL,                  0 },
    234  { '=',       nv_operator,    0,                      0 },
    235  { '>',       nv_operator,    NV_RL,                  0 },
    236  { '?',       nv_search,      0,                      false },
    237  { '@',       nv_at,          NV_NCH_NOP,             false },
    238  { 'A',       nv_edit,        0,                      0 },
    239  { 'B',       nv_bck_word,    0,                      1 },
    240  { 'C',       nv_abbrev,      NV_KEEPREG,             0 },
    241  { 'D',       nv_abbrev,      NV_KEEPREG,             0 },
    242  { 'E',       nv_wordcmd,     0,                      true },
    243  { 'F',       nv_csearch,     NV_NCH_ALW|NV_LANG,     BACKWARD },
    244  { 'G',       nv_goto,        0,                      true },
    245  { 'H',       nv_scroll,      0,                      0 },
    246  { 'I',       nv_edit,        0,                      0 },
    247  { 'J',       nv_join,        0,                      0 },
    248  { 'K',       nv_ident,       0,                      0 },
    249  { 'L',       nv_scroll,      0,                      0 },
    250  { 'M',       nv_scroll,      0,                      0 },
    251  { 'N',       nv_next,        0,                      SEARCH_REV },
    252  { 'O',       nv_open,        0,                      0 },
    253  { 'P',       nv_put,         0,                      0 },
    254  { 'Q',       nv_regreplay, 0,                      0 },
    255  { 'R',       nv_Replace,     0,                      false },
    256  { 'S',       nv_subst,       NV_KEEPREG,             0 },
    257  { 'T',       nv_csearch,     NV_NCH_ALW|NV_LANG,     BACKWARD },
    258  { 'U',       nv_Undo,        0,                      0 },
    259  { 'W',       nv_wordcmd,     0,                      true },
    260  { 'X',       nv_abbrev,      NV_KEEPREG,             0 },
    261  { 'Y',       nv_abbrev,      NV_KEEPREG,             0 },
    262  { 'Z',       nv_Zet,         NV_NCH_NOP|NV_NCW,      0 },
    263  { '[',       nv_brackets,    NV_NCH_ALW,             BACKWARD },
    264  { '\\',      nv_error,       0,                      0 },
    265  { ']',       nv_brackets,    NV_NCH_ALW,             FORWARD },
    266  { '^',       nv_beginline,   0,                      BL_WHITE | BL_FIX },
    267  { '_',       nv_lineop,      0,                      0 },
    268  { '`',       nv_gomark,      NV_NCH_ALW,             false },
    269  { 'a',       nv_edit,        NV_NCH,                 0 },
    270  { 'b',       nv_bck_word,    0,                      0 },
    271  { 'c',       nv_operator,    0,                      0 },
    272  { 'd',       nv_operator,    0,                      0 },
    273  { 'e',       nv_wordcmd,     0,                      false },
    274  { 'f',       nv_csearch,     NV_NCH_ALW|NV_LANG,     FORWARD },
    275  { 'g',       nv_g_cmd,       NV_NCH_ALW,             false },
    276  { 'h',       nv_left,        NV_RL,                  0 },
    277  { 'i',       nv_edit,        NV_NCH,                 0 },
    278  { 'j',       nv_down,        0,                      false },
    279  { 'k',       nv_up,          0,                      false },
    280  { 'l',       nv_right,       NV_RL,                  0 },
    281  { 'm',       nv_mark,        NV_NCH_NOP,             0 },
    282  { 'n',       nv_next,        0,                      0 },
    283  { 'o',       nv_open,        0,                      0 },
    284  { 'p',       nv_put,         0,                      0 },
    285  { 'q',       nv_record,      NV_NCH,                 0 },
    286  { 'r',       nv_replace,     NV_NCH_NOP|NV_LANG,     0 },
    287  { 's',       nv_subst,       NV_KEEPREG,             0 },
    288  { 't',       nv_csearch,     NV_NCH_ALW|NV_LANG,     FORWARD },
    289  { 'u',       nv_undo,        0,                      0 },
    290  { 'w',       nv_wordcmd,     0,                      false },
    291  { 'x',       nv_abbrev,      NV_KEEPREG,             0 },
    292  { 'y',       nv_operator,    0,                      0 },
    293  { 'z',       nv_zet,         NV_NCH_ALW,             0 },
    294  { '{',       nv_findpar,     0,                      BACKWARD },
    295  { '|',       nv_pipe,        0,                      0 },
    296  { '}',       nv_findpar,     0,                      FORWARD },
    297  { '~',       nv_tilde,       0,                      0 },
    298 
    299  // pound sign
    300  { POUND,     nv_ident,       0,                      0 },
    301  { K_MOUSEUP, nv_mousescroll, 0,                      MSCR_UP },
    302  { K_MOUSEDOWN, nv_mousescroll, 0,                    MSCR_DOWN },
    303  { K_MOUSELEFT, nv_mousescroll, 0,                    MSCR_LEFT },
    304  { K_MOUSERIGHT, nv_mousescroll, 0,                   MSCR_RIGHT },
    305  { K_LEFTMOUSE, nv_mouse,     0,                      0 },
    306  { K_LEFTMOUSE_NM, nv_mouse,  0,                      0 },
    307  { K_LEFTDRAG, nv_mouse,      0,                      0 },
    308  { K_LEFTRELEASE, nv_mouse,   0,                      0 },
    309  { K_LEFTRELEASE_NM, nv_mouse, 0,                     0 },
    310  { K_MOUSEMOVE, nv_mouse,     0,                      0 },
    311  { K_MIDDLEMOUSE, nv_mouse,   0,                      0 },
    312  { K_MIDDLEDRAG, nv_mouse,    0,                      0 },
    313  { K_MIDDLERELEASE, nv_mouse, 0,                      0 },
    314  { K_RIGHTMOUSE, nv_mouse,    0,                      0 },
    315  { K_RIGHTDRAG, nv_mouse,     0,                      0 },
    316  { K_RIGHTRELEASE, nv_mouse,  0,                      0 },
    317  { K_X1MOUSE, nv_mouse,       0,                      0 },
    318  { K_X1DRAG, nv_mouse,        0,                      0 },
    319  { K_X1RELEASE, nv_mouse,     0,                      0 },
    320  { K_X2MOUSE, nv_mouse,       0,                      0 },
    321  { K_X2DRAG, nv_mouse,        0,                      0 },
    322  { K_X2RELEASE, nv_mouse,     0,                      0 },
    323  { K_IGNORE,  nv_ignore,      NV_KEEPREG,             0 },
    324  { K_NOP,     nv_nop,         0,                      0 },
    325  { K_INS,     nv_edit,        0,                      0 },
    326  { K_KINS,    nv_edit,        0,                      0 },
    327  { K_BS,      nv_ctrlh,       0,                      0 },
    328  { K_UP,      nv_up,          NV_SSS|NV_STS,          false },
    329  { K_S_UP,    nv_page,        NV_SS,                  BACKWARD },
    330  { K_DOWN,    nv_down,        NV_SSS|NV_STS,          false },
    331  { K_S_DOWN,  nv_page,        NV_SS,                  FORWARD },
    332  { K_LEFT,    nv_left,        NV_SSS|NV_STS|NV_RL,    0 },
    333  { K_S_LEFT,  nv_bck_word,    NV_SS|NV_RL,            0 },
    334  { K_C_LEFT,  nv_bck_word,    NV_SSS|NV_RL|NV_STS,    1 },
    335  { K_RIGHT,   nv_right,       NV_SSS|NV_STS|NV_RL,    0 },
    336  { K_S_RIGHT, nv_wordcmd,     NV_SS|NV_RL,            false },
    337  { K_C_RIGHT, nv_wordcmd,     NV_SSS|NV_RL|NV_STS,    true },
    338  { K_PAGEUP,  nv_page,        NV_SSS|NV_STS,          BACKWARD },
    339  { K_KPAGEUP, nv_page,        NV_SSS|NV_STS,          BACKWARD },
    340  { K_PAGEDOWN, nv_page,       NV_SSS|NV_STS,          FORWARD },
    341  { K_KPAGEDOWN, nv_page,      NV_SSS|NV_STS,          FORWARD },
    342  { K_END,     nv_end,         NV_SSS|NV_STS,          false },
    343  { K_KEND,    nv_end,         NV_SSS|NV_STS,          false },
    344  { K_S_END,   nv_end,         NV_SS,                  false },
    345  { K_C_END,   nv_end,         NV_SSS|NV_STS,          true },
    346  { K_HOME,    nv_home,        NV_SSS|NV_STS,          0 },
    347  { K_KHOME,   nv_home,        NV_SSS|NV_STS,          0 },
    348  { K_S_HOME,  nv_home,        NV_SS,                  0 },
    349  { K_C_HOME,  nv_goto,        NV_SSS|NV_STS,          false },
    350  { K_DEL,     nv_abbrev,      0,                      0 },
    351  { K_KDEL,    nv_abbrev,      0,                      0 },
    352  { K_UNDO,    nv_kundo,       0,                      0 },
    353  { K_HELP,    nv_help,        NV_NCW,                 0 },
    354  { K_F1,      nv_help,        NV_NCW,                 0 },
    355  { K_XF1,     nv_help,        NV_NCW,                 0 },
    356  { K_SELECT,  nv_select,      0,                      0 },
    357  { K_PASTE_START, nv_paste,   NV_KEEPREG,             0 },
    358  { K_EVENT,   nv_event,       NV_KEEPREG,             0 },
    359  { K_COMMAND, nv_colon,       0,                      0 },
    360  { K_LUA, nv_colon,           0,                      0 },
    361 };
    362 
    363 // Number of commands in nv_cmds[].
    364 #define NV_CMDS_SIZE ARRAY_SIZE(nv_cmds)
    365 
    366 // Sorted index of commands in nv_cmds[].
    367 static int16_t nv_cmd_idx[NV_CMDS_SIZE];
    368 
    369 // The highest index for which
    370 // nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char]
    371 static int nv_max_linear;
    372 
    373 /// Compare functions for qsort() below, that checks the command character
    374 /// through the index in nv_cmd_idx[].
    375 static int nv_compare(const void *s1, const void *s2)
    376 {
    377  // The commands are sorted on absolute value.
    378  int c1 = nv_cmds[*(const int16_t *)s1].cmd_char;
    379  int c2 = nv_cmds[*(const int16_t *)s2].cmd_char;
    380  if (c1 < 0) {
    381    c1 = -c1;
    382  }
    383  if (c2 < 0) {
    384    c2 = -c2;
    385  }
    386  return c1 == c2 ? 0 : c1 > c2 ? 1 : -1;
    387 }
    388 
    389 /// Initialize the nv_cmd_idx[] table.
    390 void init_normal_cmds(void)
    391 {
    392  assert(NV_CMDS_SIZE <= SHRT_MAX);
    393 
    394  // Fill the index table with a one to one relation.
    395  for (int16_t i = 0; i < (int16_t)NV_CMDS_SIZE; i++) {
    396    nv_cmd_idx[i] = i;
    397  }
    398 
    399  // Sort the commands by the command character.
    400  qsort(&nv_cmd_idx, NV_CMDS_SIZE, sizeof(int16_t), nv_compare);
    401 
    402  // Find the first entry that can't be indexed by the command character.
    403  int16_t i;
    404  for (i = 0; i < (int16_t)NV_CMDS_SIZE; i++) {
    405    if (i != nv_cmds[nv_cmd_idx[i]].cmd_char) {
    406      break;
    407    }
    408  }
    409  nv_max_linear = i - 1;
    410 }
    411 
    412 /// Search for a command in the commands table.
    413 ///
    414 /// @return  -1 for invalid command.
    415 static int find_command(int cmdchar)
    416 {
    417  // A multi-byte character is never a command.
    418  if (cmdchar >= 0x100) {
    419    return -1;
    420  }
    421 
    422  // We use the absolute value of the character.  Special keys have a
    423  // negative value, but are sorted on their absolute value.
    424  if (cmdchar < 0) {
    425    cmdchar = -cmdchar;
    426  }
    427 
    428  // If the character is in the first part: The character is the index into
    429  // nv_cmd_idx[].
    430  assert(nv_max_linear < (int)NV_CMDS_SIZE);
    431  if (cmdchar <= nv_max_linear) {
    432    return nv_cmd_idx[cmdchar];
    433  }
    434 
    435  // Perform a binary search.
    436  int bot = nv_max_linear + 1;
    437  int top = NV_CMDS_SIZE - 1;
    438  int idx = -1;
    439  while (bot <= top) {
    440    int i = (top + bot) / 2;
    441    int c = nv_cmds[nv_cmd_idx[i]].cmd_char;
    442    if (c < 0) {
    443      c = -c;
    444    }
    445    if (cmdchar == c) {
    446      idx = nv_cmd_idx[i];
    447      break;
    448    }
    449    if (cmdchar > c) {
    450      bot = i + 1;
    451    } else {
    452      top = i - 1;
    453    }
    454  }
    455  return idx;
    456 }
    457 
    458 /// If currently editing a cmdline or text is locked: beep and give an error
    459 /// message, return true.
    460 static bool check_text_locked(oparg_T *oap)
    461 {
    462  if (!text_locked()) {
    463    return false;
    464  }
    465 
    466  if (oap != NULL) {
    467    clearopbeep(oap);
    468  }
    469  text_locked_msg();
    470  return true;
    471 }
    472 
    473 /// If text is locked, "curbuf->b_ro_locked" or "allbuf_lock" is set:
    474 /// Give an error message, possibly beep and return true.
    475 /// "oap" may be NULL.
    476 bool check_text_or_curbuf_locked(oparg_T *oap)
    477 {
    478  if (check_text_locked(oap)) {
    479    return true;
    480  }
    481 
    482  if (!curbuf_locked()) {
    483    return false;
    484  }
    485 
    486  if (oap != NULL) {
    487    clearop(oap);
    488  }
    489  return true;
    490 }
    491 
    492 static oparg_T *current_oap = NULL;
    493 
    494 /// Check if an operator was started but not finished yet.
    495 /// Includes typing a count or a register name.
    496 bool op_pending(void)
    497 {
    498  return !(current_oap != NULL
    499           && !finish_op
    500           && current_oap->prev_opcount == 0
    501           && current_oap->prev_count0 == 0
    502           && current_oap->op_type == OP_NOP
    503           && current_oap->regname == NUL);
    504 }
    505 
    506 /// Normal state entry point. This is called on:
    507 ///
    508 /// - Startup, In this case the function never returns.
    509 /// - The command-line window is opened (`q:`). Returns when `cmdwin_result` != 0.
    510 /// - The :visual command is called from :global in ex mode, `:global/PAT/visual`
    511 ///   for example. Returns when re-entering ex mode (because ex mode recursion is
    512 ///   not allowed)
    513 ///
    514 /// This used to be called main_loop() on main.c
    515 void normal_enter(bool cmdwin, bool noexmode)
    516 {
    517  NormalState state;
    518  normal_state_init(&state);
    519  oparg_T *prev_oap = current_oap;
    520  current_oap = &state.oa;
    521  state.cmdwin = cmdwin;
    522  state.noexmode = noexmode;
    523  state.toplevel = (!cmdwin || cmdwin_result == 0) && !noexmode;
    524  state_enter(&state.state);
    525  current_oap = prev_oap;
    526 }
    527 
    528 static void normal_prepare(NormalState *s)
    529 {
    530  CLEAR_FIELD(s->ca);  // also resets s->ca.retval
    531  s->ca.oap = &s->oa;
    532 
    533  // Use a count remembered from before entering an operator. After typing "3d"
    534  // we return from normal_cmd() and come back here, the "3" is remembered in
    535  // "opcount".
    536  s->ca.opcount = opcount;
    537 
    538  // If there is an operator pending, then the command we take this time will
    539  // terminate it. Finish_op tells us to finish the operation before returning
    540  // this time (unless the operation was cancelled).
    541  int c = finish_op;
    542  finish_op = (s->oa.op_type != OP_NOP);
    543  if (finish_op != c) {
    544    ui_cursor_shape();  // may show different cursor shape
    545  }
    546  may_trigger_modechanged();
    547 
    548  s->set_prevcount = false;
    549  // When not finishing an operator and no register name typed, reset the count.
    550  if (!finish_op && !s->oa.regname) {
    551    s->ca.opcount = 0;
    552    s->set_prevcount = true;
    553  }
    554 
    555  // Restore counts from before receiving K_EVENT.  This means after
    556  // typing "3", handling K_EVENT and then typing "2" we get "32", not
    557  // "3 * 2".
    558  if (s->oa.prev_opcount > 0 || s->oa.prev_count0 > 0) {
    559    s->ca.opcount = s->oa.prev_opcount;
    560    s->ca.count0 = s->oa.prev_count0;
    561    s->oa.prev_opcount = 0;
    562    s->oa.prev_count0 = 0;
    563  }
    564 
    565  s->mapped_len = typebuf_maplen();
    566  State = MODE_NORMAL_BUSY;
    567 
    568  // Set v:count here, when called from main() and not a stuffed command, so
    569  // that v:count can be used in an expression mapping when there is no count.
    570  // Do set it for redo
    571  if (s->toplevel && readbuf1_empty()) {
    572    set_vcount_ca(&s->ca, &s->set_prevcount);
    573  }
    574 }
    575 
    576 static bool normal_handle_special_visual_command(NormalState *s)
    577 {
    578  // when 'keymodel' contains "stopsel" may stop Select/Visual mode
    579  if (km_stopsel
    580      && (nv_cmds[s->idx].cmd_flags & NV_STS)
    581      && !(mod_mask & MOD_MASK_SHIFT)) {
    582    end_visual_mode();
    583    redraw_curbuf_later(UPD_INVERTED);
    584  }
    585 
    586  // Keys that work different when 'keymodel' contains "startsel"
    587  if (km_startsel) {
    588    if (nv_cmds[s->idx].cmd_flags & NV_SS) {
    589      unshift_special(&s->ca);
    590      s->idx = find_command(s->ca.cmdchar);
    591      if (s->idx < 0) {
    592        // Just in case
    593        clearopbeep(&s->oa);
    594        return true;
    595      }
    596    } else if ((nv_cmds[s->idx].cmd_flags & NV_SSS)
    597               && (mod_mask & MOD_MASK_SHIFT)) {
    598      mod_mask &= ~MOD_MASK_SHIFT;
    599    }
    600  }
    601  return false;
    602 }
    603 
    604 static bool normal_need_additional_char(NormalState *s)
    605 {
    606  int flags = nv_cmds[s->idx].cmd_flags;
    607  bool pending_op = s->oa.op_type != OP_NOP;
    608  int cmdchar = s->ca.cmdchar;
    609  // without NV_NCH we never need to check for an additional char
    610  return flags & NV_NCH && (
    611                            // NV_NCH_NOP is set and no operator is pending, get a second char
    612                            ((flags & NV_NCH_NOP) == NV_NCH_NOP && !pending_op)
    613                            // NV_NCH_ALW is set, always get a second char
    614                            || (flags & NV_NCH_ALW) == NV_NCH_ALW
    615                            // 'q' without a pending operator, recording or executing a register,
    616                            // needs to be followed by a second char, examples:
    617                            // - qc => record using register c
    618                            // - q: => open command-line window
    619                            || (cmdchar == 'q'
    620                                && !pending_op
    621                                && reg_recording == 0
    622                                && reg_executing == 0)
    623                            // 'a' or 'i' after an operator is a text object, examples:
    624                            // - ciw => change inside word
    625                            // - da( => delete parenthesis and everything inside.
    626                            // Also, don't do anything when these keys are received in visual mode
    627                            // so just get another char.
    628                            //
    629                            // TODO(tarruda): Visual state needs to be refactored into a
    630                            // separate state that "inherits" from normal state.
    631                            || ((cmdchar == 'a' || cmdchar == 'i')
    632                                && (pending_op || VIsual_active)));
    633 }
    634 
    635 static bool normal_need_redraw_mode_message(NormalState *s)
    636 {
    637  // In Visual mode and with "^O" in Insert mode, a short message will be
    638  // overwritten by the mode message.  Wait a bit, until a key is hit.
    639  // In Visual mode, it's more important to keep the Visual area updated
    640  // than keeping a message (e.g. from a /pat search).
    641  // Only do this if the command was typed, not from a mapping.
    642  // Don't wait when emsg_silent is non-zero.
    643  // Also wait a bit after an error message, e.g. for "^O:".
    644  // Don't redraw the screen, it would remove the message.
    645  return (
    646          // 'showmode' is set and messages can be printed
    647          ((p_smd && msg_silent == 0
    648            // must restart insert mode (ctrl+o or ctrl+l) or just entered visual mode
    649            && (restart_edit != 0 || (VIsual_active
    650                                      && s->old_pos.lnum == curwin->w_cursor.lnum
    651                                      && s->old_pos.col == curwin->w_cursor.col))
    652            // command-line must be cleared or redrawn
    653            && (clear_cmdline || redraw_cmdline)
    654            // some message was printed or scrolled
    655            && (msg_didout || (msg_didany && msg_scroll))
    656            // it is fine to remove the current message
    657            && !msg_nowait
    658            // the command was the result of direct user input and not a mapping
    659            && KeyTyped)
    660           // must restart insert mode, not in visual mode and error message is
    661           // being shown
    662           || (restart_edit != 0 && !VIsual_active && msg_scroll
    663               && emsg_on_display))
    664          // no register was used
    665          && s->oa.regname == 0
    666          && !(s->ca.retval & CA_COMMAND_BUSY)
    667          && stuff_empty()
    668          && typebuf_typed()
    669          && emsg_silent == 0
    670          && !in_assert_fails
    671          && !did_wait_return
    672          && s->oa.op_type == OP_NOP);
    673 }
    674 
    675 static void normal_redraw_mode_message(NormalState *s)
    676 {
    677  int save_State = State;
    678 
    679  // Draw the cursor with the right shape here
    680  if (restart_edit != 0) {
    681    State = MODE_INSERT;
    682  }
    683 
    684  // If need to redraw, and there is a "keep_msg", redraw before the
    685  // delay
    686  if (must_redraw && keep_msg != NULL && !emsg_on_display) {
    687    char *kmsg;
    688 
    689    kmsg = keep_msg;
    690    keep_msg = NULL;
    691    // Showmode() will clear keep_msg, but we want to use it anyway.
    692    // First update w_topline.
    693    setcursor();
    694    update_screen();
    695    // now reset it, otherwise it's put in the history again
    696    keep_msg = kmsg;
    697 
    698    kmsg = xstrdup(keep_msg);
    699    msg(kmsg, keep_msg_hl_id);
    700    xfree(kmsg);
    701  }
    702  setcursor();
    703  ui_cursor_shape();                  // show different cursor shape
    704  ui_flush();
    705  if (msg_scroll || emsg_on_display) {
    706    msg_delay(1003, true);    // wait extra second for scrolled or error message
    707  }
    708  msg_delay(3003, false);    // wait three seconds before doing 'showmode'
    709  State = save_State;
    710 
    711  msg_scroll = false;
    712  emsg_on_display = false;
    713 }
    714 
    715 // TODO(tarruda): Split into a "normal pending" state that can handle K_EVENT
    716 static void normal_get_additional_char(NormalState *s)
    717 {
    718  int *cp;
    719  bool repl = false;            // get character for replace mode
    720  bool lit = false;             // get extra character literally
    721  bool lang;                    // getting a text character
    722 
    723  no_mapping++;
    724  allow_keys++;                 // no mapping for nchar, but allow key codes
    725  // Don't generate a CursorHold event here, most commands can't handle
    726  // it, e.g., nv_replace(), nv_csearch().
    727  did_cursorhold = true;
    728  if (s->ca.cmdchar == 'g') {
    729    // For 'g' get the next character now, so that we can check for
    730    // "gr", "g'" and "g`".
    731    s->ca.nchar = plain_vgetc();
    732    LANGMAP_ADJUST(s->ca.nchar, true);
    733    s->need_flushbuf |= add_to_showcmd(s->ca.nchar);
    734    if (s->ca.nchar == 'r' || s->ca.nchar == '\'' || s->ca.nchar == '`'
    735        || s->ca.nchar == Ctrl_BSL) {
    736      cp = &s->ca.extra_char;            // need to get a third character
    737      if (s->ca.nchar != 'r') {
    738        lit = true;                           // get it literally
    739      } else {
    740        repl = true;                          // get it in replace mode
    741      }
    742    } else {
    743      cp = NULL;                      // no third character needed
    744    }
    745  } else {
    746    if (s->ca.cmdchar == 'r') {
    747      // get it in replace mode
    748      repl = true;
    749    }
    750    cp = &s->ca.nchar;
    751  }
    752  lang = (repl || (nv_cmds[s->idx].cmd_flags & NV_LANG));
    753 
    754  // Get a second or third character.
    755  if (cp != NULL) {
    756    bool langmap_active = false;  // using :lmap mappings
    757    if (repl) {
    758      State = MODE_REPLACE;                // pretend Replace mode
    759      ui_cursor_shape_no_check_conceal();  // show different cursor shape
    760    }
    761    if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) {
    762      // Allow mappings defined with ":lmap".
    763      no_mapping--;
    764      allow_keys--;
    765      if (repl) {
    766        State = MODE_LREPLACE;
    767      } else {
    768        State = MODE_LANGMAP;
    769      }
    770      langmap_active = true;
    771    }
    772 
    773    *cp = plain_vgetc();
    774 
    775    if (langmap_active) {
    776      // Undo the decrement done above
    777      no_mapping++;
    778      allow_keys++;
    779    }
    780    State = MODE_NORMAL_BUSY;
    781    s->need_flushbuf |= add_to_showcmd(*cp);
    782 
    783    if (!lit) {
    784      // Typing CTRL-K gets a digraph.
    785      if (*cp == Ctrl_K && ((nv_cmds[s->idx].cmd_flags & NV_LANG)
    786                            || cp == &s->ca.extra_char)
    787          && vim_strchr(p_cpo, CPO_DIGRAPH) == NULL) {
    788        s->c = get_digraph(false);
    789        if (s->c > 0) {
    790          *cp = s->c;
    791          // Guessing how to update showcmd here...
    792          del_from_showcmd(3);
    793          s->need_flushbuf |= add_to_showcmd(*cp);
    794        }
    795      }
    796 
    797      // adjust chars > 127, except after "tTfFr" commands
    798      LANGMAP_ADJUST(*cp, !lang);
    799    }
    800 
    801    // When the next character is CTRL-\ a following CTRL-N means the
    802    // command is aborted and we go to Normal mode.
    803    if (cp == &s->ca.extra_char
    804        && s->ca.nchar == Ctrl_BSL
    805        && (s->ca.extra_char == Ctrl_N || s->ca.extra_char == Ctrl_G)) {
    806      s->ca.cmdchar = Ctrl_BSL;
    807      s->ca.nchar = s->ca.extra_char;
    808      s->idx = find_command(s->ca.cmdchar);
    809    } else if ((s->ca.nchar == 'n' || s->ca.nchar == 'N')
    810               && s->ca.cmdchar == 'g') {
    811      s->ca.oap->op_type = get_op_type(*cp, NUL);
    812    } else if (*cp == Ctrl_BSL) {
    813      int towait = (p_ttm >= 0 ? (int)p_ttm : (int)p_tm);
    814 
    815      // There is a busy wait here when typing "f<C-\>" and then
    816      // something different from CTRL-N.  Can't be avoided.
    817      while ((s->c = vpeekc()) <= 0 && towait > 0) {
    818        do_sleep(towait > 50 ? 50 : towait, false);
    819        towait -= 50;
    820      }
    821      if (s->c > 0) {
    822        s->c = plain_vgetc();
    823        if (s->c != Ctrl_N && s->c != Ctrl_G) {
    824          vungetc(s->c);
    825        } else {
    826          s->ca.cmdchar = Ctrl_BSL;
    827          s->ca.nchar = s->c;
    828          s->idx = find_command(s->ca.cmdchar);
    829          assert(s->idx >= 0);
    830        }
    831      }
    832    }
    833 
    834    if (lang) {
    835      // When getting a text character and the next character is a
    836      // multi-byte character, it could be a composing character.
    837      // However, don't wait for it to arrive. Also, do enable mapping,
    838      // because if it's put back with vungetc() it's too late to apply
    839      // mapping.
    840      no_mapping--;
    841      GraphemeState state = GRAPHEME_STATE_INIT;
    842      int prev_code = s->ca.nchar;
    843 
    844      while ((s->c = vpeekc()) > 0
    845             && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) {
    846        s->c = plain_vgetc();
    847 
    848        if (!utf_iscomposing(prev_code, s->c, &state)) {
    849          vungetc(s->c);                   // it wasn't, put it back
    850          break;
    851        }
    852 
    853        // first composing char, first put base char into buffer
    854        if (s->ca.nchar_len == 0) {
    855          s->ca.nchar_len = utf_char2bytes(s->ca.nchar, s->ca.nchar_composing);
    856        }
    857 
    858        if (s->ca.nchar_len + utf_char2len(s->c) < (int)sizeof(s->ca.nchar_composing)) {
    859          s->ca.nchar_len += utf_char2bytes(s->c, s->ca.nchar_composing + s->ca.nchar_len);
    860        }
    861        prev_code = s->c;
    862      }
    863      s->ca.nchar_composing[s->ca.nchar_len] = NUL;
    864      no_mapping++;
    865      // Vim may be in a different mode when the user types the next key,
    866      // but when replaying a recording the next key is already in the
    867      // typeahead buffer, so record an <Ignore> before that to prevent
    868      // the vpeekc() above from applying wrong mappings when replaying.
    869      no_u_sync++;
    870      gotchars_ignore();
    871      no_u_sync--;
    872    }
    873  }
    874  no_mapping--;
    875  allow_keys--;
    876 }
    877 
    878 static void normal_invert_horizontal(NormalState *s)
    879 {
    880  switch (s->ca.cmdchar) {
    881  case 'l':
    882    s->ca.cmdchar = 'h'; break;
    883  case K_RIGHT:
    884    s->ca.cmdchar = K_LEFT; break;
    885  case K_S_RIGHT:
    886    s->ca.cmdchar = K_S_LEFT; break;
    887  case K_C_RIGHT:
    888    s->ca.cmdchar = K_C_LEFT; break;
    889  case 'h':
    890    s->ca.cmdchar = 'l'; break;
    891  case K_LEFT:
    892    s->ca.cmdchar = K_RIGHT; break;
    893  case K_S_LEFT:
    894    s->ca.cmdchar = K_S_RIGHT; break;
    895  case K_C_LEFT:
    896    s->ca.cmdchar = K_C_RIGHT; break;
    897  case '>':
    898    s->ca.cmdchar = '<'; break;
    899  case '<':
    900    s->ca.cmdchar = '>'; break;
    901  }
    902  s->idx = find_command(s->ca.cmdchar);
    903 }
    904 
    905 static bool normal_get_command_count(NormalState *s)
    906 {
    907  if (VIsual_active && VIsual_select) {
    908    return false;
    909  }
    910  // Handle a count before a command and compute ca.count0.
    911  // Note that '0' is a command and not the start of a count, but it's
    912  // part of a count after other digits.
    913  while ((s->c >= '1' && s->c <= '9')
    914         || (s->ca.count0 != 0 && (s->c == K_DEL || s->c == K_KDEL || s->c == '0'))) {
    915    if (s->c == K_DEL || s->c == K_KDEL) {
    916      s->ca.count0 /= 10;
    917      del_from_showcmd(4);            // delete the digit and ~@%
    918    } else if (s->ca.count0 > 99999999) {
    919      s->ca.count0 = 999999999;
    920    } else {
    921      s->ca.count0 = s->ca.count0 * 10 + (s->c - '0');
    922    }
    923 
    924    // Set v:count here, when called from main() and not a stuffed
    925    // command, so that v:count can be used in an expression mapping
    926    // right after the count. Do set it for redo.
    927    if (s->toplevel && readbuf1_empty()) {
    928      set_vcount_ca(&s->ca, &s->set_prevcount);
    929    }
    930 
    931    if (s->ctrl_w) {
    932      no_mapping++;
    933      allow_keys++;                   // no mapping for nchar, but keys
    934    }
    935 
    936    no_zero_mapping++;                // don't map zero here
    937    s->c = plain_vgetc();
    938    LANGMAP_ADJUST(s->c, true);
    939    no_zero_mapping--;
    940    if (s->ctrl_w) {
    941      no_mapping--;
    942      allow_keys--;
    943    }
    944    s->need_flushbuf |= add_to_showcmd(s->c);
    945  }
    946 
    947  // If we got CTRL-W there may be a/another count
    948  if (s->c == Ctrl_W && !s->ctrl_w && s->oa.op_type == OP_NOP) {
    949    s->ctrl_w = true;
    950    s->ca.opcount = s->ca.count0;           // remember first count
    951    s->ca.count0 = 0;
    952    no_mapping++;
    953    allow_keys++;                        // no mapping for nchar, but keys
    954    s->c = plain_vgetc();                // get next character
    955    LANGMAP_ADJUST(s->c, true);
    956    no_mapping--;
    957    allow_keys--;
    958    s->need_flushbuf |= add_to_showcmd(s->c);
    959    return true;
    960  }
    961 
    962  return false;
    963 }
    964 
    965 static void normal_finish_command(NormalState *s)
    966 {
    967  bool did_visual_op = false;
    968 
    969  if (s->command_finished) {
    970    goto normal_end;
    971  }
    972 
    973  // If we didn't start or finish an operator, reset oap->regname, unless we
    974  // need it later.
    975  if (!finish_op
    976      && !s->oa.op_type
    977      && (s->idx < 0 || !(nv_cmds[s->idx].cmd_flags & NV_KEEPREG))) {
    978    clearop(&s->oa);
    979    set_reg_var(get_default_register_name());
    980  }
    981 
    982  // Get the length of mapped chars again after typing a count, second
    983  // character or "z333<cr>".
    984  if (s->old_mapped_len > 0) {
    985    s->old_mapped_len = typebuf_maplen();
    986  }
    987 
    988  // If an operation is pending, handle it.  But not for K_IGNORE or
    989  // K_MOUSEMOVE.
    990  if (s->ca.cmdchar != K_IGNORE && s->ca.cmdchar != K_MOUSEMOVE) {
    991    did_visual_op = VIsual_active && s->oa.op_type != OP_NOP
    992                    // For OP_COLON, do_pending_operator() stuffs ':' into
    993                    // the read buffer, which isn't executed immediately.
    994                    && s->oa.op_type != OP_COLON;
    995    do_pending_operator(&s->ca, s->old_col, false);
    996  }
    997 
    998  // Wait for a moment when a message is displayed that will be overwritten
    999  // by the mode message.
   1000  if (normal_need_redraw_mode_message(s)) {
   1001    normal_redraw_mode_message(s);
   1002  }
   1003 
   1004  // Finish up after executing a Normal mode command.
   1005 normal_end:
   1006 
   1007  msg_nowait = false;
   1008 
   1009  if (finish_op || did_visual_op) {
   1010    set_reg_var(get_default_register_name());
   1011  }
   1012 
   1013  const bool prev_finish_op = finish_op;
   1014  if (s->oa.op_type == OP_NOP) {
   1015    // Reset finish_op, in case it was set
   1016    finish_op = false;
   1017    may_trigger_modechanged();
   1018  }
   1019  // Redraw the cursor with another shape, if we were in Operator-pending
   1020  // mode or did a replace command.
   1021  if (prev_finish_op || s->ca.cmdchar == 'r'
   1022      || (s->ca.cmdchar == 'g' && s->ca.nchar == 'r')) {
   1023    ui_cursor_shape();                  // may show different cursor shape
   1024  }
   1025 
   1026  if (s->oa.op_type == OP_NOP && s->oa.regname == 0
   1027      && s->ca.cmdchar != K_EVENT) {
   1028    clear_showcmd();
   1029  }
   1030 
   1031  checkpcmark();                // check if we moved since setting pcmark
   1032  xfree(s->ca.searchbuf);
   1033 
   1034  mb_check_adjust_col(curwin);  // #6203
   1035 
   1036  if (curwin->w_p_scb && s->toplevel) {
   1037    validate_cursor(curwin);          // may need to update w_leftcol
   1038    do_check_scrollbind(true);
   1039  }
   1040 
   1041  if (curwin->w_p_crb && s->toplevel) {
   1042    validate_cursor(curwin);          // may need to update w_leftcol
   1043    do_check_cursorbind();
   1044  }
   1045 
   1046  // May restart edit(), if we got here with CTRL-O in Insert mode (but not
   1047  // if still inside a mapping that started in Visual mode).
   1048  // May switch from Visual to Select mode after CTRL-O command.
   1049  if (s->oa.op_type == OP_NOP
   1050      && ((restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0)
   1051          || restart_VIsual_select == 1)
   1052      && !(s->ca.retval & CA_COMMAND_BUSY)
   1053      && stuff_empty()
   1054      && s->oa.regname == 0) {
   1055    if (restart_VIsual_select == 1) {
   1056      VIsual_select = true;
   1057      VIsual_select_reg = 0;
   1058      may_trigger_modechanged();
   1059      showmode();
   1060      restart_VIsual_select = 0;
   1061    }
   1062    if (restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0) {
   1063      edit(restart_edit, false, 1);
   1064    }
   1065  }
   1066 
   1067  if (restart_VIsual_select == 2) {
   1068    restart_VIsual_select = 1;
   1069  }
   1070 
   1071  // Save count before an operator for next time
   1072  opcount = s->ca.opcount;
   1073 }
   1074 
   1075 static int normal_execute(VimState *state, int key)
   1076 {
   1077  NormalState *s = (NormalState *)state;
   1078  s->command_finished = false;
   1079  s->ctrl_w = false;                  // got CTRL-W command
   1080  s->old_col = curwin->w_curswant;
   1081  s->c = key;
   1082 
   1083  LANGMAP_ADJUST(s->c, get_real_state() != MODE_SELECT);
   1084 
   1085  // If a mapping was started in Visual or Select mode, remember the length
   1086  // of the mapping.  This is used below to not return to Insert mode for as
   1087  // long as the mapping is being executed.
   1088  if (restart_edit == 0) {
   1089    s->old_mapped_len = 0;
   1090  } else if (s->old_mapped_len || (VIsual_active && s->mapped_len == 0
   1091                                   && typebuf_maplen() > 0)) {
   1092    s->old_mapped_len = typebuf_maplen();
   1093  }
   1094 
   1095  if (s->c == NUL) {
   1096    s->c = K_ZERO;
   1097  }
   1098 
   1099  // In Select mode, typed text replaces the selection.
   1100  if (VIsual_active && VIsual_select && (vim_isprintc(s->c)
   1101                                         || s->c == NL || s->c == CAR || s->c == K_KENTER)) {
   1102    // Fake a "c"hange command.
   1103    // When "restart_edit" is set fake a "d"elete command, Insert mode will restart automatically.
   1104    // Insert the typed character in the typeahead buffer, so that it can
   1105    // be mapped in Insert mode.  Required for ":lmap" to work.
   1106    int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask, true);
   1107 
   1108    // When recording and gotchars() was called the character will be
   1109    // recorded again, remove the previous recording.
   1110    if (KeyTyped) {
   1111      ungetchars(len);
   1112    }
   1113 
   1114    if (restart_edit != 0) {
   1115      s->c = 'd';
   1116    } else {
   1117      s->c = 'c';
   1118    }
   1119    msg_nowait = true;          // don't delay going to insert mode
   1120    s->old_mapped_len = 0;      // do go to Insert mode
   1121  }
   1122 
   1123  s->need_flushbuf = add_to_showcmd(s->c);
   1124 
   1125  while (normal_get_command_count(s)) {}
   1126 
   1127  if (s->c == K_EVENT) {
   1128    // Save the count values so that ca.opcount and ca.count0 are exactly
   1129    // the same when coming back here after handling K_EVENT.
   1130    s->oa.prev_opcount = s->ca.opcount;
   1131    s->oa.prev_count0 = s->ca.count0;
   1132  } else if (s->ca.opcount != 0) {
   1133    // If we're in the middle of an operator (including after entering a
   1134    // yank buffer with '"') AND we had a count before the operator, then
   1135    // that count overrides the current value of ca.count0.
   1136    // What this means effectively, is that commands like "3dw" get turned
   1137    // into "d3w" which makes things fall into place pretty neatly.
   1138    // If you give a count before AND after the operator, they are
   1139    // multiplied.
   1140    if (s->ca.count0) {
   1141      if (s->ca.opcount >= 999999999 / s->ca.count0) {
   1142        s->ca.count0 = 999999999;
   1143      } else {
   1144        s->ca.count0 *= s->ca.opcount;
   1145      }
   1146    } else {
   1147      s->ca.count0 = s->ca.opcount;
   1148    }
   1149  }
   1150 
   1151  // Always remember the count.  It will be set to zero (on the next call,
   1152  // above) when there is no pending operator.
   1153  // When called from main(), save the count for use by the "count" built-in
   1154  // variable.
   1155  s->ca.opcount = s->ca.count0;
   1156  s->ca.count1 = (s->ca.count0 == 0 ? 1 : s->ca.count0);
   1157 
   1158  // Only set v:count when called from main() and not a stuffed command.
   1159  // Do set it for redo.
   1160  if (s->toplevel && readbuf1_empty()) {
   1161    set_vcount(s->ca.count0, s->ca.count1, s->set_prevcount);
   1162  }
   1163 
   1164  // Find the command character in the table of commands.
   1165  // For CTRL-W we already got nchar when looking for a count.
   1166  if (s->ctrl_w) {
   1167    s->ca.nchar = s->c;
   1168    s->ca.cmdchar = Ctrl_W;
   1169  } else {
   1170    s->ca.cmdchar = s->c;
   1171  }
   1172 
   1173  s->idx = find_command(s->ca.cmdchar);
   1174 
   1175  if (s->idx < 0) {
   1176    // Not a known command: beep.
   1177    clearopbeep(&s->oa);
   1178    s->command_finished = true;
   1179    goto finish;
   1180  }
   1181 
   1182  if ((nv_cmds[s->idx].cmd_flags & NV_NCW) && check_text_or_curbuf_locked(&s->oa)) {
   1183    // this command is not allowed now
   1184    s->command_finished = true;
   1185    goto finish;
   1186  }
   1187 
   1188  // In Visual/Select mode, a few keys are handled in a special way.
   1189  if (VIsual_active && normal_handle_special_visual_command(s)) {
   1190    s->command_finished = true;
   1191    goto finish;
   1192  }
   1193 
   1194  if (curwin->w_p_rl && KeyTyped && !KeyStuffed
   1195      && (nv_cmds[s->idx].cmd_flags & NV_RL)) {
   1196    // Invert horizontal movements and operations.  Only when typed by the
   1197    // user directly, not when the result of a mapping or "x" translated
   1198    // to "dl".
   1199    normal_invert_horizontal(s);
   1200  }
   1201 
   1202  // Get an additional character if we need one.
   1203  if (normal_need_additional_char(s)) {
   1204    normal_get_additional_char(s);
   1205  }
   1206 
   1207  // Flush the showcmd characters onto the screen so we can see them while
   1208  // the command is being executed.  Only do this when the shown command was
   1209  // actually displayed, otherwise this will slow down a lot when executing
   1210  // mappings.
   1211  if (s->need_flushbuf) {
   1212    ui_flush();
   1213  }
   1214 
   1215  if (s->ca.cmdchar != K_IGNORE && s->ca.cmdchar != K_EVENT) {
   1216    did_cursorhold = false;
   1217  }
   1218 
   1219  State = MODE_NORMAL;
   1220 
   1221  if (s->ca.nchar == ESC || s->ca.extra_char == ESC) {
   1222    clearop(&s->oa);
   1223    s->command_finished = true;
   1224    goto finish;
   1225  }
   1226 
   1227  if (s->ca.cmdchar != K_IGNORE) {
   1228    msg_didout = false;        // don't scroll screen up for normal command
   1229    msg_col = 0;
   1230  }
   1231 
   1232  s->old_pos = curwin->w_cursor;           // remember where the cursor was
   1233 
   1234  // When 'keymodel' contains "startsel" some keys start Select/Visual
   1235  // mode.
   1236  if (!VIsual_active && km_startsel) {
   1237    if (nv_cmds[s->idx].cmd_flags & NV_SS) {
   1238      start_selection();
   1239      unshift_special(&s->ca);
   1240      s->idx = find_command(s->ca.cmdchar);
   1241      assert(s->idx >= 0);
   1242    } else if ((nv_cmds[s->idx].cmd_flags & NV_SSS)
   1243               && (mod_mask & MOD_MASK_SHIFT)) {
   1244      start_selection();
   1245      mod_mask &= ~MOD_MASK_SHIFT;
   1246    }
   1247  }
   1248 
   1249  // Execute the command!
   1250  // Call the command function found in the commands table.
   1251  s->ca.arg = nv_cmds[s->idx].cmd_arg;
   1252  (nv_cmds[s->idx].cmd_func)(&s->ca);
   1253 
   1254 finish:
   1255  normal_finish_command(s);
   1256  return 1;
   1257 }
   1258 
   1259 static void normal_check_stuff_buffer(NormalState *s)
   1260 {
   1261  if (stuff_empty()) {
   1262    did_check_timestamps = false;
   1263 
   1264    if (need_check_timestamps) {
   1265      check_timestamps(false);
   1266    }
   1267 
   1268    if (need_wait_return) {
   1269      // if wait_return still needed call it now
   1270      wait_return(false);
   1271    }
   1272  }
   1273 }
   1274 
   1275 static void normal_check_interrupt(NormalState *s)
   1276 {
   1277  // Reset "got_int" now that we got back to the main loop.  Except when
   1278  // inside a ":g/pat/cmd" command, then the "got_int" needs to abort
   1279  // the ":g" command.
   1280  // For ":g/pat/vi" we reset "got_int" when used once.  When used
   1281  // a second time we go back to Ex mode and abort the ":g" command.
   1282  if (got_int) {
   1283    if (s->noexmode && global_busy && !exmode_active
   1284        && s->previous_got_int) {
   1285      // Typed two CTRL-C in a row: go back to ex mode as if "Q" was
   1286      // used and keep "got_int" set, so that it aborts ":g".
   1287      exmode_active = true;
   1288      State = MODE_NORMAL;
   1289    } else if (!global_busy || !exmode_active) {
   1290      if (!quit_more) {
   1291        // flush all buffers
   1292        vgetc();
   1293      }
   1294      got_int = false;
   1295    }
   1296    s->previous_got_int = true;
   1297  } else {
   1298    s->previous_got_int = false;
   1299  }
   1300 }
   1301 
   1302 static void normal_check_window_scrolled(NormalState *s)
   1303 {
   1304  if (!finish_op) {
   1305    may_trigger_win_scrolled_resized();
   1306  }
   1307 }
   1308 
   1309 static void normal_check_cursor_moved(NormalState *s)
   1310 {
   1311  // Trigger CursorMoved if the cursor moved.
   1312  if (!finish_op && has_event(EVENT_CURSORMOVED)
   1313      && (last_cursormoved_win != curwin
   1314          || !equalpos(last_cursormoved, curwin->w_cursor))) {
   1315    apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
   1316    last_cursormoved_win = curwin;
   1317    last_cursormoved = curwin->w_cursor;
   1318  }
   1319 }
   1320 
   1321 static void normal_check_text_changed(NormalState *s)
   1322 {
   1323  // Trigger TextChanged if changedtick differs.
   1324  if (!finish_op && has_event(EVENT_TEXTCHANGED)
   1325      && curbuf->b_last_changedtick != buf_get_changedtick(curbuf)) {
   1326    apply_autocmds(EVENT_TEXTCHANGED, NULL, NULL, false, curbuf);
   1327    curbuf->b_last_changedtick = buf_get_changedtick(curbuf);
   1328  }
   1329 }
   1330 
   1331 static void normal_check_buffer_modified(NormalState *s)
   1332 {
   1333  // Trigger BufModified if b_modified changed
   1334  if (!finish_op && has_event(EVENT_BUFMODIFIEDSET)
   1335      && curbuf->b_changed_invalid == true) {
   1336    apply_autocmds(EVENT_BUFMODIFIEDSET, NULL, NULL, false, curbuf);
   1337    curbuf->b_changed_invalid = false;
   1338  }
   1339 }
   1340 
   1341 /// If nothing is pending and we are going to wait for the user to
   1342 /// type a character, trigger SafeState.
   1343 static void normal_check_safe_state(NormalState *s)
   1344 {
   1345  may_trigger_safestate(!op_pending() && restart_edit == 0);
   1346 }
   1347 
   1348 static void normal_check_folds(NormalState *s)
   1349 {
   1350  // Include a closed fold completely in the Visual area.
   1351  foldAdjustVisual();
   1352 
   1353  // When 'foldclose' is set, apply 'foldlevel' to folds that don't
   1354  // contain the cursor.
   1355  // When 'foldopen' is "all", open the fold(s) under the cursor.
   1356  // This may mark the window for redrawing.
   1357  if (hasAnyFolding(curwin) && !char_avail()) {
   1358    foldCheckClose();
   1359 
   1360    if (fdo_flags & kOptFdoFlagAll) {
   1361      foldOpenCursor();
   1362    }
   1363  }
   1364 }
   1365 
   1366 static void normal_redraw(NormalState *s)
   1367 {
   1368  // Before redrawing, make sure w_topline is correct, and w_leftcol
   1369  // if lines don't wrap, and w_skipcol if lines wrap.
   1370  update_topline(curwin);
   1371  validate_cursor(curwin);
   1372 
   1373  show_cursor_info_later(false);
   1374 
   1375  if (must_redraw) {
   1376    update_screen();
   1377  } else {
   1378    redraw_statuslines();
   1379    if (redraw_cmdline || clear_cmdline || redraw_mode) {
   1380      showmode();
   1381    }
   1382  }
   1383 
   1384  curbuf->b_last_used = time(NULL);
   1385 
   1386  // Display message after redraw.
   1387  if (keep_msg != NULL) {
   1388    char *const p = xstrdup(keep_msg);
   1389 
   1390    // msg_start() will set keep_msg to NULL, make a copy
   1391    // first.  Don't reset keep_msg, msg_attr_keep() uses it to
   1392    // check for duplicates.  Never put this message in
   1393    // history.
   1394    msg_hist_off = true;
   1395    msg(p, keep_msg_hl_id);
   1396    msg_hist_off = false;
   1397    xfree(p);
   1398  }
   1399 
   1400  // show fileinfo after redraw
   1401  if (need_fileinfo && !shortmess(SHM_FILEINFO)) {
   1402    fileinfo(false, true, false);
   1403    need_fileinfo = false;
   1404  }
   1405 
   1406  emsg_on_display = false;  // can delete error message now
   1407  did_emsg = false;
   1408  msg_didany = false;  // reset lines_left in msg_start()
   1409  may_clear_sb_text();  // clear scroll-back text on next msg
   1410 
   1411  setcursor();
   1412 }
   1413 
   1414 /// Function executed before each iteration of normal mode.
   1415 ///
   1416 /// @return:
   1417 ///           1 if the iteration should continue normally
   1418 ///          -1 if the iteration should be skipped
   1419 ///           0 if the main loop must exit
   1420 static int normal_check(VimState *state)
   1421 {
   1422  NormalState *s = (NormalState *)state;
   1423  normal_check_stuff_buffer(s);
   1424  normal_check_interrupt(s);
   1425 
   1426  // At the toplevel there is no exception handling.  Discard any that
   1427  // may be hanging around (e.g. from "interrupt" at the debug prompt).
   1428  if (did_throw && !ex_normal_busy) {
   1429    discard_current_exception();
   1430  }
   1431 
   1432  if (!exmode_active) {
   1433    msg_scroll = false;
   1434  }
   1435  quit_more = false;
   1436 
   1437  state_no_longer_safe(NULL);
   1438 
   1439  // If skip redraw is set (for ":" in wait_return()), don't redraw now.
   1440  // If there is nothing in the stuff_buffer or do_redraw is true,
   1441  // update cursor and redraw.
   1442  if (skip_redraw || exmode_active) {
   1443    skip_redraw = false;
   1444    setcursor();
   1445  } else if (do_redraw || stuff_empty()) {
   1446    terminal_check_refresh();
   1447 
   1448    // Ensure curwin->w_topline and curwin->w_leftcol are up to date
   1449    // before triggering a WinScrolled autocommand.
   1450    update_topline(curwin);
   1451    validate_cursor(curwin);
   1452 
   1453    normal_check_cursor_moved(s);
   1454    normal_check_text_changed(s);
   1455    normal_check_window_scrolled(s);
   1456    normal_check_buffer_modified(s);
   1457    normal_check_safe_state(s);
   1458 
   1459    // Updating diffs from changed() does not always work properly,
   1460    // esp. updating folds.  Do an update just before redrawing if
   1461    // needed.
   1462    if (curtab->tp_diff_update || curtab->tp_diff_invalid) {
   1463      ex_diffupdate(NULL);
   1464      curtab->tp_diff_update = false;
   1465    }
   1466 
   1467    // Scroll-binding for diff mode may have been postponed until
   1468    // here.  Avoids doing it for every change.
   1469    if (diff_need_scrollbind) {
   1470      check_scrollbind(0, 0);
   1471      diff_need_scrollbind = false;
   1472    }
   1473 
   1474    normal_check_folds(s);
   1475    normal_redraw(s);
   1476    do_redraw = false;
   1477 
   1478    // Now that we have drawn the first screen all the startup stuff
   1479    // has been done, close any file for startup messages.
   1480    if (time_fd != NULL) {
   1481      TIME_MSG("first screen update");
   1482      time_finish();
   1483    }
   1484    // After the first screen update may start triggering WinScrolled
   1485    // autocmd events.  Store all the scroll positions and sizes now.
   1486    may_make_initial_scroll_size_snapshot();
   1487  }
   1488 
   1489  // May perform garbage collection when waiting for a character, but
   1490  // only at the very toplevel.  Otherwise we may be using a List or
   1491  // Dict internally somewhere.
   1492  // "may_garbage_collect" is reset in vgetc() which is invoked through
   1493  // do_exmode() and normal_cmd().
   1494  may_garbage_collect = !s->cmdwin && !s->noexmode;
   1495 
   1496  // Update w_curswant if w_set_curswant has been set.
   1497  // Postponed until here to avoid computing w_virtcol too often.
   1498  update_curswant();
   1499 
   1500  if (exmode_active) {
   1501    if (s->noexmode) {
   1502      return 0;
   1503    }
   1504    do_exmode();
   1505    return -1;
   1506  }
   1507 
   1508  if (s->cmdwin && cmdwin_result != 0) {
   1509    // command-line window and cmdwin_result is set
   1510    return 0;
   1511  }
   1512 
   1513  normal_prepare(s);
   1514  return 1;
   1515 }
   1516 
   1517 /// Set v:count and v:count1 according to "cap".
   1518 /// Set v:prevcount only when "set_prevcount" is true.
   1519 static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount)
   1520 {
   1521  int64_t count = cap->count0;
   1522 
   1523  // multiply with cap->opcount the same way as above
   1524  if (cap->opcount != 0) {
   1525    count = cap->opcount * (count == 0 ? 1 : count);
   1526  }
   1527  set_vcount(count, count == 0 ? 1 : count, *set_prevcount);
   1528  *set_prevcount = false;    // only set v:prevcount once
   1529 }
   1530 
   1531 /// End Visual mode.
   1532 /// This function should ALWAYS be called to end Visual mode, except from
   1533 /// do_pending_operator().
   1534 void end_visual_mode(void)
   1535 {
   1536  VIsual_select_exclu_adj = false;
   1537  VIsual_active = false;
   1538  setmouse();
   1539  mouse_dragging = 0;
   1540 
   1541  // Save the current VIsual area for '< and '> marks, and "gv"
   1542  curbuf->b_visual.vi_mode = VIsual_mode;
   1543  curbuf->b_visual.vi_start = VIsual;
   1544  curbuf->b_visual.vi_end = curwin->w_cursor;
   1545  curbuf->b_visual.vi_curswant = curwin->w_curswant;
   1546  curbuf->b_visual_mode_eval = VIsual_mode;
   1547  if (!virtual_active(curwin)) {
   1548    curwin->w_cursor.coladd = 0;
   1549  }
   1550 
   1551  may_clear_cmdline();
   1552 
   1553  adjust_cursor_eol();
   1554  may_trigger_modechanged();
   1555 }
   1556 
   1557 /// Reset VIsual_active and VIsual_reselect.
   1558 void reset_VIsual_and_resel(void)
   1559 {
   1560  if (VIsual_active) {
   1561    end_visual_mode();
   1562    redraw_curbuf_later(UPD_INVERTED);  // delete the inversion later
   1563  }
   1564  VIsual_reselect = false;
   1565 }
   1566 
   1567 /// Reset VIsual_active and VIsual_reselect if it's set.
   1568 void reset_VIsual(void)
   1569 {
   1570  if (VIsual_active) {
   1571    end_visual_mode();
   1572    redraw_curbuf_later(UPD_INVERTED);  // delete the inversion later
   1573    VIsual_reselect = false;
   1574  }
   1575 }
   1576 
   1577 void restore_visual_mode(void)
   1578 {
   1579  if (VIsual_mode_orig != NUL) {
   1580    curbuf->b_visual.vi_mode = VIsual_mode_orig;
   1581    VIsual_mode_orig = NUL;
   1582  }
   1583 }
   1584 
   1585 /// Check for a balloon-eval special item to include when searching for an
   1586 /// identifier.  When "dir" is BACKWARD "ptr[-1]" must be valid!
   1587 ///
   1588 /// @return  true if the character at "*ptr" should be included.
   1589 ///
   1590 /// @param dir    the direction of searching, is either FORWARD or BACKWARD
   1591 /// @param *colp  is in/decremented if "ptr[-dir]" should also be included.
   1592 /// @param bnp    points to a counter for square brackets.
   1593 static bool find_is_eval_item(const char *const ptr, int *const colp, int *const bnp, const int dir)
   1594 {
   1595  // Accept everything inside [].
   1596  if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD)) {
   1597    *bnp += 1;
   1598  }
   1599  if (*bnp > 0) {
   1600    if ((*ptr == '[' && dir == BACKWARD) || (*ptr == ']' && dir == FORWARD)) {
   1601      *bnp -= 1;
   1602    }
   1603    return true;
   1604  }
   1605 
   1606  // skip over "s.var"
   1607  if (*ptr == '.') {
   1608    return true;
   1609  }
   1610 
   1611  // two-character item: s->var
   1612  if (ptr[dir == BACKWARD ? 0 : 1] == '>'
   1613      && ptr[dir == BACKWARD ? -1 : 0] == '-') {
   1614    *colp += dir;
   1615    return true;
   1616  }
   1617  return false;
   1618 }
   1619 
   1620 /// Find the identifier under or to the right of the cursor.
   1621 /// "find_type" can have one of three values:
   1622 /// FIND_IDENT:   find an identifier (keyword)
   1623 /// FIND_STRING:  find any non-white text
   1624 /// FIND_IDENT + FIND_STRING: find any non-white text, identifier preferred.
   1625 /// FIND_EVAL:  find text useful for C program debugging
   1626 ///
   1627 /// There are three steps:
   1628 /// 1. Search forward for the start of an identifier/text.  Doesn't move if
   1629 ///    already on one.
   1630 /// 2. Search backward for the start of this identifier/text.
   1631 ///    This doesn't match the real Vi but I like it a little better and it
   1632 ///    shouldn't bother anyone.
   1633 /// 3. Search forward to the end of this identifier/text.
   1634 ///    When FIND_IDENT isn't defined, we backup until a blank.
   1635 ///
   1636 /// @return  the length of the text, or zero if no text is found.
   1637 ///
   1638 /// If text is found, a pointer to the text is put in "*text".  This
   1639 /// points into the current buffer line and is not always NUL terminated.
   1640 size_t find_ident_under_cursor(char **text, int find_type)
   1641  FUNC_ATTR_NONNULL_ARG(1)
   1642 {
   1643  return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
   1644                           curwin->w_cursor.col, text, NULL, find_type);
   1645 }
   1646 
   1647 /// Like find_ident_under_cursor(), but for any window and any position.
   1648 /// However: Uses 'iskeyword' from the current window!.
   1649 ///
   1650 /// @param textcol  column where "text" starts, can be NULL
   1651 size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text, int *textcol,
   1652                         int find_type)
   1653  FUNC_ATTR_NONNULL_ARG(1, 4)
   1654 {
   1655  int col = 0;         // init to shut up GCC
   1656  int i;
   1657  int this_class = 0;
   1658  int prev_class;
   1659  int prevcol;
   1660  int bn = 0;                       // bracket nesting
   1661 
   1662  // if i == 0: try to find an identifier
   1663  // if i == 1: try to find any non-white text
   1664  char *ptr = ml_get_buf(wp->w_buffer, lnum);
   1665  for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; i++) {
   1666    // 1. skip to start of identifier/text
   1667    col = startcol;
   1668    while (ptr[col] != NUL) {
   1669      // Stop at a ']' to evaluate "a[x]".
   1670      if ((find_type & FIND_EVAL) && ptr[col] == ']') {
   1671        break;
   1672      }
   1673      this_class = mb_get_class(ptr + col);
   1674      if (this_class != 0 && (i == 1 || this_class != 1)) {
   1675        break;
   1676      }
   1677      col += utfc_ptr2len(ptr + col);
   1678    }
   1679 
   1680    // When starting on a ']' count it, so that we include the '['.
   1681    bn = ptr[col] == ']';
   1682 
   1683    // 2. Back up to start of identifier/text.
   1684    //
   1685    // Remember class of character under cursor.
   1686    if ((find_type & FIND_EVAL) && ptr[col] == ']') {
   1687      this_class = mb_get_class("a");
   1688    } else {
   1689      this_class = mb_get_class(ptr + col);
   1690    }
   1691    while (col > 0 && this_class != 0) {
   1692      prevcol = col - 1 - utf_head_off(ptr, ptr + col - 1);
   1693      prev_class = mb_get_class(ptr + prevcol);
   1694      if (this_class != prev_class
   1695          && (i == 0
   1696              || prev_class == 0
   1697              || (find_type & FIND_IDENT))
   1698          && (!(find_type & FIND_EVAL)
   1699              || prevcol == 0
   1700              || !find_is_eval_item(ptr + prevcol, &prevcol, &bn, BACKWARD))) {
   1701        break;
   1702      }
   1703      col = prevcol;
   1704    }
   1705 
   1706    // If we don't want just any old text, or we've found an
   1707    // identifier, stop searching.
   1708    this_class = MIN(this_class, 2);
   1709    if (!(find_type & FIND_STRING) || this_class == 2) {
   1710      break;
   1711    }
   1712  }
   1713 
   1714  if (ptr[col] == NUL || (i == 0 && this_class != 2)) {
   1715    // Didn't find an identifier or text.
   1716    if (find_type & FIND_STRING) {
   1717      emsg(_("E348: No string under cursor"));
   1718    } else {
   1719      emsg(_(e_noident));
   1720    }
   1721    return 0;
   1722  }
   1723  ptr += col;
   1724  *text = ptr;
   1725  if (textcol != NULL) {
   1726    *textcol = col;
   1727  }
   1728 
   1729  // 3. Find the end if the identifier/text.
   1730  bn = 0;
   1731  startcol -= col;
   1732  col = 0;
   1733  // Search for point of changing multibyte character class.
   1734  this_class = mb_get_class(ptr);
   1735  while (ptr[col] != NUL
   1736         && ((i == 0
   1737              ? mb_get_class(ptr + col) == this_class
   1738              : mb_get_class(ptr + col) != 0)
   1739             || ((find_type & FIND_EVAL)
   1740                 && col <= (int)startcol
   1741                 && find_is_eval_item(ptr + col, &col, &bn, FORWARD)))) {
   1742    col += utfc_ptr2len(ptr + col);
   1743  }
   1744 
   1745  assert(col >= 0);
   1746  return (size_t)col;
   1747 }
   1748 
   1749 /// Prepare for redo of a normal command.
   1750 static void prep_redo_cmd(cmdarg_T *cap)
   1751 {
   1752  prep_redo(cap->oap->regname, cap->count0,
   1753            NUL, cap->cmdchar, NUL, NUL, NUL);
   1754  if (cap->nchar_len > 0) {
   1755    AppendToRedobuff(cap->nchar_composing);
   1756  } else {
   1757    AppendCharToRedobuff(cap->nchar);
   1758  }
   1759 }
   1760 
   1761 /// Prepare for redo of any command.
   1762 /// Note that only the last argument can be a multi-byte char.
   1763 void prep_redo(int regname, int num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5)
   1764 {
   1765  prep_redo_num2(regname, num, cmd1, cmd2, 0, cmd3, cmd4, cmd5);
   1766 }
   1767 
   1768 /// Prepare for redo of any command with extra count after "cmd2".
   1769 void prep_redo_num2(int regname, int num1, int cmd1, int cmd2, int num2, int cmd3, int cmd4,
   1770                    int cmd5)
   1771 {
   1772  ResetRedobuff();
   1773  if (regname != 0) {   // yank from specified buffer
   1774    AppendCharToRedobuff('"');
   1775    AppendCharToRedobuff(regname);
   1776  }
   1777  if (num1 != 0) {
   1778    AppendNumberToRedobuff(num1);
   1779  }
   1780  if (cmd1 != NUL) {
   1781    AppendCharToRedobuff(cmd1);
   1782  }
   1783  if (cmd2 != NUL) {
   1784    AppendCharToRedobuff(cmd2);
   1785  }
   1786  if (num2 != 0) {
   1787    AppendNumberToRedobuff(num2);
   1788  }
   1789  if (cmd3 != NUL) {
   1790    AppendCharToRedobuff(cmd3);
   1791  }
   1792  if (cmd4 != NUL) {
   1793    AppendCharToRedobuff(cmd4);
   1794  }
   1795  if (cmd5 != NUL) {
   1796    AppendCharToRedobuff(cmd5);
   1797  }
   1798 }
   1799 
   1800 /// Check for operator active and clear it.
   1801 ///
   1802 /// Beep and return true if an operator was active.
   1803 static bool checkclearop(oparg_T *oap)
   1804 {
   1805  if (oap->op_type == OP_NOP) {
   1806    return false;
   1807  }
   1808  clearopbeep(oap);
   1809  return true;
   1810 }
   1811 
   1812 /// Check for operator or Visual active.  Clear active operator.
   1813 ///
   1814 /// Beep and return true if an operator or Visual was active.
   1815 static bool checkclearopq(oparg_T *oap)
   1816 {
   1817  if (oap->op_type == OP_NOP && !VIsual_active) {
   1818    return false;
   1819  }
   1820  clearopbeep(oap);
   1821  return true;
   1822 }
   1823 
   1824 void clearop(oparg_T *oap)
   1825 {
   1826  oap->op_type = OP_NOP;
   1827  oap->regname = 0;
   1828  oap->motion_force = NUL;
   1829  oap->use_reg_one = false;
   1830  motion_force = NUL;
   1831 }
   1832 
   1833 void clearopbeep(oparg_T *oap)
   1834 {
   1835  clearop(oap);
   1836  beep_flush();
   1837 }
   1838 
   1839 /// Remove the shift modifier from a special key.
   1840 static void unshift_special(cmdarg_T *cap)
   1841 {
   1842  switch (cap->cmdchar) {
   1843  case K_S_RIGHT:
   1844    cap->cmdchar = K_RIGHT; break;
   1845  case K_S_LEFT:
   1846    cap->cmdchar = K_LEFT; break;
   1847  case K_S_UP:
   1848    cap->cmdchar = K_UP; break;
   1849  case K_S_DOWN:
   1850    cap->cmdchar = K_DOWN; break;
   1851  case K_S_HOME:
   1852    cap->cmdchar = K_HOME; break;
   1853  case K_S_END:
   1854    cap->cmdchar = K_END; break;
   1855  }
   1856  cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask);
   1857 }
   1858 
   1859 /// If the mode is currently displayed clear the command line or update the
   1860 /// command displayed.
   1861 void may_clear_cmdline(void)
   1862 {
   1863  if (mode_displayed) {
   1864    // unshow visual mode later
   1865    clear_cmdline = true;
   1866  } else {
   1867    clear_showcmd();
   1868  }
   1869 }
   1870 
   1871 // Routines for displaying a partly typed command
   1872 static char old_showcmd_buf[SHOWCMD_BUFLEN];    // For push_showcmd()
   1873 static bool showcmd_is_clear = true;
   1874 static bool showcmd_visual = false;
   1875 
   1876 void clear_showcmd(void)
   1877 {
   1878  if (!p_sc) {
   1879    return;
   1880  }
   1881 
   1882  if (VIsual_active && !char_avail()) {
   1883    bool cursor_bot = lt(VIsual, curwin->w_cursor);
   1884    int lines;
   1885    colnr_T leftcol, rightcol;
   1886    linenr_T top, bot;
   1887 
   1888    // Show the size of the Visual area.
   1889    if (cursor_bot) {
   1890      top = VIsual.lnum;
   1891      bot = curwin->w_cursor.lnum;
   1892    } else {
   1893      top = curwin->w_cursor.lnum;
   1894      bot = VIsual.lnum;
   1895    }
   1896    // Include closed folds as a whole.
   1897    hasFolding(curwin, top, &top, NULL);
   1898    hasFolding(curwin, bot, NULL, &bot);
   1899    lines = bot - top + 1;
   1900 
   1901    if (VIsual_mode == Ctrl_V) {
   1902      char *const saved_sbr = p_sbr;
   1903      char *const saved_w_sbr = curwin->w_p_sbr;
   1904 
   1905      // Make 'sbr' empty for a moment to get the correct size.
   1906      p_sbr = empty_string_option;
   1907      curwin->w_p_sbr = empty_string_option;
   1908      getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
   1909      p_sbr = saved_sbr;
   1910      curwin->w_p_sbr = saved_w_sbr;
   1911      snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64,
   1912               (int64_t)lines, (int64_t)rightcol - leftcol + 1);
   1913    } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum) {
   1914      snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines);
   1915    } else {
   1916      char *s, *e;
   1917      int bytes = 0;
   1918      int chars = 0;
   1919 
   1920      if (cursor_bot) {
   1921        s = ml_get_pos(&VIsual);
   1922        e = get_cursor_pos_ptr();
   1923      } else {
   1924        s = get_cursor_pos_ptr();
   1925        e = ml_get_pos(&VIsual);
   1926      }
   1927      while ((*p_sel != 'e') ? s <= e : s < e) {
   1928        int l = utfc_ptr2len(s);
   1929        if (l == 0) {
   1930          bytes++;
   1931          chars++;
   1932          break;            // end of line
   1933        }
   1934        bytes += l;
   1935        chars++;
   1936        s += l;
   1937      }
   1938      if (bytes == chars) {
   1939        snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%d", chars);
   1940      } else {
   1941        snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%d-%d", chars, bytes);
   1942      }
   1943    }
   1944    int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN - 1 : SHOWCMD_COLS;
   1945    showcmd_buf[limit] = NUL;  // truncate
   1946    showcmd_visual = true;
   1947  } else {
   1948    showcmd_buf[0] = NUL;
   1949    showcmd_visual = false;
   1950 
   1951    // Don't actually display something if there is nothing to clear.
   1952    if (showcmd_is_clear) {
   1953      return;
   1954    }
   1955  }
   1956 
   1957  display_showcmd();
   1958 }
   1959 
   1960 /// Add 'c' to string of shown command chars.
   1961 ///
   1962 /// @return  true if output has been written (and setcursor() has been called).
   1963 bool add_to_showcmd(int c)
   1964 {
   1965  static int ignore[] = {
   1966    K_IGNORE,
   1967    K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
   1968    K_MIDDLEMOUSE, K_MIDDLEDRAG, K_MIDDLERELEASE,
   1969    K_RIGHTMOUSE, K_RIGHTDRAG, K_RIGHTRELEASE,
   1970    K_MOUSEDOWN, K_MOUSEUP, K_MOUSELEFT, K_MOUSERIGHT,
   1971    K_X1MOUSE, K_X1DRAG, K_X1RELEASE, K_X2MOUSE, K_X2DRAG, K_X2RELEASE,
   1972    K_EVENT,
   1973    0
   1974  };
   1975 
   1976  if (!p_sc || msg_silent != 0) {
   1977    return false;
   1978  }
   1979 
   1980  if (showcmd_visual) {
   1981    showcmd_buf[0] = NUL;
   1982    showcmd_visual = false;
   1983  }
   1984 
   1985  // Ignore keys that are scrollbar updates and mouse clicks
   1986  if (IS_SPECIAL(c)) {
   1987    for (int i = 0; ignore[i] != 0; i++) {
   1988      if (ignore[i] == c) {
   1989        return false;
   1990      }
   1991    }
   1992  }
   1993 
   1994  char *p;
   1995  char mbyte_buf[MB_MAXCHAR + 1];
   1996  if (c <= 0x7f || !vim_isprintc(c)) {
   1997    p = transchar(c);
   1998    if (*p == ' ') {
   1999      STRCPY(p, "<20>");
   2000    }
   2001  } else {
   2002    mbyte_buf[utf_char2bytes(c, mbyte_buf)] = NUL;
   2003    p = mbyte_buf;
   2004  }
   2005  size_t old_len = strlen(showcmd_buf);
   2006  size_t extra_len = strlen(p);
   2007  size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN - 1 : SHOWCMD_COLS;
   2008  if (old_len + extra_len > limit) {
   2009    size_t overflow = old_len + extra_len - limit;
   2010    memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
   2011  }
   2012  strcat(showcmd_buf, p);
   2013 
   2014  if (char_avail()) {
   2015    return false;
   2016  }
   2017 
   2018  display_showcmd();
   2019 
   2020  return true;
   2021 }
   2022 
   2023 void add_to_showcmd_c(int c)
   2024 {
   2025  add_to_showcmd(c);
   2026  setcursor();
   2027 }
   2028 
   2029 /// Delete 'len' characters from the end of the shown command.
   2030 static void del_from_showcmd(int len)
   2031 {
   2032  if (!p_sc) {
   2033    return;
   2034  }
   2035 
   2036  int old_len = (int)strlen(showcmd_buf);
   2037  len = MIN(len, old_len);
   2038  showcmd_buf[old_len - len] = NUL;
   2039 
   2040  if (!char_avail()) {
   2041    display_showcmd();
   2042  }
   2043 }
   2044 
   2045 /// push_showcmd() and pop_showcmd() are used when waiting for the user to type
   2046 /// something and there is a partial mapping.
   2047 void push_showcmd(void)
   2048 {
   2049  if (p_sc) {
   2050    STRCPY(old_showcmd_buf, showcmd_buf);
   2051  }
   2052 }
   2053 
   2054 void pop_showcmd(void)
   2055 {
   2056  if (!p_sc) {
   2057    return;
   2058  }
   2059 
   2060  STRCPY(showcmd_buf, old_showcmd_buf);
   2061 
   2062  display_showcmd();
   2063 }
   2064 
   2065 static void display_showcmd(void)
   2066 {
   2067  showcmd_is_clear = (showcmd_buf[0] == NUL);
   2068 
   2069  if (*p_sloc == 's') {
   2070    if (showcmd_is_clear) {
   2071      curwin->w_redr_status = true;
   2072    } else {
   2073      win_redr_status(curwin);
   2074      setcursor();  // put cursor back where it belongs
   2075    }
   2076    return;
   2077  }
   2078  if (*p_sloc == 't') {
   2079    if (showcmd_is_clear) {
   2080      redraw_tabline = true;
   2081    } else {
   2082      draw_tabline();
   2083      setcursor();  // put cursor back where it belongs
   2084    }
   2085    return;
   2086  }
   2087  // 'showcmdloc' is "last" or empty
   2088 
   2089  if (ui_has(kUIMessages)) {
   2090    MAXSIZE_TEMP_ARRAY(content, 1);
   2091    MAXSIZE_TEMP_ARRAY(chunk, 3);
   2092    if (!showcmd_is_clear) {
   2093      // placeholder for future highlight support
   2094      ADD_C(chunk, INTEGER_OBJ(0));
   2095      ADD_C(chunk, CSTR_AS_OBJ(showcmd_buf));
   2096      ADD_C(chunk, INTEGER_OBJ(0));
   2097      ADD_C(content, ARRAY_OBJ(chunk));
   2098    }
   2099    ui_call_msg_showcmd(content);
   2100    return;
   2101  }
   2102  if (p_ch == 0) {
   2103    return;
   2104  }
   2105 
   2106  msg_grid_validate();
   2107  int showcmd_row = Rows - 1;
   2108  grid_line_start(&msg_grid_adj, showcmd_row);
   2109 
   2110  int len = 0;
   2111  if (!showcmd_is_clear) {
   2112    len = grid_line_puts(sc_col, showcmd_buf, -1, HL_ATTR(HLF_MSG));
   2113  }
   2114 
   2115  // clear the rest of an old message by outputting up to SHOWCMD_COLS spaces
   2116  grid_line_puts(sc_col + len, (char *)"          " + len, -1, HL_ATTR(HLF_MSG));
   2117 
   2118  grid_line_flush();
   2119 }
   2120 
   2121 int get_vtopline(win_T *wp)
   2122 {
   2123  return plines_m_win_fill(wp, 1, wp->w_topline) - wp->w_topfill;
   2124 }
   2125 
   2126 /// When "check" is false, prepare for commands that scroll the window.
   2127 /// When "check" is true, take care of scroll-binding after the window has
   2128 /// scrolled.  Called from normal_cmd() and edit().
   2129 void do_check_scrollbind(bool check)
   2130 {
   2131  static win_T *old_curwin = NULL;
   2132  static linenr_T old_vtopline = 0;
   2133  static buf_T *old_buf = NULL;
   2134  static colnr_T old_leftcol = 0;
   2135 
   2136  int vtopline = get_vtopline(curwin);
   2137 
   2138  if (check && curwin->w_p_scb) {
   2139    // If a ":syncbind" command was just used, don't scroll, only reset
   2140    // the values.
   2141    if (did_syncbind) {
   2142      did_syncbind = false;
   2143    } else if (curwin == old_curwin) {
   2144      // Synchronize other windows, as necessary according to
   2145      // 'scrollbind'.  Don't do this after an ":edit" command, except
   2146      // when 'diff' is set.
   2147      if ((curwin->w_buffer == old_buf
   2148           || curwin->w_p_diff
   2149           )
   2150          && (vtopline != old_vtopline
   2151              || curwin->w_leftcol != old_leftcol)) {
   2152        check_scrollbind(vtopline - old_vtopline, curwin->w_leftcol - old_leftcol);
   2153      }
   2154    } else if (vim_strchr(p_sbo, 'j')) {  // jump flag set in 'scrollopt'
   2155      // When switching between windows, make sure that the relative
   2156      // vertical offset is valid for the new window.  The relative
   2157      // offset is invalid whenever another 'scrollbind' window has
   2158      // scrolled to a point that would force the current window to
   2159      // scroll past the beginning or end of its buffer.  When the
   2160      // resync is performed, some of the other 'scrollbind' windows may
   2161      // need to jump so that the current window's relative position is
   2162      // visible on-screen.
   2163      check_scrollbind(vtopline - curwin->w_scbind_pos, 0);
   2164    }
   2165    curwin->w_scbind_pos = vtopline;
   2166  }
   2167 
   2168  old_curwin = curwin;
   2169  old_vtopline = vtopline;
   2170  old_buf = curwin->w_buffer;
   2171  old_leftcol = curwin->w_leftcol;
   2172 }
   2173 
   2174 /// Synchronize any windows that have "scrollbind" set, based on the
   2175 /// number of rows by which the current window has changed
   2176 /// (1998-11-02 16:21:01  R. Edward Ralston <eralston@computer.org>)
   2177 void check_scrollbind(linenr_T vtopline_diff, int leftcol_diff)
   2178 {
   2179  win_T *old_curwin = curwin;
   2180  buf_T *old_curbuf = curbuf;
   2181  int old_VIsual_select = VIsual_select;
   2182  int old_VIsual_active = VIsual_active;
   2183  colnr_T tgt_leftcol = curwin->w_leftcol;
   2184 
   2185  // check 'scrollopt' string for vertical and horizontal scroll options
   2186  bool want_ver = old_curwin->w_p_diff
   2187                  || (vim_strchr(p_sbo, 'v') && vtopline_diff != 0);
   2188  bool want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || vtopline_diff != 0));
   2189 
   2190  // loop through the scrollbound windows and scroll accordingly
   2191  VIsual_select = VIsual_active = 0;
   2192  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   2193    curwin = wp;
   2194    curbuf = curwin->w_buffer;
   2195    // skip original window and windows with 'noscrollbind'
   2196    if (curwin == old_curwin || !curwin->w_p_scb) {
   2197      continue;
   2198    }
   2199 
   2200    // do the vertical scroll
   2201    if (want_ver) {
   2202      if (old_curwin->w_p_diff && curwin->w_p_diff) {
   2203        diff_set_topline(old_curwin, curwin);
   2204      } else {
   2205        curwin->w_scbind_pos += vtopline_diff;
   2206        int curr_vtopline = get_vtopline(curwin);
   2207 
   2208        // Perf: reuse curr_vtopline to reduce the time in plines_m_win_fill().
   2209        // Equivalent to:
   2210        //   int max_vtopline = plines_m_win_fill(curwin, 1, curbuf->b_ml.ml_line_count);
   2211        int max_vtopline = curr_vtopline + curwin->w_topfill
   2212                           + plines_m_win_fill(curwin, curwin->w_topline + 1,
   2213                                               curbuf->b_ml.ml_line_count);
   2214 
   2215        int new_vtopline = MAX(MIN((linenr_T)curwin->w_scbind_pos, max_vtopline), 1);
   2216 
   2217        int y = new_vtopline - curr_vtopline;
   2218        if (y > 0) {
   2219          scrollup(curwin, y, false);
   2220        } else {
   2221          scrolldown(curwin, -y, false);
   2222        }
   2223      }
   2224 
   2225      redraw_later(curwin, UPD_VALID);
   2226      cursor_correct(curwin);
   2227      curwin->w_redr_status = true;
   2228    }
   2229 
   2230    // do the horizontal scroll
   2231    if (want_hor) {
   2232      set_leftcol(tgt_leftcol);
   2233    }
   2234  }
   2235 
   2236  // reset current-window
   2237  VIsual_select = old_VIsual_select;
   2238  VIsual_active = old_VIsual_active;
   2239  curwin = old_curwin;
   2240  curbuf = old_curbuf;
   2241 }
   2242 
   2243 /// Command character that's ignored.
   2244 /// Used for CTRL-Q and CTRL-S to avoid problems with terminals that use
   2245 /// xon/xoff.
   2246 static void nv_ignore(cmdarg_T *cap)
   2247 {
   2248  cap->retval |= CA_COMMAND_BUSY;       // don't call edit() now
   2249 }
   2250 
   2251 /// Command character that doesn't do anything, but unlike nv_ignore() does
   2252 /// start edit().  Used for "startinsert" executed while starting up.
   2253 static void nv_nop(cmdarg_T *cap)
   2254 {
   2255 }
   2256 
   2257 /// Command character doesn't exist.
   2258 static void nv_error(cmdarg_T *cap)
   2259 {
   2260  clearopbeep(cap->oap);
   2261 }
   2262 
   2263 /// <Help> and <F1> commands.
   2264 static void nv_help(cmdarg_T *cap)
   2265 {
   2266  if (!checkclearopq(cap->oap)) {
   2267    ex_help(NULL);
   2268  }
   2269 }
   2270 
   2271 /// CTRL-A and CTRL-X: Add or subtract from letter or number under cursor.
   2272 static void nv_addsub(cmdarg_T *cap)
   2273 {
   2274  if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
   2275    clearopbeep(cap->oap);
   2276  } else if (!VIsual_active && cap->oap->op_type == OP_NOP) {
   2277    prep_redo_cmd(cap);
   2278    cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB;
   2279    op_addsub(cap->oap, cap->count1, cap->arg);
   2280    cap->oap->op_type = OP_NOP;
   2281  } else if (VIsual_active) {
   2282    nv_operator(cap);
   2283  } else {
   2284    clearop(cap->oap);
   2285  }
   2286 }
   2287 
   2288 /// CTRL-F, CTRL-B, etc: Scroll page up or down.
   2289 static void nv_page(cmdarg_T *cap)
   2290 {
   2291  if (checkclearop(cap->oap)) {
   2292    return;
   2293  }
   2294 
   2295  if (mod_mask & MOD_MASK_CTRL) {
   2296    // <C-PageUp>: tab page back; <C-PageDown>: tab page forward
   2297    if (cap->arg == BACKWARD) {
   2298      goto_tabpage(-cap->count1);
   2299    } else {
   2300      goto_tabpage(cap->count0);
   2301    }
   2302  } else {
   2303    pagescroll(cap->arg, cap->count1, false);
   2304  }
   2305 }
   2306 
   2307 /// Implementation of "gd" and "gD" command.
   2308 ///
   2309 /// @param thisblock  1 for "1gd" and "1gD"
   2310 static void nv_gd(oparg_T *oap, int nchar, int thisblock)
   2311 {
   2312  size_t len;
   2313  char *ptr;
   2314  if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
   2315      || !find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)) {
   2316    clearopbeep(oap);
   2317    return;
   2318  }
   2319 
   2320  if ((fdo_flags & kOptFdoFlagSearch) && KeyTyped && oap->op_type == OP_NOP) {
   2321    foldOpenCursor();
   2322  }
   2323  // clear any search statistics
   2324  if (messaging() && !msg_silent && !shortmess(SHM_SEARCHCOUNT)) {
   2325    clear_cmdline = true;
   2326  }
   2327 }
   2328 
   2329 /// @return true if line[offset] is not inside a C-style comment or string,
   2330 ///         false otherwise.
   2331 static bool is_ident(const char *line, int offset)
   2332 {
   2333  bool incomment = false;
   2334  int instring = 0;
   2335  int prev = 0;
   2336 
   2337  for (int i = 0; i < offset && line[i] != NUL; i++) {
   2338    if (instring != 0) {
   2339      if (prev != '\\' && (uint8_t)line[i] == instring) {
   2340        instring = 0;
   2341      }
   2342    } else if ((line[i] == '"' || line[i] == '\'') && !incomment) {
   2343      instring = (uint8_t)line[i];
   2344    } else {
   2345      if (incomment) {
   2346        if (prev == '*' && line[i] == '/') {
   2347          incomment = false;
   2348        }
   2349      } else if (prev == '/' && line[i] == '*') {
   2350        incomment = true;
   2351      } else if (prev == '/' && line[i] == '/') {
   2352        return false;
   2353      }
   2354    }
   2355 
   2356    prev = (uint8_t)line[i];
   2357  }
   2358 
   2359  return incomment == false && instring == 0;
   2360 }
   2361 
   2362 /// Search for variable declaration of "ptr[len]".
   2363 /// When "locally" is true in the current function ("gd"), otherwise in the
   2364 /// current file ("gD").
   2365 ///
   2366 /// @param thisblock  when true check the {} block scope.
   2367 /// @param flags_arg  flags passed to searchit()
   2368 ///
   2369 /// @return           fail when not found.
   2370 bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_arg)
   2371 {
   2372  pos_T par_pos;
   2373  pos_T found_pos;
   2374  bool t;
   2375  bool retval = true;
   2376  bool incll;
   2377  int searchflags = flags_arg;
   2378 
   2379  size_t patsize = len + 7;
   2380  char *pat = xmalloc(patsize);
   2381 
   2382  // Put "\V" before the pattern to avoid that the special meaning of "."
   2383  // and "~" causes trouble.
   2384  assert(patsize <= INT_MAX);
   2385  size_t patlen = (size_t)snprintf(pat, patsize,
   2386                                   vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
   2387                                   (int)len, ptr);
   2388  pos_T old_pos = curwin->w_cursor;
   2389  bool save_p_ws = p_ws;
   2390  bool save_p_scs = p_scs;
   2391  p_ws = false;         // don't wrap around end of file now
   2392  p_scs = false;        // don't switch ignorecase off now
   2393 
   2394  // With "gD" go to line 1.
   2395  // With "gd" Search back for the start of the current function, then go
   2396  // back until a blank line.  If this fails go to line 1.
   2397  if (!locally || !findpar(&incll, BACKWARD, 1, '{', false)) {
   2398    setpcmark();                        // Set in findpar() otherwise
   2399    curwin->w_cursor.lnum = 1;
   2400    par_pos = curwin->w_cursor;
   2401  } else {
   2402    par_pos = curwin->w_cursor;
   2403    while (curwin->w_cursor.lnum > 1
   2404           && *skipwhite(get_cursor_line_ptr()) != NUL) {
   2405      curwin->w_cursor.lnum--;
   2406    }
   2407  }
   2408  curwin->w_cursor.col = 0;
   2409 
   2410  // Search forward for the identifier, ignore comment lines.
   2411  clearpos(&found_pos);
   2412  while (true) {
   2413    t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
   2414                 pat, patlen, 1, searchflags, RE_LAST, NULL);
   2415    if (curwin->w_cursor.lnum >= old_pos.lnum) {
   2416      t = false;         // match after start is failure too
   2417    }
   2418 
   2419    if (thisblock && t != false) {
   2420      const int64_t maxtravel = old_pos.lnum - curwin->w_cursor.lnum + 1;
   2421      const pos_T *pos = findmatchlimit(NULL, '}', FM_FORWARD, maxtravel);
   2422 
   2423      // Check that the block the match is in doesn't end before the
   2424      // position where we started the search from.
   2425      if (pos != NULL && pos->lnum < old_pos.lnum) {
   2426        // There can't be a useful match before the end of this block.
   2427        // Skip to the end
   2428        curwin->w_cursor = *pos;
   2429        continue;
   2430      }
   2431    }
   2432 
   2433    if (t == false) {
   2434      // If we previously found a valid position, use it.
   2435      if (found_pos.lnum != 0) {
   2436        curwin->w_cursor = found_pos;
   2437        t = true;
   2438      }
   2439      break;
   2440    }
   2441    if (get_leader_len(get_cursor_line_ptr(), NULL, false, true) > 0) {
   2442      // Ignore this line, continue at start of next line.
   2443      curwin->w_cursor.lnum++;
   2444      curwin->w_cursor.col = 0;
   2445      continue;
   2446    }
   2447    bool valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col);
   2448 
   2449    // If the current position is not a valid identifier and a previous match is
   2450    // present, favor that one instead.
   2451    if (!valid && found_pos.lnum != 0) {
   2452      curwin->w_cursor = found_pos;
   2453      break;
   2454    }
   2455    // global search: use first match found
   2456    if (valid && !locally) {
   2457      break;
   2458    }
   2459    if (valid && curwin->w_cursor.lnum >= par_pos.lnum) {
   2460      // If we previously found a valid position, use it.
   2461      if (found_pos.lnum != 0) {
   2462        curwin->w_cursor = found_pos;
   2463      }
   2464      break;
   2465    }
   2466 
   2467    // For finding a local variable and the match is before the "{" or
   2468    // inside a comment, continue searching.  For K&R style function
   2469    // declarations this skips the function header without types.
   2470    if (!valid) {
   2471      clearpos(&found_pos);
   2472    } else {
   2473      found_pos = curwin->w_cursor;
   2474    }
   2475    // Remove SEARCH_START from flags to avoid getting stuck at one position.
   2476    searchflags &= ~SEARCH_START;
   2477  }
   2478 
   2479  if (t == false) {
   2480    retval = false;
   2481    curwin->w_cursor = old_pos;
   2482  } else {
   2483    curwin->w_set_curswant = true;
   2484    // "n" searches forward now
   2485    reset_search_dir();
   2486  }
   2487 
   2488  xfree(pat);
   2489  p_ws = save_p_ws;
   2490  p_scs = save_p_scs;
   2491 
   2492  return retval;
   2493 }
   2494 
   2495 /// Move 'dist' lines in direction 'dir', counting lines by *screen*
   2496 /// lines rather than lines in the file.
   2497 /// 'dist' must be positive.
   2498 ///
   2499 /// @return  true if able to move cursor, false otherwise.
   2500 bool nv_screengo(oparg_T *oap, int dir, int dist, bool skip_conceal)
   2501 {
   2502  int linelen = linetabsize(curwin, curwin->w_cursor.lnum);
   2503  bool retval = true;
   2504  bool atend = false;
   2505  int col_off1;                 // margin offset for first screen line
   2506  int col_off2;                 // margin offset for wrapped screen line
   2507  int width1;                   // text width for first screen line
   2508  int width2;                   // text width for wrapped screen line
   2509 
   2510  oap->motion_type = kMTCharWise;
   2511  oap->inclusive = (curwin->w_curswant == MAXCOL);
   2512 
   2513  col_off1 = win_col_off(curwin);
   2514  col_off2 = col_off1 - win_col_off2(curwin);
   2515  width1 = curwin->w_view_width - col_off1;
   2516  width2 = curwin->w_view_width - col_off2;
   2517 
   2518  if (width2 == 0) {
   2519    width2 = 1;  // Avoid divide by zero.
   2520  }
   2521 
   2522  if (curwin->w_view_width != 0) {
   2523    int n;
   2524    // Instead of sticking at the last character of the buffer line we
   2525    // try to stick in the last column of the screen.
   2526    if (curwin->w_curswant == MAXCOL) {
   2527      atend = true;
   2528      validate_virtcol(curwin);
   2529      if (width1 <= 0) {
   2530        curwin->w_curswant = 0;
   2531      } else {
   2532        curwin->w_curswant = width1 - 1;
   2533        if (curwin->w_virtcol > curwin->w_curswant) {
   2534          curwin->w_curswant += ((curwin->w_virtcol
   2535                                  - curwin->w_curswant -
   2536                                  1) / width2 + 1) * width2;
   2537        }
   2538      }
   2539    } else {
   2540      if (linelen > width1) {
   2541        n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
   2542      } else {
   2543        n = width1;
   2544      }
   2545      curwin->w_curswant = MIN(curwin->w_curswant, n - 1);
   2546    }
   2547 
   2548    while (dist--) {
   2549      if (dir == BACKWARD) {
   2550        if (curwin->w_curswant >= width1
   2551            && !hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
   2552          // Move back within the line. This can give a negative value
   2553          // for w_curswant if width1 < width2 (with cpoptions+=n),
   2554          // which will get clipped to column 0.
   2555          curwin->w_curswant -= width2;
   2556        } else {
   2557          // to previous line
   2558          if (curwin->w_cursor.lnum <= 1) {
   2559            retval = false;
   2560            break;
   2561          }
   2562          cursor_up_inner(curwin, 1, skip_conceal);
   2563 
   2564          linelen = linetabsize(curwin, curwin->w_cursor.lnum);
   2565          if (linelen > width1) {
   2566            int w = (((linelen - width1 - 1) / width2) + 1) * width2;
   2567            assert(w <= 0 || curwin->w_curswant <= INT_MAX - w);
   2568            curwin->w_curswant += w;
   2569          }
   2570        }
   2571      } else {  // dir == FORWARD
   2572        if (linelen > width1) {
   2573          n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1;
   2574        } else {
   2575          n = width1;
   2576        }
   2577        if (curwin->w_curswant + width2 < (colnr_T)n
   2578            && !hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
   2579          // move forward within line
   2580          curwin->w_curswant += width2;
   2581        } else {
   2582          // to next line
   2583          if (curwin->w_cursor.lnum >= curwin->w_buffer->b_ml.ml_line_count) {
   2584            retval = false;
   2585            break;
   2586          }
   2587          cursor_down_inner(curwin, 1, skip_conceal);
   2588          curwin->w_curswant %= width2;
   2589 
   2590          // Check if the cursor has moved below the number display
   2591          // when width1 < width2 (with cpoptions+=n). Subtract width2
   2592          // to get a negative value for w_curswant, which will get
   2593          // clipped to column 0.
   2594          if (curwin->w_curswant >= width1) {
   2595            curwin->w_curswant -= width2;
   2596          }
   2597          linelen = linetabsize(curwin, curwin->w_cursor.lnum);
   2598        }
   2599      }
   2600    }
   2601  }
   2602 
   2603  if (virtual_active(curwin) && atend) {
   2604    coladvance(curwin, MAXCOL);
   2605  } else {
   2606    coladvance(curwin, curwin->w_curswant);
   2607  }
   2608 
   2609  if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
   2610    // Check for landing on a character that got split at the end of the
   2611    // last line.  We want to advance a screenline, not end up in the same
   2612    // screenline or move two screenlines.
   2613    validate_virtcol(curwin);
   2614    colnr_T virtcol = curwin->w_virtcol;
   2615    if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL) {
   2616      virtcol -= vim_strsize(get_showbreak_value(curwin));
   2617    }
   2618 
   2619    int c = utf_ptr2char(get_cursor_pos_ptr());
   2620    if (dir == FORWARD && virtcol < curwin->w_curswant
   2621        && (curwin->w_curswant <= (colnr_T)width1)
   2622        && !vim_isprintc(c) && c > 255) {
   2623      oneright();
   2624    }
   2625 
   2626    if (virtcol > curwin->w_curswant
   2627        && (curwin->w_curswant < (colnr_T)width1
   2628            ? (curwin->w_curswant > (colnr_T)width1 / 2)
   2629            : ((curwin->w_curswant - width1) % width2
   2630               > (colnr_T)width2 / 2))) {
   2631      curwin->w_cursor.col--;
   2632    }
   2633  }
   2634 
   2635  if (atend) {
   2636    curwin->w_curswant = MAXCOL;            // stick in the last column
   2637  }
   2638  adjust_skipcol();
   2639 
   2640  return retval;
   2641 }
   2642 
   2643 /// Handle CTRL-E and CTRL-Y commands: scroll a line up or down.
   2644 /// cap->arg must be true for CTRL-E.
   2645 void nv_scroll_line(cmdarg_T *cap)
   2646 {
   2647  if (!checkclearop(cap->oap)) {
   2648    scroll_redraw(cap->arg, cap->count1);
   2649  }
   2650 }
   2651 
   2652 /// Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh',
   2653 /// 'z<Left>', and 'z<Right>' commands accept a count after 'z'.
   2654 /// @return  true to process the 'z' command and false to skip it.
   2655 static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg)
   2656 {
   2657  int nchar = *nchar_arg;
   2658 
   2659  // "z123{nchar}": edit the count before obtaining {nchar}
   2660  if (checkclearop(cap->oap)) {
   2661    return false;
   2662  }
   2663  int n = nchar - '0';
   2664 
   2665  while (true) {
   2666    no_mapping++;
   2667    allow_keys++;         // no mapping for nchar, but allow key codes
   2668    nchar = plain_vgetc();
   2669    LANGMAP_ADJUST(nchar, true);
   2670    no_mapping--;
   2671    allow_keys--;
   2672    add_to_showcmd(nchar);
   2673 
   2674    if (nchar == K_DEL || nchar == K_KDEL) {
   2675      n /= 10;
   2676    } else if (ascii_isdigit(nchar)) {
   2677      if (vim_append_digit_int(&n, nchar - '0') == FAIL) {
   2678        clearopbeep(cap->oap);
   2679        break;
   2680      }
   2681    } else if (nchar == CAR) {
   2682      win_setheight(n);
   2683      break;
   2684    } else if (nchar == 'l'
   2685               || nchar == 'h'
   2686               || nchar == K_LEFT
   2687               || nchar == K_RIGHT) {
   2688      cap->count1 = n ? n * cap->count1 : cap->count1;
   2689      *nchar_arg = nchar;
   2690      return true;
   2691    } else {
   2692      clearopbeep(cap->oap);
   2693      break;
   2694    }
   2695  }
   2696  cap->oap->op_type = OP_NOP;
   2697  return false;
   2698 }
   2699 
   2700 /// "zug" and "zuw": undo "zg" and "zw"
   2701 /// "zg": add good word to word list
   2702 /// "zw": add wrong word to word list
   2703 /// "zG": add good word to temp word list
   2704 /// "zW": add wrong word to temp word list
   2705 static int nv_zg_zw(cmdarg_T *cap, int nchar)
   2706 {
   2707  bool undo = false;
   2708 
   2709  if (nchar == 'u') {
   2710    no_mapping++;
   2711    allow_keys++;               // no mapping for nchar, but allow key codes
   2712    nchar = plain_vgetc();
   2713    LANGMAP_ADJUST(nchar, true);
   2714    no_mapping--;
   2715    allow_keys--;
   2716    add_to_showcmd(nchar);
   2717 
   2718    if (vim_strchr("gGwW", nchar) == NULL) {
   2719      clearopbeep(cap->oap);
   2720      return OK;
   2721    }
   2722    undo = true;
   2723  }
   2724 
   2725  if (checkclearop(cap->oap)) {
   2726    return OK;
   2727  }
   2728  char *ptr = NULL;
   2729  size_t len;
   2730  if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
   2731    return FAIL;
   2732  }
   2733  if (ptr == NULL) {
   2734    pos_T pos = curwin->w_cursor;
   2735 
   2736    // Find bad word under the cursor.  When 'spell' is
   2737    // off this fails and find_ident_under_cursor() is
   2738    // used below.
   2739    emsg_off++;
   2740    len = spell_move_to(curwin, FORWARD, SMT_ALL, true, NULL);
   2741    emsg_off--;
   2742    if (len != 0 && curwin->w_cursor.col <= pos.col) {
   2743      ptr = ml_get_pos(&curwin->w_cursor);
   2744    }
   2745    curwin->w_cursor = pos;
   2746  }
   2747 
   2748  if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
   2749    return FAIL;
   2750  }
   2751  assert(len <= INT_MAX);
   2752  spell_add_word(ptr, (int)len,
   2753                 nchar == 'w' || nchar == 'W' ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
   2754                 (nchar == 'G' || nchar == 'W') ? 0 : cap->count1,
   2755                 undo);
   2756 
   2757  return OK;
   2758 }
   2759 
   2760 /// Commands that start with "z".
   2761 static void nv_zet(cmdarg_T *cap)
   2762 {
   2763  colnr_T col;
   2764  int nchar = cap->nchar;
   2765  int old_fdl = (int)curwin->w_p_fdl;
   2766  int old_fen = curwin->w_p_fen;
   2767 
   2768  int siso = get_sidescrolloff_value(curwin);
   2769 
   2770  if (ascii_isdigit(nchar) && !nv_z_get_count(cap, &nchar)) {
   2771    return;
   2772  }
   2773 
   2774  // "zf" and "zF" are always an operator, "zd", "zo", "zO", "zc"
   2775  // and "zC" only in Visual mode.  "zj" and "zk" are motion
   2776  // commands.
   2777  if (cap->nchar != 'f' && cap->nchar != 'F'
   2778      && !(VIsual_active && vim_strchr("dcCoO", cap->nchar))
   2779      && cap->nchar != 'j' && cap->nchar != 'k'
   2780      && checkclearop(cap->oap)) {
   2781    return;
   2782  }
   2783 
   2784  // For "z+", "z<CR>", "zt", "z.", "zz", "z^", "z-", "zb":
   2785  // If line number given, set cursor.
   2786  if ((vim_strchr("+\r\nt.z^-b", nchar) != NULL)
   2787      && cap->count0
   2788      && cap->count0 != curwin->w_cursor.lnum) {
   2789    setpcmark();
   2790    if (cap->count0 > curbuf->b_ml.ml_line_count) {
   2791      curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
   2792    } else {
   2793      curwin->w_cursor.lnum = cap->count0;
   2794    }
   2795    check_cursor_col(curwin);
   2796  }
   2797 
   2798  switch (nchar) {
   2799  // "z+", "z<CR>" and "zt": put cursor at top of screen
   2800  case '+':
   2801    if (cap->count0 == 0) {
   2802      // No count given: put cursor at the line below screen
   2803      validate_botline_win(curwin);  // make sure w_botline is valid
   2804      curwin->w_cursor.lnum = MIN(curwin->w_botline, curbuf->b_ml.ml_line_count);
   2805    }
   2806    FALLTHROUGH;
   2807  case NL:
   2808  case CAR:
   2809  case K_KENTER:
   2810    beginline(BL_WHITE | BL_FIX);
   2811    FALLTHROUGH;
   2812 
   2813  case 't':
   2814    scroll_cursor_top(curwin, 0, true);
   2815    redraw_later(curwin, UPD_VALID);
   2816    set_fraction(curwin);
   2817    break;
   2818 
   2819  // "z." and "zz": put cursor in middle of screen
   2820  case '.':
   2821    beginline(BL_WHITE | BL_FIX);
   2822    FALLTHROUGH;
   2823 
   2824  case 'z':
   2825    scroll_cursor_halfway(curwin, true, false);
   2826    redraw_later(curwin, UPD_VALID);
   2827    set_fraction(curwin);
   2828    break;
   2829 
   2830  // "z^", "z-" and "zb": put cursor at bottom of screen
   2831  case '^':     // Strange Vi behavior: <count>z^ finds line at top of window
   2832                // when <count> is at bottom of window, and puts that one at
   2833                // bottom of window.
   2834    if (cap->count0 != 0) {
   2835      scroll_cursor_bot(curwin, 0, true);
   2836      curwin->w_cursor.lnum = curwin->w_topline;
   2837    } else if (curwin->w_topline == 1) {
   2838      curwin->w_cursor.lnum = 1;
   2839    } else {
   2840      curwin->w_cursor.lnum = curwin->w_topline - 1;
   2841    }
   2842    FALLTHROUGH;
   2843  case '-':
   2844    beginline(BL_WHITE | BL_FIX);
   2845    FALLTHROUGH;
   2846 
   2847  case 'b':
   2848    scroll_cursor_bot(curwin, 0, true);
   2849    redraw_later(curwin, UPD_VALID);
   2850    set_fraction(curwin);
   2851    break;
   2852 
   2853  // "zH" - scroll screen right half-page
   2854  case 'H':
   2855    cap->count1 *= curwin->w_view_width / 2;
   2856    FALLTHROUGH;
   2857 
   2858  // "zh" - scroll screen to the right
   2859  case 'h':
   2860  case K_LEFT:
   2861    if (!curwin->w_p_wrap) {
   2862      set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol
   2863                  ? 0 : curwin->w_leftcol - (colnr_T)cap->count1);
   2864    }
   2865    break;
   2866 
   2867  // "zL" - scroll window left half-page
   2868  case 'L':
   2869    cap->count1 *= curwin->w_view_width / 2;
   2870    FALLTHROUGH;
   2871 
   2872  // "zl" - scroll window to the left if not wrapping
   2873  case 'l':
   2874  case K_RIGHT:
   2875    if (!curwin->w_p_wrap) {
   2876      set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1);
   2877    }
   2878    break;
   2879 
   2880  // "zs" - scroll screen, cursor at the start
   2881  case 's':
   2882    if (!curwin->w_p_wrap) {
   2883      if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
   2884        col = 0;                        // like the cursor is in col 0
   2885      } else {
   2886        getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
   2887      }
   2888      if (col > siso) {
   2889        col -= siso;
   2890      } else {
   2891        col = 0;
   2892      }
   2893      if (curwin->w_leftcol != col) {
   2894        curwin->w_leftcol = col;
   2895        redraw_later(curwin, UPD_NOT_VALID);
   2896      }
   2897    }
   2898    break;
   2899 
   2900  // "ze" - scroll screen, cursor at the end
   2901  case 'e':
   2902    if (!curwin->w_p_wrap) {
   2903      if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
   2904        col = 0;                        // like the cursor is in col 0
   2905      } else {
   2906        getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
   2907      }
   2908      int n = curwin->w_view_width - win_col_off(curwin);
   2909      if (col + siso < n) {
   2910        col = 0;
   2911      } else {
   2912        col = col + siso - n + 1;
   2913      }
   2914      if (curwin->w_leftcol != col) {
   2915        curwin->w_leftcol = col;
   2916        redraw_later(curwin, UPD_NOT_VALID);
   2917      }
   2918    }
   2919    break;
   2920 
   2921  // "zp", "zP" in block mode put without adding trailing spaces
   2922  case 'P':
   2923  case 'p':
   2924    nv_put(cap);
   2925    break;
   2926  // "zy" Yank without trailing spaces
   2927  case 'y':
   2928    nv_operator(cap);
   2929    break;
   2930 
   2931  // "zF": create fold command
   2932  // "zf": create fold operator
   2933  case 'F':
   2934  case 'f':
   2935    if (foldManualAllowed(true)) {
   2936      cap->nchar = 'f';
   2937      nv_operator(cap);
   2938      curwin->w_p_fen = true;
   2939 
   2940      // "zF" is like "zfzf"
   2941      if (nchar == 'F' && cap->oap->op_type == OP_FOLD) {
   2942        nv_operator(cap);
   2943        finish_op = true;
   2944      }
   2945    } else {
   2946      clearopbeep(cap->oap);
   2947    }
   2948    break;
   2949 
   2950  // "zd": delete fold at cursor
   2951  // "zD": delete fold at cursor recursively
   2952  case 'd':
   2953  case 'D':
   2954    if (foldManualAllowed(false)) {
   2955      if (VIsual_active) {
   2956        nv_operator(cap);
   2957      } else {
   2958        deleteFold(curwin, curwin->w_cursor.lnum,
   2959                   curwin->w_cursor.lnum, nchar == 'D', false);
   2960      }
   2961    }
   2962    break;
   2963 
   2964  // "zE": erase all folds
   2965  case 'E':
   2966    if (foldmethodIsManual(curwin)) {
   2967      clearFolding(curwin);
   2968      changed_window_setting(curwin);
   2969    } else if (foldmethodIsMarker(curwin)) {
   2970      deleteFold(curwin, 1, curbuf->b_ml.ml_line_count, true, false);
   2971    } else {
   2972      emsg(_("E352: Cannot erase folds with current 'foldmethod'"));
   2973    }
   2974    break;
   2975 
   2976  // "zn": fold none: reset 'foldenable'
   2977  case 'n':
   2978    curwin->w_p_fen = false;
   2979    break;
   2980 
   2981  // "zN": fold Normal: set 'foldenable'
   2982  case 'N':
   2983    curwin->w_p_fen = true;
   2984    break;
   2985 
   2986  // "zi": invert folding: toggle 'foldenable'
   2987  case 'i':
   2988    curwin->w_p_fen = !curwin->w_p_fen;
   2989    break;
   2990 
   2991  // "za": open closed fold or close open fold at cursor
   2992  case 'a':
   2993    if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
   2994      openFold(curwin->w_cursor, cap->count1);
   2995    } else {
   2996      closeFold(curwin->w_cursor, cap->count1);
   2997      curwin->w_p_fen = true;
   2998    }
   2999    break;
   3000 
   3001  // "zA": open fold at cursor recursively
   3002  case 'A':
   3003    if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) {
   3004      openFoldRecurse(curwin->w_cursor);
   3005    } else {
   3006      closeFoldRecurse(curwin->w_cursor);
   3007      curwin->w_p_fen = true;
   3008    }
   3009    break;
   3010 
   3011  // "zo": open fold at cursor or Visual area
   3012  case 'o':
   3013    if (VIsual_active) {
   3014      nv_operator(cap);
   3015    } else {
   3016      openFold(curwin->w_cursor, cap->count1);
   3017    }
   3018    break;
   3019 
   3020  // "zO": open fold recursively
   3021  case 'O':
   3022    if (VIsual_active) {
   3023      nv_operator(cap);
   3024    } else {
   3025      openFoldRecurse(curwin->w_cursor);
   3026    }
   3027    break;
   3028 
   3029  // "zc": close fold at cursor or Visual area
   3030  case 'c':
   3031    if (VIsual_active) {
   3032      nv_operator(cap);
   3033    } else {
   3034      closeFold(curwin->w_cursor, cap->count1);
   3035    }
   3036    curwin->w_p_fen = true;
   3037    break;
   3038 
   3039  // "zC": close fold recursively
   3040  case 'C':
   3041    if (VIsual_active) {
   3042      nv_operator(cap);
   3043    } else {
   3044      closeFoldRecurse(curwin->w_cursor);
   3045    }
   3046    curwin->w_p_fen = true;
   3047    break;
   3048 
   3049  // "zv": open folds at the cursor
   3050  case 'v':
   3051    foldOpenCursor();
   3052    break;
   3053 
   3054  // "zx": re-apply 'foldlevel' and open folds at the cursor
   3055  case 'x':
   3056    curwin->w_p_fen = true;
   3057    curwin->w_foldinvalid = true;               // recompute folds
   3058    newFoldLevel();                             // update right now
   3059    foldOpenCursor();
   3060    break;
   3061 
   3062  // "zX": undo manual opens/closes, re-apply 'foldlevel'
   3063  case 'X':
   3064    curwin->w_p_fen = true;
   3065    curwin->w_foldinvalid = true;               // recompute folds
   3066    old_fdl = -1;                               // force an update
   3067    break;
   3068 
   3069  // "zm": fold more
   3070  case 'm':
   3071    if (curwin->w_p_fdl > 0) {
   3072      curwin->w_p_fdl -= cap->count1;
   3073      curwin->w_p_fdl = MAX(curwin->w_p_fdl, 0);
   3074    }
   3075    old_fdl = -1;                       // force an update
   3076    curwin->w_p_fen = true;
   3077    break;
   3078 
   3079  // "zM": close all folds
   3080  case 'M':
   3081    curwin->w_p_fdl = 0;
   3082    old_fdl = -1;                       // force an update
   3083    curwin->w_p_fen = true;
   3084    break;
   3085 
   3086  // "zr": reduce folding
   3087  case 'r':
   3088    curwin->w_p_fdl += cap->count1;
   3089    {
   3090      int d = getDeepestNesting(curwin);
   3091      curwin->w_p_fdl = MIN(curwin->w_p_fdl, d);
   3092    }
   3093    break;
   3094 
   3095  case 'R':     //  "zR": open all folds
   3096    curwin->w_p_fdl = getDeepestNesting(curwin);
   3097    old_fdl = -1;                       // force an update
   3098    break;
   3099 
   3100  case 'j':     // "zj" move to next fold downwards
   3101  case 'k':     // "zk" move to next fold upwards
   3102    if (foldMoveTo(true, nchar == 'j' ? FORWARD : BACKWARD,
   3103                   cap->count1) == false) {
   3104      clearopbeep(cap->oap);
   3105    }
   3106    break;
   3107 
   3108  case 'u':     // "zug" and "zuw": undo "zg" and "zw"
   3109  case 'g':     // "zg": add good word to word list
   3110  case 'w':     // "zw": add wrong word to word list
   3111  case 'G':     // "zG": add good word to temp word list
   3112  case 'W':     // "zW": add wrong word to temp word list
   3113    if (nv_zg_zw(cap, nchar) == FAIL) {
   3114      return;
   3115    }
   3116    break;
   3117 
   3118  case '=':     // "z=": suggestions for a badly spelled word
   3119    if (!checkclearop(cap->oap)) {
   3120      spell_suggest(cap->count0);
   3121    }
   3122    break;
   3123 
   3124  default:
   3125    clearopbeep(cap->oap);
   3126  }
   3127 
   3128  // Redraw when 'foldenable' changed
   3129  if (old_fen != curwin->w_p_fen) {
   3130    if (foldmethodIsDiff(curwin) && curwin->w_p_scb) {
   3131      // Adjust 'foldenable' in diff-synced windows.
   3132      FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   3133        if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) {
   3134          wp->w_p_fen = curwin->w_p_fen;
   3135          changed_window_setting(wp);
   3136        }
   3137      }
   3138    }
   3139    changed_window_setting(curwin);
   3140  }
   3141 
   3142  // Redraw when 'foldlevel' changed.
   3143  if (old_fdl != curwin->w_p_fdl) {
   3144    newFoldLevel();
   3145  }
   3146 }
   3147 
   3148 /// "Q" command.
   3149 static void nv_regreplay(cmdarg_T *cap)
   3150 {
   3151  if (checkclearop(cap->oap)) {
   3152    return;
   3153  }
   3154 
   3155  while (cap->count1-- && !got_int) {
   3156    if (do_execreg(reg_recorded, false, false, false) == false) {
   3157      clearopbeep(cap->oap);
   3158      break;
   3159    }
   3160    line_breakcheck();
   3161  }
   3162 }
   3163 
   3164 /// Handle a ":" command and <Cmd> or Lua mappings.
   3165 static void nv_colon(cmdarg_T *cap)
   3166 {
   3167  bool cmd_result;
   3168  bool is_cmdkey = cap->cmdchar == K_COMMAND;
   3169  bool is_lua = cap->cmdchar == K_LUA;
   3170 
   3171  if (VIsual_active && !is_cmdkey && !is_lua) {
   3172    nv_operator(cap);
   3173    return;
   3174  }
   3175 
   3176  if (cap->oap->op_type != OP_NOP) {
   3177    // Using ":" as a movement is charwise exclusive.
   3178    cap->oap->motion_type = kMTCharWise;
   3179    cap->oap->inclusive = false;
   3180  } else if (cap->count0 && !is_cmdkey && !is_lua) {
   3181    // translate "count:" into ":.,.+(count - 1)"
   3182    stuffcharReadbuff('.');
   3183    if (cap->count0 > 1) {
   3184      stuffReadbuff(",.+");
   3185      stuffnumReadbuff(cap->count0 - 1);
   3186    }
   3187  }
   3188 
   3189  // When typing, don't type below an old message
   3190  if (KeyTyped) {
   3191    compute_cmdrow();
   3192  }
   3193 
   3194  if (is_lua) {
   3195    cmd_result = map_execute_lua(true, false);
   3196  } else {
   3197    // get a command line and execute it
   3198    cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL,
   3199                            cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0);
   3200  }
   3201 
   3202  if (cmd_result == false) {
   3203    // The Ex command failed, do not execute the operator.
   3204    clearop(cap->oap);
   3205  } else if (cap->oap->op_type != OP_NOP
   3206             && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count
   3207                 || cap->oap->start.col > ml_get_len(cap->oap->start.lnum)
   3208                 || did_emsg)) {
   3209    // The start of the operator has become invalid by the Ex command.
   3210    clearopbeep(cap->oap);
   3211  }
   3212 }
   3213 
   3214 /// Handle CTRL-G command.
   3215 static void nv_ctrlg(cmdarg_T *cap)
   3216 {
   3217  if (VIsual_active) {  // toggle Selection/Visual mode
   3218    VIsual_select = !VIsual_select;
   3219    may_trigger_modechanged();
   3220    showmode();
   3221  } else if (!checkclearop(cap->oap)) {
   3222    // print full name if count given or :cd used
   3223    fileinfo(cap->count0, false, true);
   3224  }
   3225 }
   3226 
   3227 /// Handle CTRL-H <Backspace> command.
   3228 static void nv_ctrlh(cmdarg_T *cap)
   3229 {
   3230  if (VIsual_active && VIsual_select) {
   3231    cap->cmdchar = 'x';         // BS key behaves like 'x' in Select mode
   3232    v_visop(cap);
   3233  } else {
   3234    nv_left(cap);
   3235  }
   3236 }
   3237 
   3238 /// CTRL-L: clear screen and redraw.
   3239 static void nv_clear(cmdarg_T *cap)
   3240 {
   3241  if (checkclearop(cap->oap)) {
   3242    return;
   3243  }
   3244 
   3245  // Clear all syntax states to force resyncing.
   3246  syn_stack_free_all(curwin->w_s);
   3247  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   3248    wp->w_s->b_syn_slow = false;
   3249  }
   3250  redraw_later(curwin, UPD_CLEAR);
   3251 }
   3252 
   3253 /// CTRL-O: In Select mode: switch to Visual mode for one command.
   3254 /// Otherwise: Go to older pcmark.
   3255 static void nv_ctrlo(cmdarg_T *cap)
   3256 {
   3257  if (VIsual_active && VIsual_select) {
   3258    VIsual_select = false;
   3259    may_trigger_modechanged();
   3260    showmode();
   3261    restart_VIsual_select = 2;          // restart Select mode later
   3262  } else {
   3263    cap->count1 = -cap->count1;
   3264    nv_pcmark(cap);
   3265  }
   3266 }
   3267 
   3268 /// CTRL-^ command, short for ":e #".  Works even when the alternate buffer is
   3269 /// not named.
   3270 static void nv_hat(cmdarg_T *cap)
   3271 {
   3272  if (!checkclearopq(cap->oap)) {
   3273    buflist_getfile(cap->count0, 0,
   3274                    GETF_SETMARK|GETF_ALT, false);
   3275  }
   3276 }
   3277 
   3278 /// "Z" commands.
   3279 static void nv_Zet(cmdarg_T *cap)
   3280 {
   3281  if (checkclearopq(cap->oap)) {
   3282    return;
   3283  }
   3284 
   3285  switch (cap->nchar) {
   3286  // "ZZ": equivalent to ":x".
   3287  case 'Z':
   3288    do_cmdline_cmd("x");
   3289    break;
   3290 
   3291  // "ZQ": equivalent to ":q!" (Elvis compatible).
   3292  case 'Q':
   3293    do_cmdline_cmd("q!");
   3294    break;
   3295 
   3296  default:
   3297    clearopbeep(cap->oap);
   3298  }
   3299 }
   3300 
   3301 /// Call nv_ident() as if "c1" was used, with "c2" as next character.
   3302 void do_nv_ident(int c1, int c2)
   3303 {
   3304  oparg_T oa;
   3305  cmdarg_T ca;
   3306 
   3307  clear_oparg(&oa);
   3308  CLEAR_FIELD(ca);
   3309  ca.oap = &oa;
   3310  ca.cmdchar = c1;
   3311  ca.nchar = c2;
   3312  nv_ident(&ca);
   3313 }
   3314 
   3315 /// 'K' normal-mode command. Get the command to lookup the keyword under the
   3316 /// cursor.
   3317 static size_t nv_K_getcmd(cmdarg_T *cap, char *kp, bool kp_help, bool kp_ex, char **ptr_arg,
   3318                          size_t n, char *buf, size_t bufsize, size_t *buflen)
   3319 {
   3320  if (kp_help) {
   3321    // in the help buffer
   3322    STRCPY(buf, "he! ");
   3323    *buflen = STRLEN_LITERAL("he! ");
   3324    return n;
   3325  }
   3326 
   3327  if (kp_ex) {
   3328    *buflen = 0;
   3329    // 'keywordprg' is an ex command
   3330    if (cap->count0 != 0) {  // Send the count to the ex command.
   3331      *buflen = (size_t)snprintf(buf, bufsize, "%" PRId64, (int64_t)(cap->count0));
   3332    }
   3333    *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "%s ", kp);
   3334    return n;
   3335  }
   3336 
   3337  char *ptr = *ptr_arg;
   3338 
   3339  // An external command will probably use an argument starting
   3340  // with "-" as an option.  To avoid trouble we skip the "-".
   3341  while (*ptr == '-' && n > 0) {
   3342    ptr++;
   3343    n--;
   3344  }
   3345  if (n == 0) {
   3346    // found dashes only
   3347    emsg(_(e_noident));
   3348    xfree(buf);
   3349    *ptr_arg = ptr;
   3350    return 0;
   3351  }
   3352 
   3353  // When a count is given, turn it into a range.  Is this
   3354  // really what we want?
   3355  bool isman = (strcmp(kp, "man") == 0);
   3356  bool isman_s = (strcmp(kp, "man -s") == 0);
   3357  if (cap->count0 != 0 && !(isman || isman_s)) {
   3358    *buflen = (size_t)snprintf(buf, bufsize, ".,.+%" PRId64, (int64_t)(cap->count0 - 1));
   3359  }
   3360 
   3361  do_cmdline_cmd("tabnew");
   3362  *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "terminal ");
   3363  if (cap->count0 == 0 && isman_s) {
   3364    *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "man ");
   3365  } else {
   3366    *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "%s ", kp);
   3367  }
   3368  if (cap->count0 != 0 && (isman || isman_s)) {
   3369    *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen,
   3370                                "%" PRId64 " ", (int64_t)cap->count0);
   3371  }
   3372 
   3373  *ptr_arg = ptr;
   3374  return n;
   3375 }
   3376 
   3377 /// Handle the commands that use the word under the cursor.
   3378 /// [g] CTRL-]   :ta to current identifier
   3379 /// [g] 'K'      run program for current identifier
   3380 /// [g] '*'      / to current identifier or string
   3381 /// [g] '#'      ? to current identifier or string
   3382 ///  g  ']'      :tselect for current identifier
   3383 static void nv_ident(cmdarg_T *cap)
   3384 {
   3385  char *ptr = NULL;
   3386  char *p;
   3387  size_t n = 0;                 // init for GCC
   3388  int cmdchar;
   3389  bool g_cmd;                   // "g" command
   3390  bool tag_cmd = false;
   3391 
   3392  if (cap->cmdchar == 'g') {    // "g*", "g#", "g]" and "gCTRL-]"
   3393    cmdchar = cap->nchar;
   3394    g_cmd = true;
   3395  } else {
   3396    cmdchar = cap->cmdchar;
   3397    g_cmd = false;
   3398  }
   3399 
   3400  if (cmdchar == POUND) {       // the pound sign, '#' for English keyboards
   3401    cmdchar = '#';
   3402  }
   3403 
   3404  // The "]", "CTRL-]" and "K" commands accept an argument in Visual mode.
   3405  if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K') {
   3406    if (VIsual_active && get_visual_text(cap, &ptr, &n) == false) {
   3407      return;
   3408    }
   3409    if (checkclearopq(cap->oap)) {
   3410      return;
   3411    }
   3412  }
   3413 
   3414  if (ptr == NULL && (n = find_ident_under_cursor(&ptr,
   3415                                                  ((cmdchar == '*'
   3416                                                    || cmdchar == '#')
   3417                                                   ? FIND_IDENT|FIND_STRING
   3418                                                   : FIND_IDENT))) == 0) {
   3419    clearop(cap->oap);
   3420    return;
   3421  }
   3422 
   3423  // Allocate buffer to put the command in.  Inserting backslashes can
   3424  // double the length of the word.  p_kp / curbuf->b_p_kp could be added
   3425  // and some numbers.
   3426  char *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp;  // 'keywordprg'
   3427  bool kp_help = (*kp == NUL || strcmp(kp, ":he") == 0 || strcmp(kp, ":help") == 0);
   3428  if (kp_help && *skipwhite(ptr) == NUL) {
   3429    emsg(_(e_noident));   // found white space only
   3430    return;
   3431  }
   3432  bool kp_ex = (*kp == ':');  // 'keywordprg' is an ex command
   3433  size_t bufsize = n * 2 + 30 + strlen(kp);
   3434  char *buf = xmalloc(bufsize);
   3435  buf[0] = NUL;
   3436  size_t buflen = 0;
   3437 
   3438  switch (cmdchar) {
   3439  case '*':
   3440  case '#':
   3441    // Put cursor at start of word, makes search skip the word
   3442    // under the cursor.
   3443    // Call setpcmark() first, so "*``" puts the cursor back where
   3444    // it was.
   3445    setpcmark();
   3446    curwin->w_cursor.col = (colnr_T)(ptr - get_cursor_line_ptr());
   3447 
   3448    if (!g_cmd && vim_iswordp(ptr)) {
   3449      STRCPY(buf, "\\<");
   3450      buflen = STRLEN_LITERAL("\\<");
   3451    }
   3452    no_smartcase = true;                // don't use 'smartcase' now
   3453    break;
   3454 
   3455  case 'K':
   3456    n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, bufsize, &buflen);
   3457    if (n == 0) {
   3458      return;
   3459    }
   3460    break;
   3461 
   3462  case ']':
   3463    tag_cmd = true;
   3464    STRCPY(buf, "ts ");
   3465    buflen = STRLEN_LITERAL("ts ");
   3466    break;
   3467 
   3468  default:
   3469    tag_cmd = true;
   3470    if (curbuf->b_help) {
   3471      STRCPY(buf, "he! ");
   3472      buflen = STRLEN_LITERAL("he! ");
   3473    } else {
   3474      if (g_cmd) {
   3475        STRCPY(buf, "tj ");
   3476        buflen = STRLEN_LITERAL("tj ");
   3477      } else if (cap->count0 == 0) {
   3478        STRCPY(buf, "ta ");
   3479        buflen = STRLEN_LITERAL("ta ");
   3480      } else {
   3481        buflen = (size_t)snprintf(buf, bufsize, ":%" PRId64 "ta ", (int64_t)cap->count0);
   3482      }
   3483    }
   3484  }
   3485 
   3486  // Now grab the chars in the identifier
   3487  if (cmdchar == 'K' && !kp_help) {
   3488    ptr = xstrnsave(ptr, n);
   3489    if (kp_ex) {
   3490      // Escape the argument properly for an Ex command
   3491      p = vim_strsave_fnameescape(ptr, VSE_NONE);
   3492    } else {
   3493      // Escape the argument properly for a shell command
   3494      p = vim_strsave_shellescape(ptr, true, true);
   3495    }
   3496    xfree(ptr);
   3497    size_t plen = strlen(p);
   3498    char *newbuf = xrealloc(buf, buflen + plen + 1);
   3499    buf = newbuf;
   3500    STRCPY(buf + buflen, p);
   3501    buflen += plen;
   3502    xfree(p);
   3503  } else {
   3504    char *aux_ptr;
   3505    if (cmdchar == '*') {
   3506      aux_ptr = (magic_isset() ? "/.*~[^$\\" : "/^$\\");
   3507    } else if (cmdchar == '#') {
   3508      aux_ptr = (magic_isset() ? "/?.*~[^$\\" : "/?^$\\");
   3509    } else if (tag_cmd) {
   3510      if (strcmp(curbuf->b_p_ft, "help") == 0) {
   3511        // ":help" handles unescaped argument
   3512        aux_ptr = "";
   3513      } else {
   3514        aux_ptr = "\\|\"\n[";
   3515      }
   3516    } else {
   3517      aux_ptr = "\\|\"\n*?[";
   3518    }
   3519 
   3520    p = buf + buflen;
   3521    while (n-- > 0) {
   3522      // put a backslash before \ and some others
   3523      if (vim_strchr(aux_ptr, (uint8_t)(*ptr)) != NULL) {
   3524        *p++ = '\\';
   3525      }
   3526 
   3527      // When current byte is a part of multibyte character, copy all
   3528      // bytes of that character.
   3529      const size_t len = (size_t)(utfc_ptr2len(ptr) - 1);
   3530      for (size_t i = 0; i < len && n > 0; i++, n--) {
   3531        *p++ = *ptr++;
   3532      }
   3533      *p++ = *ptr++;
   3534    }
   3535    *p = NUL;
   3536    buflen = (size_t)(p - buf);
   3537  }
   3538 
   3539  // Execute the command.
   3540  if (cmdchar == '*' || cmdchar == '#') {
   3541    if (!g_cmd && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) {
   3542      STRCPY(buf + buflen, "\\>");
   3543      buflen += STRLEN_LITERAL("\\>");
   3544    }
   3545 
   3546    // put pattern in search history
   3547    init_history();
   3548    add_to_history(HIST_SEARCH, buf, buflen, true, NUL);
   3549 
   3550    normal_search(cap, cmdchar == '*' ? '/' : '?', buf, buflen, 0, NULL);
   3551  } else {
   3552    g_tag_at_cursor = true;
   3553    do_cmdline_cmd(buf);
   3554    g_tag_at_cursor = false;
   3555 
   3556    if (cmdchar == 'K' && !kp_ex && !kp_help) {
   3557      // Start insert mode in terminal buffer
   3558      restart_edit = 'i';
   3559 
   3560      add_map("<esc>", "<Cmd>bdelete!<CR>", MODE_TERMINAL, true);
   3561    }
   3562  }
   3563 
   3564  xfree(buf);
   3565 }
   3566 
   3567 /// Get visually selected text, within one line only.
   3568 ///
   3569 /// @param pp    return: start of selected text
   3570 /// @param lenp  return: length of selected text
   3571 ///
   3572 /// @return      false if more than one line selected.
   3573 bool get_visual_text(cmdarg_T *cap, char **pp, size_t *lenp)
   3574 {
   3575  if (VIsual_mode != 'V') {
   3576    unadjust_for_sel();
   3577  }
   3578  if (VIsual.lnum != curwin->w_cursor.lnum) {
   3579    if (cap != NULL) {
   3580      clearopbeep(cap->oap);
   3581    }
   3582    return false;
   3583  }
   3584  if (VIsual_mode == 'V') {
   3585    *pp = get_cursor_line_ptr();
   3586    *lenp = (size_t)get_cursor_line_len();
   3587  } else {
   3588    if (lt(curwin->w_cursor, VIsual)) {
   3589      *pp = ml_get_pos(&curwin->w_cursor);
   3590      *lenp = (size_t)VIsual.col - (size_t)curwin->w_cursor.col + 1;
   3591    } else {
   3592      *pp = ml_get_pos(&VIsual);
   3593      *lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
   3594    }
   3595    if (**pp == NUL) {
   3596      *lenp = 0;
   3597    }
   3598    if (*lenp > 0) {
   3599      // Correct the length to include all bytes of the last character.
   3600      *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1);
   3601    }
   3602  }
   3603  reset_VIsual_and_resel();
   3604  return true;
   3605 }
   3606 
   3607 /// CTRL-T: backwards in tag stack
   3608 static void nv_tagpop(cmdarg_T *cap)
   3609 {
   3610  if (!checkclearopq(cap->oap)) {
   3611    do_tag("", DT_POP, cap->count1, false, true);
   3612  }
   3613 }
   3614 
   3615 /// Handle scrolling command 'H', 'L' and 'M'.
   3616 static void nv_scroll(cmdarg_T *cap)
   3617 {
   3618  int n;
   3619  linenr_T lnum;
   3620 
   3621  cap->oap->motion_type = kMTLineWise;
   3622  setpcmark();
   3623 
   3624  if (cap->cmdchar == 'L') {
   3625    validate_botline_win(curwin);  // make sure curwin->w_botline is valid
   3626    curwin->w_cursor.lnum = curwin->w_botline - 1;
   3627    if (cap->count1 - 1 >= curwin->w_cursor.lnum) {
   3628      curwin->w_cursor.lnum = 1;
   3629    } else {
   3630      if (win_lines_concealed(curwin)) {
   3631        // Count a fold for one screen line.
   3632        for (n = cap->count1 - 1; n > 0 && curwin->w_cursor.lnum > curwin->w_topline; n--) {
   3633          hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL);
   3634          n += decor_conceal_line(curwin, curwin->w_cursor.lnum, true);
   3635          if (curwin->w_cursor.lnum > curwin->w_topline) {
   3636            curwin->w_cursor.lnum--;
   3637          }
   3638        }
   3639      } else {
   3640        curwin->w_cursor.lnum -= cap->count1 - 1;
   3641      }
   3642    }
   3643  } else {
   3644    if (cap->cmdchar == 'M') {
   3645      int used = 0;
   3646      // Don't count filler lines above the window.
   3647      used -= win_get_fill(curwin, curwin->w_topline) - curwin->w_topfill;
   3648      validate_botline_win(curwin);  // make sure w_empty_rows is valid
   3649      int half = (curwin->w_view_height - curwin->w_empty_rows + 1) / 2;
   3650      for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) {
   3651        // Count half the number of filler lines to be "below this
   3652        // line" and half to be "above the next line".
   3653        if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + n) / 2 >= half) {
   3654          n--;
   3655          break;
   3656        }
   3657        used += plines_win(curwin, curwin->w_topline + n, true);
   3658        if (used >= half) {
   3659          break;
   3660        }
   3661        if (hasFolding(curwin, curwin->w_topline + n, NULL, &lnum)) {
   3662          n = lnum - curwin->w_topline;
   3663        }
   3664      }
   3665      if (n > 0 && used > curwin->w_view_height) {
   3666        n--;
   3667      }
   3668    } else {  // (cap->cmdchar == 'H')
   3669      n = cap->count1 - 1;
   3670      if (win_lines_concealed(curwin)) {
   3671        // Count a fold for one screen line.
   3672        lnum = curwin->w_topline;
   3673        while ((decor_conceal_line(curwin, lnum - 1, true) || n-- > 0)
   3674               && lnum < curwin->w_botline - 1) {
   3675          hasFolding(curwin, lnum, NULL, &lnum);
   3676          lnum++;
   3677        }
   3678        n = lnum - curwin->w_topline;
   3679      }
   3680    }
   3681    curwin->w_cursor.lnum = MIN(curwin->w_topline + n, curbuf->b_ml.ml_line_count);
   3682  }
   3683 
   3684  // Correct for 'so', except when an operator is pending.
   3685  if (cap->oap->op_type == OP_NOP) {
   3686    cursor_correct(curwin);
   3687  }
   3688  beginline(BL_SOL | BL_FIX);
   3689 }
   3690 
   3691 /// Cursor right commands.
   3692 static void nv_right(cmdarg_T *cap)
   3693 {
   3694  int n;
   3695 
   3696  if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
   3697    // <C-Right> and <S-Right> move a word or WORD right
   3698    if (mod_mask & MOD_MASK_CTRL) {
   3699      cap->arg = true;
   3700    }
   3701    nv_wordcmd(cap);
   3702    return;
   3703  }
   3704 
   3705  cap->oap->motion_type = kMTCharWise;
   3706  cap->oap->inclusive = false;
   3707  bool past_line = (VIsual_active && *p_sel != 'o');
   3708 
   3709  // In virtual edit mode, there's no such thing as "past_line", as lines
   3710  // are (theoretically) infinitely long.
   3711  if (virtual_active(curwin)) {
   3712    past_line = false;
   3713  }
   3714 
   3715  for (n = cap->count1; n > 0; n--) {
   3716    if ((!past_line && oneright() == false)
   3717        || (past_line && *get_cursor_pos_ptr() == NUL)) {
   3718      //    <Space> wraps to next line if 'whichwrap' has 's'.
   3719      //        'l' wraps to next line if 'whichwrap' has 'l'.
   3720      // CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
   3721      if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL)
   3722           || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL)
   3723           || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL))
   3724          && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
   3725        // When deleting we also count the NL as a character.
   3726        // Set cap->oap->inclusive when last char in the line is
   3727        // included, move to next line after that
   3728        if (cap->oap->op_type != OP_NOP
   3729            && !cap->oap->inclusive
   3730            && !LINEEMPTY(curwin->w_cursor.lnum)) {
   3731          cap->oap->inclusive = true;
   3732        } else {
   3733          curwin->w_cursor.lnum++;
   3734          curwin->w_cursor.col = 0;
   3735          curwin->w_cursor.coladd = 0;
   3736          curwin->w_set_curswant = true;
   3737          cap->oap->inclusive = false;
   3738        }
   3739        continue;
   3740      }
   3741      if (cap->oap->op_type == OP_NOP) {
   3742        // Only beep and flush if not moved at all
   3743        if (n == cap->count1) {
   3744          beep_flush();
   3745        }
   3746      } else {
   3747        if (!LINEEMPTY(curwin->w_cursor.lnum)) {
   3748          cap->oap->inclusive = true;
   3749        }
   3750      }
   3751      break;
   3752    } else if (past_line) {
   3753      curwin->w_set_curswant = true;
   3754      if (virtual_active(curwin)) {
   3755        oneright();
   3756      } else {
   3757        curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
   3758      }
   3759    }
   3760  }
   3761  if (n != cap->count1 && (fdo_flags & kOptFdoFlagHor) && KeyTyped
   3762      && cap->oap->op_type == OP_NOP) {
   3763    foldOpenCursor();
   3764  }
   3765 }
   3766 
   3767 /// Cursor left commands.
   3768 ///
   3769 /// @return  true when operator end should not be adjusted.
   3770 static void nv_left(cmdarg_T *cap)
   3771 {
   3772  int n;
   3773 
   3774  if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
   3775    // <C-Left> and <S-Left> move a word or WORD left
   3776    if (mod_mask & MOD_MASK_CTRL) {
   3777      cap->arg = 1;
   3778    }
   3779    nv_bck_word(cap);
   3780    return;
   3781  }
   3782 
   3783  cap->oap->motion_type = kMTCharWise;
   3784  cap->oap->inclusive = false;
   3785  for (n = cap->count1; n > 0; n--) {
   3786    if (oneleft() == false) {
   3787      // <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'.
   3788      //                 'h' wraps to previous line if 'whichwrap' has 'h'.
   3789      //           CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
   3790      if ((((cap->cmdchar == K_BS || cap->cmdchar == Ctrl_H)
   3791            && vim_strchr(p_ww, 'b') != NULL)
   3792           || (cap->cmdchar == 'h' && vim_strchr(p_ww, 'h') != NULL)
   3793           || (cap->cmdchar == K_LEFT && vim_strchr(p_ww, '<') != NULL))
   3794          && curwin->w_cursor.lnum > 1) {
   3795        curwin->w_cursor.lnum--;
   3796        coladvance(curwin, MAXCOL);
   3797        curwin->w_set_curswant = true;
   3798 
   3799        // When the NL before the first char has to be deleted we
   3800        // put the cursor on the NUL after the previous line.
   3801        // This is a very special case, be careful!
   3802        // Don't adjust op_end now, otherwise it won't work.
   3803        if ((cap->oap->op_type == OP_DELETE || cap->oap->op_type == OP_CHANGE)
   3804            && !LINEEMPTY(curwin->w_cursor.lnum)) {
   3805          char *cp = get_cursor_pos_ptr();
   3806 
   3807          if (*cp != NUL) {
   3808            curwin->w_cursor.col += utfc_ptr2len(cp);
   3809          }
   3810          cap->retval |= CA_NO_ADJ_OP_END;
   3811        }
   3812        continue;
   3813      } else if (cap->oap->op_type == OP_NOP && n == cap->count1) {
   3814        // Only beep and flush if not moved at all
   3815        beep_flush();
   3816      }
   3817      break;
   3818    }
   3819  }
   3820  if (n != cap->count1 && (fdo_flags & kOptFdoFlagHor) && KeyTyped
   3821      && cap->oap->op_type == OP_NOP) {
   3822    foldOpenCursor();
   3823  }
   3824 }
   3825 
   3826 /// Cursor up commands.
   3827 /// cap->arg is true for "-": Move cursor to first non-blank.
   3828 static void nv_up(cmdarg_T *cap)
   3829 {
   3830  if (mod_mask & MOD_MASK_SHIFT) {
   3831    // <S-Up> is page up
   3832    cap->arg = BACKWARD;
   3833    nv_page(cap);
   3834    return;
   3835  }
   3836 
   3837  cap->oap->motion_type = kMTLineWise;
   3838  if (cursor_up(cap->count1, cap->oap->op_type == OP_NOP) == false) {
   3839    clearopbeep(cap->oap);
   3840  } else if (cap->arg) {
   3841    beginline(BL_WHITE | BL_FIX);
   3842  }
   3843 }
   3844 
   3845 /// Cursor down commands.
   3846 /// cap->arg is true for CR and "+": Move cursor to first non-blank.
   3847 static void nv_down(cmdarg_T *cap)
   3848 {
   3849  if (mod_mask & MOD_MASK_SHIFT) {
   3850    // <S-Down> is page down
   3851    cap->arg = FORWARD;
   3852    nv_page(cap);
   3853  } else if (bt_quickfix(curbuf) && cap->cmdchar == CAR) {
   3854    // Quickfix window only: view the result under the cursor.
   3855    qf_view_result(false);
   3856  } else {
   3857    // In the cmdline window a <CR> executes the command.
   3858    if (cmdwin_type != 0 && cap->cmdchar == CAR) {
   3859      cmdwin_result = CAR;
   3860    } else if (bt_prompt(curbuf) && cap->cmdchar == CAR
   3861               && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
   3862      // In a prompt buffer a <CR> in the last line invokes the callback.
   3863      prompt_invoke_callback();
   3864      if (restart_edit == 0) {
   3865        restart_edit = 'a';
   3866      }
   3867    } else {
   3868      cap->oap->motion_type = kMTLineWise;
   3869      if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) {
   3870        clearopbeep(cap->oap);
   3871      } else if (cap->arg) {
   3872        beginline(BL_WHITE | BL_FIX);
   3873      }
   3874    }
   3875  }
   3876 }
   3877 
   3878 /// Grab the file name under the cursor and edit it.
   3879 static void nv_gotofile(cmdarg_T *cap)
   3880 {
   3881  linenr_T lnum = -1;
   3882 
   3883  if (check_text_or_curbuf_locked(cap->oap)) {
   3884    return;
   3885  }
   3886 
   3887  if (!check_can_set_curbuf_disabled()) {
   3888    return;
   3889  }
   3890 
   3891  char *ptr = grab_file_name(cap->count1, &lnum);
   3892 
   3893  if (ptr != NULL) {
   3894    // do autowrite if necessary
   3895    if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf)) {
   3896      autowrite(curbuf, false);
   3897    }
   3898    setpcmark();
   3899    if (do_ecmd(0, ptr, NULL, NULL, ECMD_LAST,
   3900                buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK
   3901        && cap->nchar == 'F' && lnum >= 0) {
   3902      curwin->w_cursor.lnum = lnum;
   3903      check_cursor_lnum(curwin);
   3904      beginline(BL_SOL | BL_FIX);
   3905    }
   3906    xfree(ptr);
   3907  } else {
   3908    clearop(cap->oap);
   3909  }
   3910 }
   3911 
   3912 /// <End> command: to end of current line or last line.
   3913 static void nv_end(cmdarg_T *cap)
   3914 {
   3915  if (cap->arg || (mod_mask & MOD_MASK_CTRL)) {  // CTRL-END = goto last line
   3916    cap->arg = true;
   3917    nv_goto(cap);
   3918    cap->count1 = 1;                    // to end of current line
   3919  }
   3920  nv_dollar(cap);
   3921 }
   3922 
   3923 /// Handle the "$" command.
   3924 static void nv_dollar(cmdarg_T *cap)
   3925 {
   3926  cap->oap->motion_type = kMTCharWise;
   3927  cap->oap->inclusive = true;
   3928  // In virtual mode when off the edge of a line and an operator
   3929  // is pending (whew!) keep the cursor where it is.
   3930  // Otherwise, send it to the end of the line.
   3931  if (!virtual_active(curwin) || gchar_cursor() != NUL
   3932      || cap->oap->op_type == OP_NOP) {
   3933    curwin->w_curswant = MAXCOL;        // so we stay at the end
   3934  }
   3935  if (cursor_down(cap->count1 - 1,
   3936                  cap->oap->op_type == OP_NOP) == false) {
   3937    clearopbeep(cap->oap);
   3938  } else if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
   3939    foldOpenCursor();
   3940  }
   3941 }
   3942 
   3943 /// Implementation of '?' and '/' commands.
   3944 /// If cap->arg is true don't set PC mark.
   3945 static void nv_search(cmdarg_T *cap)
   3946 {
   3947  oparg_T *oap = cap->oap;
   3948  pos_T save_cursor = curwin->w_cursor;
   3949 
   3950  if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13) {
   3951    // Translate "g??" to "g?g?"
   3952    cap->cmdchar = 'g';
   3953    cap->nchar = '?';
   3954    nv_operator(cap);
   3955    return;
   3956  }
   3957 
   3958  // When using 'incsearch' the cursor may be moved to set a different search
   3959  // start position.
   3960  cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0, true);
   3961 
   3962  if (cap->searchbuf == NULL) {
   3963    clearop(oap);
   3964    return;
   3965  }
   3966 
   3967  normal_search(cap, cap->cmdchar, cap->searchbuf, strlen(cap->searchbuf),
   3968                (cap->arg || !equalpos(save_cursor, curwin->w_cursor))
   3969                ? 0 : SEARCH_MARK, NULL);
   3970 }
   3971 
   3972 /// Handle "N" and "n" commands.
   3973 /// cap->arg is SEARCH_REV for "N", 0 for "n".
   3974 static void nv_next(cmdarg_T *cap)
   3975 {
   3976  pos_T old = curwin->w_cursor;
   3977  int wrapped = false;
   3978  int i = normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, &wrapped);
   3979 
   3980  if (i == 1 && !wrapped && equalpos(old, curwin->w_cursor)) {
   3981    // Avoid getting stuck on the current cursor position, which can happen when
   3982    // an offset is given and the cursor is on the last char in the buffer:
   3983    // Repeat with count + 1.
   3984    cap->count1 += 1;
   3985    normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, NULL);
   3986    cap->count1 -= 1;
   3987  }
   3988 
   3989  // Redraw the window to refresh the highlighted matches.
   3990  if (i > 0 && p_hls && !no_hlsearch
   3991      && win_hl_attr(curwin, HLF_LC) != win_hl_attr(curwin, HLF_L)) {
   3992    redraw_later(curwin, UPD_SOME_VALID);
   3993  }
   3994 }
   3995 
   3996 /// Search for "pat" in direction "dir" ('/' or '?', 0 for repeat).
   3997 /// Uses only cap->count1 and cap->oap from "cap".
   3998 ///
   3999 /// @param opt  extra flags for do_search()
   4000 ///
   4001 /// @return 0 for failure, 1 for found, 2 for found and line offset added.
   4002 static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int opt, int *wrapped)
   4003 {
   4004  searchit_arg_T sia;
   4005  pos_T const prev_cursor = curwin->w_cursor;
   4006 
   4007  cap->oap->motion_type = kMTCharWise;
   4008  cap->oap->inclusive = false;
   4009  cap->oap->use_reg_one = true;
   4010  curwin->w_set_curswant = true;
   4011 
   4012  CLEAR_FIELD(sia);
   4013  int i = do_search(cap->oap, dir, dir, pat, patlen, cap->count1,
   4014                    opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
   4015  if (wrapped != NULL) {
   4016    *wrapped = sia.sa_wrapped;
   4017  }
   4018  if (i == 0) {
   4019    clearop(cap->oap);
   4020  } else {
   4021    if (i == 2) {
   4022      cap->oap->motion_type = kMTLineWise;
   4023    }
   4024    curwin->w_cursor.coladd = 0;
   4025    if (cap->oap->op_type == OP_NOP && (fdo_flags & kOptFdoFlagSearch) && KeyTyped) {
   4026      foldOpenCursor();
   4027    }
   4028  }
   4029  // Redraw the window to refresh the highlighted matches.
   4030  if (!equalpos(curwin->w_cursor, prev_cursor) && p_hls && !no_hlsearch
   4031      && win_hl_attr(curwin, HLF_LC) != win_hl_attr(curwin, HLF_L)) {
   4032    redraw_later(curwin, UPD_SOME_VALID);
   4033  }
   4034 
   4035  // "/$" will put the cursor after the end of the line, may need to
   4036  // correct that here
   4037  check_cursor(curwin);
   4038 
   4039  return i;
   4040 }
   4041 
   4042 /// Character search commands.
   4043 /// cap->arg is BACKWARD for 'F' and 'T', FORWARD for 'f' and 't', true for
   4044 /// ',' and false for ';'.
   4045 /// cap->nchar is NUL for ',' and ';' (repeat the search)
   4046 static void nv_csearch(cmdarg_T *cap)
   4047 {
   4048  bool cursor_dec = false;
   4049 
   4050  // If adjusted cursor position previously, unadjust it.
   4051  if (*p_sel == 'e' && VIsual_active && VIsual_mode == 'v'
   4052      && VIsual_select_exclu_adj) {
   4053    unadjust_for_sel();
   4054    cursor_dec = true;
   4055  }
   4056 
   4057  bool t_cmd = cap->cmdchar == 't' || cap->cmdchar == 'T';
   4058 
   4059  cap->oap->motion_type = kMTCharWise;
   4060  if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) {
   4061    clearopbeep(cap->oap);
   4062    // Revert unadjust when failed.
   4063    if (cursor_dec) {
   4064      adjust_for_sel(cap);
   4065    }
   4066    return;
   4067  }
   4068 
   4069  curwin->w_set_curswant = true;
   4070  // Include a Tab for "tx" and for "dfx".
   4071  if (gchar_cursor() == TAB && virtual_active(curwin) && cap->arg == FORWARD
   4072      && (t_cmd || cap->oap->op_type != OP_NOP)) {
   4073    colnr_T scol, ecol;
   4074 
   4075    getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
   4076    curwin->w_cursor.coladd = ecol - scol;
   4077  } else {
   4078    curwin->w_cursor.coladd = 0;
   4079  }
   4080  adjust_for_sel(cap);
   4081  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
   4082    foldOpenCursor();
   4083  }
   4084 }
   4085 
   4086 /// "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
   4087 /// "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
   4088 /// "[/", "[*", "]/", "]*": go to Nth comment start/end.
   4089 /// "[m" or "]m" search for prev/next start of (Java) method.
   4090 /// "[M" or "]M" search for prev/next end of (Java) method.
   4091 static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos)
   4092 {
   4093  pos_T new_pos = { 0, 0, 0 };
   4094  pos_T *pos = NULL;  // init for GCC
   4095  pos_T prev_pos;
   4096  int n;
   4097  int findc;
   4098 
   4099  if (cap->nchar == '*') {
   4100    cap->nchar = '/';
   4101  }
   4102  prev_pos.lnum = 0;
   4103  if (cap->nchar == 'm' || cap->nchar == 'M') {
   4104    if (cap->cmdchar == '[') {
   4105      findc = '{';
   4106    } else {
   4107      findc = '}';
   4108    }
   4109    n = 9999;
   4110  } else {
   4111    findc = cap->nchar;
   4112    n = cap->count1;
   4113  }
   4114  for (; n > 0; n--) {
   4115    if ((pos = findmatchlimit(cap->oap, findc,
   4116                              (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) {
   4117      if (new_pos.lnum == 0) {        // nothing found
   4118        if (cap->nchar != 'm' && cap->nchar != 'M') {
   4119          clearopbeep(cap->oap);
   4120        }
   4121      } else {
   4122        pos = &new_pos;               // use last one found
   4123      }
   4124      break;
   4125    }
   4126    prev_pos = new_pos;
   4127    curwin->w_cursor = *pos;
   4128    new_pos = *pos;
   4129  }
   4130  curwin->w_cursor = *old_pos;
   4131 
   4132  // Handle "[m", "]m", "[M" and "[M".  The findmatchlimit() only
   4133  // brought us to the match for "[m" and "]M" when inside a method.
   4134  // Try finding the '{' or '}' we want to be at.
   4135  // Also repeat for the given count.
   4136  if (cap->nchar == 'm' || cap->nchar == 'M') {
   4137    int c;
   4138    // norm is true for "]M" and "[m"
   4139    bool norm = ((findc == '{') == (cap->nchar == 'm'));
   4140 
   4141    n = cap->count1;
   4142    // found a match: we were inside a method
   4143    if (prev_pos.lnum != 0) {
   4144      pos = &prev_pos;
   4145      curwin->w_cursor = prev_pos;
   4146      if (norm) {
   4147        n--;
   4148      }
   4149    } else {
   4150      pos = NULL;
   4151    }
   4152    while (n > 0) {
   4153      while (true) {
   4154        if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) {
   4155          // if not found anything, that's an error
   4156          if (pos == NULL) {
   4157            clearopbeep(cap->oap);
   4158          }
   4159          n = 0;
   4160          break;
   4161        }
   4162        c = gchar_cursor();
   4163        if (c == '{' || c == '}') {
   4164          // Must have found end/start of class: use it.
   4165          // Or found the place to be at.
   4166          if ((c == findc && norm) || (n == 1 && !norm)) {
   4167            new_pos = curwin->w_cursor;
   4168            pos = &new_pos;
   4169            n = 0;
   4170          } else if (new_pos.lnum == 0) {
   4171            // if no match found at all, we started outside of the
   4172            // class and we're inside now.  Just go on.
   4173            new_pos = curwin->w_cursor;
   4174            pos = &new_pos;
   4175          } else if ((pos = findmatchlimit(cap->oap, findc,
   4176                                           (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD,
   4177                                           0)) == NULL) {
   4178            // found start/end of other method: go to match
   4179            n = 0;
   4180          } else {
   4181            curwin->w_cursor = *pos;
   4182          }
   4183          break;
   4184        }
   4185      }
   4186      n--;
   4187    }
   4188    curwin->w_cursor = *old_pos;
   4189    if (pos == NULL && new_pos.lnum != 0) {
   4190      clearopbeep(cap->oap);
   4191    }
   4192  }
   4193  if (pos != NULL) {
   4194    setpcmark();
   4195    curwin->w_cursor = *pos;
   4196    curwin->w_set_curswant = true;
   4197    if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped
   4198        && cap->oap->op_type == OP_NOP) {
   4199      foldOpenCursor();
   4200    }
   4201  }
   4202 }
   4203 
   4204 /// "[" and "]" commands.
   4205 /// cap->arg is BACKWARD for "[" and FORWARD for "]".
   4206 static void nv_brackets(cmdarg_T *cap)
   4207 {
   4208  int flag;
   4209  int n;
   4210 
   4211  cap->oap->motion_type = kMTCharWise;
   4212  cap->oap->inclusive = false;
   4213  pos_T old_pos = curwin->w_cursor;         // cursor position before command
   4214  curwin->w_cursor.coladd = 0;              // TODO(Unknown): don't do this for an error.
   4215 
   4216  // "[f" or "]f" : Edit file under the cursor (same as "gf")
   4217  if (cap->nchar == 'f') {
   4218    nv_gotofile(cap);
   4219  } else if (vim_strchr("iI\011dD\004", cap->nchar) != NULL) {
   4220    // Find the occurrence(s) of the identifier or define under cursor
   4221    // in current and included files or jump to the first occurrence.
   4222    //
   4223    //                    search       list           jump
   4224    //                  fwd   bwd    fwd   bwd     fwd    bwd
   4225    // identifier       "]i"  "[i"   "]I"  "[I"   "]^I"  "[^I"
   4226    // define           "]d"  "[d"   "]D"  "[D"   "]^D"  "[^D"
   4227    char *ptr;
   4228    size_t len;
   4229 
   4230    if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
   4231      clearop(cap->oap);
   4232    } else {
   4233      // Make a copy, if the line was changed it will be freed.
   4234      ptr = xmemdupz(ptr, len);
   4235      find_pattern_in_path(ptr, 0, len, true,
   4236                           cap->count0 == 0 ? !isupper(cap->nchar) : false,
   4237                           (((cap->nchar & 0xf) == ('d' & 0xf))
   4238                            ? FIND_DEFINE
   4239                            : FIND_ANY),
   4240                           cap->count1,
   4241                           (isupper(cap->nchar) ? ACTION_SHOW_ALL
   4242                                                : islower(cap->nchar) ? ACTION_SHOW
   4243                                                                      : ACTION_GOTO),
   4244                           (cap->cmdchar == ']'
   4245                            ? curwin->w_cursor.lnum + 1
   4246                            : 1),
   4247                           MAXLNUM,
   4248                           false, false);
   4249      xfree(ptr);
   4250      curwin->w_set_curswant = true;
   4251    }
   4252  } else if ((cap->cmdchar == '[' && vim_strchr("{(*/#mM", cap->nchar) != NULL)
   4253             || (cap->cmdchar == ']' && vim_strchr("})*/#mM", cap->nchar) != NULL)) {
   4254    // "[{", "[(", "]}" or "])": go to Nth unclosed '{', '(', '}' or ')'
   4255    // "[#", "]#": go to start/end of Nth innermost #if..#endif construct.
   4256    // "[/", "[*", "]/", "]*": go to Nth comment start/end.
   4257    // "[m" or "]m" search for prev/next start of (Java) method.
   4258    // "[M" or "]M" search for prev/next end of (Java) method.
   4259    nv_bracket_block(cap, &old_pos);
   4260  } else if (cap->nchar == '[' || cap->nchar == ']') {
   4261    // "[[", "[]", "]]" and "][": move to start or end of function
   4262    if (cap->nchar == cap->cmdchar) {               // "]]" or "[["
   4263      flag = '{';
   4264    } else {
   4265      flag = '}';                   // "][" or "[]"
   4266    }
   4267    curwin->w_set_curswant = true;
   4268    // Imitate strange Vi behaviour: When using "]]" with an operator we also stop at '}'.
   4269    if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag,
   4270                 (cap->oap->op_type != OP_NOP
   4271                  && cap->arg == FORWARD && flag == '{'))) {
   4272      clearopbeep(cap->oap);
   4273    } else {
   4274      if (cap->oap->op_type == OP_NOP) {
   4275        beginline(BL_WHITE | BL_FIX);
   4276      }
   4277      if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
   4278        foldOpenCursor();
   4279      }
   4280    }
   4281  } else if (cap->nchar == 'p' || cap->nchar == 'P') {
   4282    // "[p", "[P", "]P" and "]p": put with indent adjustment
   4283    nv_put_opt(cap, true);
   4284  } else if (cap->nchar == '\'' || cap->nchar == '`') {
   4285    // "['", "[`", "]'" and "]`": jump to next mark
   4286    fmark_T *fm = pos_to_mark(curbuf, NULL, curwin->w_cursor);
   4287    assert(fm != NULL);
   4288    fmark_T *prev_fm;
   4289    for (n = cap->count1; n > 0; n--) {
   4290      prev_fm = fm;
   4291      fm = getnextmark(&fm->mark, cap->cmdchar == '[' ? BACKWARD : FORWARD,
   4292                       cap->nchar == '\'');
   4293      if (fm == NULL) {
   4294        break;
   4295      }
   4296    }
   4297    if (fm == NULL) {
   4298      fm = prev_fm;
   4299    }
   4300    MarkMove flags = kMarkContext;
   4301    flags |= cap->nchar == '\'' ? kMarkBeginLine : 0;
   4302    nv_mark_move_to(cap, flags, fm);
   4303  } else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) {
   4304    // [ or ] followed by a middle mouse click: put selected text with
   4305    // indent adjustment.  Any other button just does as usual.
   4306    do_mouse(cap->oap, cap->nchar,
   4307             (cap->cmdchar == ']') ? FORWARD : BACKWARD,
   4308             cap->count1, PUT_FIXINDENT);
   4309  } else if (cap->nchar == 'z') {
   4310    // "[z" and "]z": move to start or end of open fold.
   4311    if (foldMoveTo(false, cap->cmdchar == ']' ? FORWARD : BACKWARD,
   4312                   cap->count1) == false) {
   4313      clearopbeep(cap->oap);
   4314    }
   4315  } else if (cap->nchar == 'c') {
   4316    // "[c" and "]c": move to next or previous diff-change.
   4317    if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
   4318                     cap->count1) == false) {
   4319      clearopbeep(cap->oap);
   4320    }
   4321  } else if (cap->nchar == 'r' || cap->nchar == 's' || cap->nchar == 'S') {
   4322    // "[r", "[s", "[S", "]r", "]s" and "]S": move to next spell error.
   4323    setpcmark();
   4324    for (n = 0; n < cap->count1; n++) {
   4325      if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
   4326                        cap->nchar == 's'
   4327                        ? SMT_ALL
   4328                        : cap->nchar == 'r' ? SMT_RARE : SMT_BAD,
   4329                        false, NULL) == 0) {
   4330        clearopbeep(cap->oap);
   4331        break;
   4332      }
   4333      curwin->w_set_curswant = true;
   4334    }
   4335    if (cap->oap->op_type == OP_NOP && (fdo_flags & kOptFdoFlagSearch) && KeyTyped) {
   4336      foldOpenCursor();
   4337    }
   4338  } else {
   4339    // Not a valid cap->nchar.
   4340    clearopbeep(cap->oap);
   4341  }
   4342 }
   4343 
   4344 /// Handle Normal mode "%" command.
   4345 static void nv_percent(cmdarg_T *cap)
   4346 {
   4347  linenr_T lnum = curwin->w_cursor.lnum;
   4348 
   4349  cap->oap->inclusive = true;
   4350  if (cap->count0) {  // {cnt}% : goto {cnt} percentage in file
   4351    if (cap->count0 > 100) {
   4352      clearopbeep(cap->oap);
   4353    } else {
   4354      cap->oap->motion_type = kMTLineWise;
   4355      setpcmark();
   4356      // Round up, so 'normal 100%' always jumps at the line line.
   4357      // Beyond 21474836 lines, (ml_line_count * 100 + 99) would
   4358      // overflow on 32-bits, so use a formula with less accuracy
   4359      // to avoid overflows.
   4360      if (curbuf->b_ml.ml_line_count >= 21474836) {
   4361        curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99)
   4362                                / 100 * cap->count0;
   4363      } else {
   4364        curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count *
   4365                                 cap->count0 + 99) / 100;
   4366      }
   4367      curwin->w_cursor.lnum = MIN(MAX(curwin->w_cursor.lnum, 1), curbuf->b_ml.ml_line_count);
   4368 
   4369      beginline(BL_SOL | BL_FIX);
   4370    }
   4371  } else {  // "%" : go to matching paren
   4372    pos_T *pos;
   4373    cap->oap->motion_type = kMTCharWise;
   4374    cap->oap->use_reg_one = true;
   4375    if ((pos = findmatch(cap->oap, NUL)) == NULL) {
   4376      clearopbeep(cap->oap);
   4377    } else {
   4378      setpcmark();
   4379      curwin->w_cursor = *pos;
   4380      curwin->w_set_curswant = true;
   4381      curwin->w_cursor.coladd = 0;
   4382      adjust_for_sel(cap);
   4383    }
   4384  }
   4385  if (cap->oap->op_type == OP_NOP
   4386      && lnum != curwin->w_cursor.lnum
   4387      && (fdo_flags & kOptFdoFlagPercent)
   4388      && KeyTyped) {
   4389    foldOpenCursor();
   4390  }
   4391 }
   4392 
   4393 /// Handle "(" and ")" commands.
   4394 /// cap->arg is BACKWARD for "(" and FORWARD for ")".
   4395 static void nv_brace(cmdarg_T *cap)
   4396 {
   4397  cap->oap->motion_type = kMTCharWise;
   4398  cap->oap->use_reg_one = true;
   4399  // The motion used to be inclusive for "(", but that is not what Vi does.
   4400  cap->oap->inclusive = false;
   4401  curwin->w_set_curswant = true;
   4402 
   4403  if (findsent(cap->arg, cap->count1) == FAIL) {
   4404    clearopbeep(cap->oap);
   4405    return;
   4406  }
   4407 
   4408  // Don't leave the cursor on the NUL past end of line.
   4409  adjust_cursor(cap->oap);
   4410  curwin->w_cursor.coladd = 0;
   4411  if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
   4412    foldOpenCursor();
   4413  }
   4414 }
   4415 
   4416 /// "m" command: Mark a position.
   4417 static void nv_mark(cmdarg_T *cap)
   4418 {
   4419  if (checkclearop(cap->oap)) {
   4420    return;
   4421  }
   4422 
   4423  if (setmark(cap->nchar) == false) {
   4424    clearopbeep(cap->oap);
   4425  }
   4426 }
   4427 
   4428 /// "{" and "}" commands.
   4429 /// cmd->arg is BACKWARD for "{" and FORWARD for "}".
   4430 static void nv_findpar(cmdarg_T *cap)
   4431 {
   4432  cap->oap->motion_type = kMTCharWise;
   4433  cap->oap->inclusive = false;
   4434  cap->oap->use_reg_one = true;
   4435  curwin->w_set_curswant = true;
   4436  if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, false)) {
   4437    clearopbeep(cap->oap);
   4438    return;
   4439  }
   4440 
   4441  curwin->w_cursor.coladd = 0;
   4442  if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
   4443    foldOpenCursor();
   4444  }
   4445 }
   4446 
   4447 /// "u" command: Undo or make lower case.
   4448 static void nv_undo(cmdarg_T *cap)
   4449 {
   4450  if (cap->oap->op_type == OP_LOWER
   4451      || VIsual_active) {
   4452    // translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu"
   4453    cap->cmdchar = 'g';
   4454    cap->nchar = 'u';
   4455    nv_operator(cap);
   4456  } else {
   4457    nv_kundo(cap);
   4458  }
   4459 }
   4460 
   4461 /// <Undo> command.
   4462 static void nv_kundo(cmdarg_T *cap)
   4463 {
   4464  if (checkclearopq(cap->oap)) {
   4465    return;
   4466  }
   4467 
   4468  u_undo(cap->count1);
   4469  curwin->w_set_curswant = true;
   4470 }
   4471 
   4472 /// Handle the "r" command.
   4473 static void nv_replace(cmdarg_T *cap)
   4474 {
   4475  int had_ctrl_v;
   4476 
   4477  if (checkclearop(cap->oap)) {
   4478    return;
   4479  }
   4480  if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
   4481    clearopbeep(cap->oap);
   4482    return;
   4483  }
   4484 
   4485  // get another character
   4486  if (cap->nchar == Ctrl_V || cap->nchar == Ctrl_Q) {
   4487    had_ctrl_v = Ctrl_V;
   4488    cap->nchar = get_literal(false);
   4489    // Don't redo a multibyte character with CTRL-V.
   4490    if (cap->nchar > DEL) {
   4491      had_ctrl_v = NUL;
   4492    }
   4493  } else {
   4494    had_ctrl_v = NUL;
   4495  }
   4496 
   4497  // Abort if the character is a special key.
   4498  if (IS_SPECIAL(cap->nchar)) {
   4499    clearopbeep(cap->oap);
   4500    return;
   4501  }
   4502 
   4503  // Visual mode "r"
   4504  if (VIsual_active) {
   4505    if (got_int) {
   4506      got_int = false;
   4507    }
   4508    if (had_ctrl_v) {
   4509      // Use a special (negative) number to make a difference between a
   4510      // literal CR or NL and a line break.
   4511      if (cap->nchar == CAR) {
   4512        cap->nchar = REPLACE_CR_NCHAR;
   4513      } else if (cap->nchar == NL) {
   4514        cap->nchar = REPLACE_NL_NCHAR;
   4515      }
   4516    }
   4517    nv_operator(cap);
   4518    return;
   4519  }
   4520 
   4521  // Break tabs, etc.
   4522  if (virtual_active(curwin)) {
   4523    if (u_save_cursor() == false) {
   4524      return;
   4525    }
   4526    if (gchar_cursor() == NUL) {
   4527      // Add extra space and put the cursor on the first one.
   4528      coladvance_force((colnr_T)(getviscol() + cap->count1));
   4529      assert(cap->count1 <= INT_MAX);
   4530      curwin->w_cursor.col -= (colnr_T)cap->count1;
   4531    } else if (gchar_cursor() == TAB) {
   4532      coladvance_force(getviscol());
   4533    }
   4534  }
   4535 
   4536  // Abort if not enough characters to replace.
   4537  if ((size_t)get_cursor_pos_len() < (unsigned)cap->count1
   4538      || (mb_charlen(get_cursor_pos_ptr()) < cap->count1)) {
   4539    clearopbeep(cap->oap);
   4540    return;
   4541  }
   4542 
   4543  // Replacing with a TAB is done by edit() when it is complicated because
   4544  // 'expandtab' or 'smarttab' is set.  CTRL-V TAB inserts a literal TAB.
   4545  // Other characters are done below to avoid problems with things like
   4546  // CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
   4547  if (had_ctrl_v != Ctrl_V && cap->nchar == '\t' && (curbuf->b_p_et || p_sta)) {
   4548    stuffnumReadbuff(cap->count1);
   4549    stuffcharReadbuff('R');
   4550    stuffcharReadbuff('\t');
   4551    stuffcharReadbuff(ESC);
   4552    return;
   4553  }
   4554 
   4555  // save line for undo
   4556  if (u_save_cursor() == false) {
   4557    return;
   4558  }
   4559 
   4560  if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n')) {
   4561    // Replace character(s) by a single newline.
   4562    // Strange vi behaviour: Only one newline is inserted.
   4563    // Delete the characters here.
   4564    // Insert the newline with an insert command, takes care of
   4565    // autoindent.      The insert command depends on being on the last
   4566    // character of a line or not.
   4567    del_chars(cap->count1, false);        // delete the characters
   4568    stuffcharReadbuff('\r');
   4569    stuffcharReadbuff(ESC);
   4570 
   4571    // Give 'r' to edit(), to get the redo command right.
   4572    invoke_edit(cap, true, 'r', false);
   4573  } else {
   4574    prep_redo(cap->oap->regname, cap->count1, NUL, 'r', NUL, had_ctrl_v, 0);
   4575 
   4576    curbuf->b_op_start = curwin->w_cursor;
   4577    const int old_State = State;
   4578 
   4579    if (cap->nchar_len > 0) {
   4580      AppendToRedobuff(cap->nchar_composing);
   4581    } else {
   4582      AppendCharToRedobuff(cap->nchar);
   4583    }
   4584 
   4585    // This is slow, but it handles replacing a single-byte with a
   4586    // multi-byte and the other way around.  Also handles adding
   4587    // composing characters for utf-8.
   4588    for (int n = cap->count1; n > 0; n--) {
   4589      State = MODE_REPLACE;
   4590      if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) {
   4591        int c = ins_copychar(curwin->w_cursor.lnum
   4592                             + (cap->nchar == Ctrl_Y ? -1 : 1));
   4593        if (c != NUL) {
   4594          ins_char(c);
   4595        } else {
   4596          // will be decremented further down
   4597          curwin->w_cursor.col++;
   4598        }
   4599      } else {
   4600        if (cap->nchar_len) {
   4601          ins_char_bytes(cap->nchar_composing, (size_t)cap->nchar_len);
   4602        } else {
   4603          ins_char(cap->nchar);
   4604        }
   4605      }
   4606      State = old_State;
   4607    }
   4608    curwin->w_cursor.col--;         // cursor on the last replaced char
   4609    // if the character on the left of the current cursor is a multi-byte
   4610    // character, move two characters left
   4611    mb_adjust_cursor();
   4612    curbuf->b_op_end = curwin->w_cursor;
   4613    curwin->w_set_curswant = true;
   4614    set_last_insert(cap->nchar);
   4615  }
   4616 
   4617  foldUpdateAfterInsert();
   4618 }
   4619 
   4620 /// 'o': Exchange start and end of Visual area.
   4621 /// 'O': same, but in block mode exchange left and right corners.
   4622 static void v_swap_corners(int cmdchar)
   4623 {
   4624  colnr_T left, right;
   4625 
   4626  if (cmdchar == 'O' && VIsual_mode == Ctrl_V) {
   4627    pos_T old_cursor = curwin->w_cursor;
   4628    getvcols(curwin, &old_cursor, &VIsual, &left, &right);
   4629    curwin->w_cursor.lnum = VIsual.lnum;
   4630    coladvance(curwin, left);
   4631    VIsual = curwin->w_cursor;
   4632 
   4633    curwin->w_cursor.lnum = old_cursor.lnum;
   4634    curwin->w_curswant = right;
   4635    // 'selection "exclusive" and cursor at right-bottom corner: move it
   4636    // right one column
   4637    if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e') {
   4638      curwin->w_curswant++;
   4639    }
   4640    coladvance(curwin, curwin->w_curswant);
   4641    if (curwin->w_cursor.col == old_cursor.col
   4642        && (!virtual_active(curwin)
   4643            || curwin->w_cursor.coladd ==
   4644            old_cursor.coladd)) {
   4645      curwin->w_cursor.lnum = VIsual.lnum;
   4646      if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e') {
   4647        right++;
   4648      }
   4649      coladvance(curwin, right);
   4650      VIsual = curwin->w_cursor;
   4651 
   4652      curwin->w_cursor.lnum = old_cursor.lnum;
   4653      coladvance(curwin, left);
   4654      curwin->w_curswant = left;
   4655    }
   4656  } else {
   4657    pos_T old_cursor = curwin->w_cursor;
   4658    curwin->w_cursor = VIsual;
   4659    VIsual = old_cursor;
   4660    curwin->w_set_curswant = true;
   4661  }
   4662 }
   4663 
   4664 /// "R" (cap->arg is false) and "gR" (cap->arg is true).
   4665 static void nv_Replace(cmdarg_T *cap)
   4666 {
   4667  if (VIsual_active) {          // "R" is replace lines
   4668    cap->cmdchar = 'c';
   4669    cap->nchar = NUL;
   4670    VIsual_mode_orig = VIsual_mode;     // remember original area for gv
   4671    VIsual_mode = 'V';
   4672    nv_operator(cap);
   4673    return;
   4674  }
   4675 
   4676  if (checkclearopq(cap->oap)) {
   4677    return;
   4678  }
   4679 
   4680  if (!MODIFIABLE(curbuf)) {
   4681    emsg(_(e_modifiable));
   4682  } else {
   4683    if (virtual_active(curwin)) {
   4684      coladvance(curwin, getviscol());
   4685    }
   4686    invoke_edit(cap, false, cap->arg ? 'V' : 'R', false);
   4687  }
   4688 }
   4689 
   4690 /// "gr".
   4691 static void nv_vreplace(cmdarg_T *cap)
   4692 {
   4693  if (VIsual_active) {
   4694    cap->cmdchar = 'r';
   4695    cap->nchar = cap->extra_char;
   4696    nv_replace(cap);            // Do same as "r" in Visual mode for now
   4697    return;
   4698  }
   4699 
   4700  if (checkclearopq(cap->oap)) {
   4701    return;
   4702  }
   4703 
   4704  if (!MODIFIABLE(curbuf)) {
   4705    emsg(_(e_modifiable));
   4706  } else {
   4707    if (cap->extra_char == Ctrl_V || cap->extra_char == Ctrl_Q) {
   4708      // get another character
   4709      cap->extra_char = get_literal(false);
   4710    }
   4711    if (cap->extra_char < ' ') {
   4712      // Prefix a control character with CTRL-V to avoid it being used as
   4713      // a command.
   4714      stuffcharReadbuff(Ctrl_V);
   4715    }
   4716    stuffcharReadbuff(cap->extra_char);
   4717    stuffcharReadbuff(ESC);
   4718    if (virtual_active(curwin)) {
   4719      coladvance(curwin, getviscol());
   4720    }
   4721    invoke_edit(cap, true, 'v', false);
   4722  }
   4723 }
   4724 
   4725 /// Swap case for "~" command, when it does not work like an operator.
   4726 static void n_swapchar(cmdarg_T *cap)
   4727 {
   4728  bool did_change = false;
   4729 
   4730  if (checkclearopq(cap->oap)) {
   4731    return;
   4732  }
   4733 
   4734  if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
   4735    clearopbeep(cap->oap);
   4736    return;
   4737  }
   4738 
   4739  prep_redo_cmd(cap);
   4740 
   4741  if (u_save_cursor() == false) {
   4742    return;
   4743  }
   4744 
   4745  pos_T startpos = curwin->w_cursor;
   4746  for (int n = cap->count1; n > 0; n--) {
   4747    did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
   4748    inc_cursor();
   4749    if (gchar_cursor() == NUL) {
   4750      if (vim_strchr(p_ww, '~') != NULL
   4751          && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
   4752        curwin->w_cursor.lnum++;
   4753        curwin->w_cursor.col = 0;
   4754        if (n > 1) {
   4755          if (u_savesub(curwin->w_cursor.lnum) == false) {
   4756            break;
   4757          }
   4758          u_clearline(curbuf);
   4759        }
   4760      } else {
   4761        break;
   4762      }
   4763    }
   4764  }
   4765 
   4766  check_cursor(curwin);
   4767  curwin->w_set_curswant = true;
   4768  if (did_change) {
   4769    changed_lines(curbuf, startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1,
   4770                  0, true);
   4771    curbuf->b_op_start = startpos;
   4772    curbuf->b_op_end = curwin->w_cursor;
   4773    if (curbuf->b_op_end.col > 0) {
   4774      curbuf->b_op_end.col--;
   4775    }
   4776  }
   4777 }
   4778 
   4779 /// Move the cursor to the mark position
   4780 ///
   4781 /// Wrapper to mark_move_to() that also handles normal mode command arguments.
   4782 /// @note  It will switch the buffer if neccesarry, move the cursor and set the
   4783 /// view depending on the given flags.
   4784 /// @param cap  command line arguments
   4785 /// @param flags for mark_move_to()
   4786 /// @param mark  mark
   4787 /// @return  The result of calling mark_move_to()
   4788 static MarkMoveRes nv_mark_move_to(cmdarg_T *cap, MarkMove flags, fmark_T *fm)
   4789 {
   4790  MarkMoveRes res = mark_move_to(fm, flags);
   4791  if (res & kMarkMoveFailed) {
   4792    clearop(cap->oap);
   4793  }
   4794  cap->oap->motion_type = flags & kMarkBeginLine ? kMTLineWise : kMTCharWise;
   4795  if (cap->cmdchar == '`') {
   4796    cap->oap->use_reg_one = true;
   4797  }
   4798  cap->oap->inclusive = false;  // ignored if not kMTCharWise
   4799  curwin->w_set_curswant = true;
   4800  return res;
   4801 }
   4802 
   4803 /// Handle commands that are operators in Visual mode.
   4804 static void v_visop(cmdarg_T *cap)
   4805 {
   4806  static char trans[] = "YyDdCcxdXdAAIIrr";
   4807 
   4808  // Uppercase means linewise, except in block mode, then "D" deletes till
   4809  // the end of the line, and "C" replaces till EOL
   4810  if (isupper(cap->cmdchar)) {
   4811    if (VIsual_mode != Ctrl_V) {
   4812      VIsual_mode_orig = VIsual_mode;
   4813      VIsual_mode = 'V';
   4814    } else if (cap->cmdchar == 'C' || cap->cmdchar == 'D') {
   4815      curwin->w_curswant = MAXCOL;
   4816    }
   4817  }
   4818  cap->cmdchar = (uint8_t)(*(vim_strchr(trans, cap->cmdchar) + 1));
   4819  nv_operator(cap);
   4820 }
   4821 
   4822 /// "s" and "S" commands.
   4823 static void nv_subst(cmdarg_T *cap)
   4824 {
   4825  if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
   4826    clearopbeep(cap->oap);
   4827    return;
   4828  }
   4829  if (VIsual_active) {  // "vs" and "vS" are the same as "vc"
   4830    if (cap->cmdchar == 'S') {
   4831      VIsual_mode_orig = VIsual_mode;
   4832      VIsual_mode = 'V';
   4833    }
   4834    cap->cmdchar = 'c';
   4835    nv_operator(cap);
   4836  } else {
   4837    nv_optrans(cap);
   4838  }
   4839 }
   4840 
   4841 /// Abbreviated commands.
   4842 static void nv_abbrev(cmdarg_T *cap)
   4843 {
   4844  if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL) {
   4845    cap->cmdchar = 'x';                 // DEL key behaves like 'x'
   4846  }
   4847  // in Visual mode these commands are operators
   4848  if (VIsual_active) {
   4849    v_visop(cap);
   4850  } else {
   4851    nv_optrans(cap);
   4852  }
   4853 }
   4854 
   4855 /// Translate a command into another command.
   4856 static void nv_optrans(cmdarg_T *cap)
   4857 {
   4858  static const char *(ar[]) = { "dl", "dh", "d$", "c$", "cl", "cc", "yy",
   4859                                ":s\r" };
   4860  static const char *str = "xXDCsSY&";
   4861 
   4862  if (!checkclearopq(cap->oap)) {
   4863    if (cap->count0) {
   4864      stuffnumReadbuff(cap->count0);
   4865    }
   4866    stuffReadbuff(ar[strchr(str, (char)cap->cmdchar) - str]);
   4867  }
   4868  cap->opcount = 0;
   4869 }
   4870 
   4871 /// "'" and "`" commands.  Also for "g'" and "g`".
   4872 /// cap->arg is true for "'" and "g'".
   4873 static void nv_gomark(cmdarg_T *cap)
   4874 {
   4875  int name;
   4876  MarkMove flags = jop_flags & kOptJopFlagView ? kMarkSetView : 0;  // flags for moving to the mark
   4877  if (cap->oap->op_type != OP_NOP) {
   4878    // When there is a pending operator, do not restore the view as this is usually unexpected.
   4879    flags = 0;
   4880  }
   4881  MarkMoveRes move_res = 0;  // Result from moving to the mark
   4882  const bool old_KeyTyped = KeyTyped;  // getting file may reset it
   4883 
   4884  if (cap->cmdchar == 'g') {
   4885    name = cap->extra_char;
   4886    flags |= KMarkNoContext;
   4887  } else {
   4888    name = cap->nchar;
   4889    flags |= kMarkContext;
   4890  }
   4891  flags |= cap->arg ? kMarkBeginLine : 0;
   4892  flags |= cap->count0 ? kMarkSetView : 0;
   4893 
   4894  fmark_T *fm = mark_get(curbuf, curwin, NULL, kMarkAll, name);
   4895  move_res = nv_mark_move_to(cap, flags, fm);
   4896 
   4897  // May need to clear the coladd that a mark includes.
   4898  if (!virtual_active(curwin)) {
   4899    curwin->w_cursor.coladd = 0;
   4900  }
   4901 
   4902  if (cap->oap->op_type == OP_NOP
   4903      && move_res & kMarkMoveSuccess
   4904      && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedCursor)
   4905      && (fdo_flags & kOptFdoFlagMark)
   4906      && old_KeyTyped) {
   4907    foldOpenCursor();
   4908  }
   4909 }
   4910 
   4911 /// Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands.
   4912 /// Movement in the jumplist and changelist.
   4913 static void nv_pcmark(cmdarg_T *cap)
   4914 {
   4915  fmark_T *fm = NULL;
   4916  MarkMove flags = jop_flags & kOptJopFlagView ? kMarkSetView : 0;  // flags for moving to the mark
   4917  MarkMoveRes move_res = 0;  // Result from moving to the mark
   4918  const bool old_KeyTyped = KeyTyped;  // getting file may reset it.
   4919 
   4920  if (checkclearopq(cap->oap)) {
   4921    return;
   4922  }
   4923 
   4924  if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL) {
   4925    if (!goto_tabpage_lastused()) {
   4926      clearopbeep(cap->oap);
   4927    }
   4928    return;
   4929  }
   4930 
   4931  if (cap->cmdchar == 'g') {
   4932    fm = get_changelist(curbuf, curwin, cap->count1);
   4933  } else {
   4934    fm = get_jumplist(curwin, cap->count1);
   4935    flags |= KMarkNoContext | kMarkJumpList;
   4936  }
   4937  // Changelist and jumplist have their own error messages. Therefore avoid
   4938  // calling nv_mark_move_to() when not found to avoid incorrect error
   4939  // messages.
   4940  if (fm != NULL) {
   4941    move_res = nv_mark_move_to(cap, flags, fm);
   4942  } else if (cap->cmdchar == 'g') {
   4943    if (curbuf->b_changelistlen == 0) {
   4944      emsg(_(e_changelist_is_empty));
   4945    } else if (cap->count1 < 0) {
   4946      emsg(_("E662: At start of changelist"));
   4947    } else {
   4948      emsg(_("E663: At end of changelist"));
   4949    }
   4950  } else {
   4951    clearopbeep(cap->oap);
   4952  }
   4953  if (cap->oap->op_type == OP_NOP
   4954      && (move_res & kMarkSwitchedBuf || move_res & kMarkChangedLine)
   4955      && (fdo_flags & kOptFdoFlagMark)
   4956      && old_KeyTyped) {
   4957    foldOpenCursor();
   4958  }
   4959 }
   4960 
   4961 /// Handle '"' command.
   4962 static void nv_regname(cmdarg_T *cap)
   4963 {
   4964  if (checkclearop(cap->oap)) {
   4965    return;
   4966  }
   4967  if (cap->nchar == '=') {
   4968    cap->nchar = get_expr_register();
   4969  }
   4970  if (cap->nchar != NUL && valid_yank_reg(cap->nchar, false)) {
   4971    cap->oap->regname = cap->nchar;
   4972    cap->opcount = cap->count0;         // remember count before '"'
   4973    set_reg_var(cap->oap->regname);
   4974  } else {
   4975    clearopbeep(cap->oap);
   4976  }
   4977 }
   4978 
   4979 /// Handle "v", "V" and "CTRL-V" commands.
   4980 /// Also for "gh", "gH" and "g^H" commands: Always start Select mode, cap->arg
   4981 /// is true.
   4982 /// Handle CTRL-Q just like CTRL-V.
   4983 static void nv_visual(cmdarg_T *cap)
   4984 {
   4985  if (cap->cmdchar == Ctrl_Q) {
   4986    cap->cmdchar = Ctrl_V;
   4987  }
   4988 
   4989  // 'v', 'V' and CTRL-V can be used while an operator is pending to make it
   4990  // charwise, linewise, or blockwise.
   4991  if (cap->oap->op_type != OP_NOP) {
   4992    motion_force = cap->oap->motion_force = cap->cmdchar;
   4993    finish_op = false;          // operator doesn't finish now but later
   4994    return;
   4995  }
   4996 
   4997  VIsual_select = cap->arg;
   4998  if (VIsual_active) {      // change Visual mode
   4999    if (VIsual_mode == cap->cmdchar) {      // stop visual mode
   5000      end_visual_mode();
   5001    } else {                                  // toggle char/block mode
   5002                                              //           or char/line mode
   5003      VIsual_mode = cap->cmdchar;
   5004      showmode();
   5005      may_trigger_modechanged();
   5006    }
   5007    redraw_curbuf_later(UPD_INVERTED);  // update the inversion
   5008  } else {                // start Visual mode
   5009    if (cap->count0 > 0 && resel_VIsual_mode != NUL) {
   5010      // use previously selected part
   5011      VIsual = curwin->w_cursor;
   5012 
   5013      VIsual_active = true;
   5014      VIsual_reselect = true;
   5015      if (!cap->arg) {
   5016        // start Select mode when 'selectmode' contains "cmd"
   5017        may_start_select('c');
   5018      }
   5019      setmouse();
   5020      if (p_smd && msg_silent == 0) {
   5021        redraw_cmdline = true;              // show visual mode later
   5022      }
   5023      // For V and ^V, we multiply the number of lines even if there
   5024      // was only one -- webb
   5025      if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) {
   5026        curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1;
   5027        check_cursor(curwin);
   5028      }
   5029      VIsual_mode = resel_VIsual_mode;
   5030      if (VIsual_mode == 'v') {
   5031        if (resel_VIsual_line_count <= 1) {
   5032          update_curswant_force();
   5033          assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
   5034          curwin->w_curswant += resel_VIsual_vcol * cap->count0;
   5035          if (*p_sel != 'e') {
   5036            curwin->w_curswant--;
   5037          }
   5038        } else {
   5039          curwin->w_curswant = resel_VIsual_vcol;
   5040        }
   5041        coladvance(curwin, curwin->w_curswant);
   5042      }
   5043      if (resel_VIsual_vcol == MAXCOL) {
   5044        curwin->w_curswant = MAXCOL;
   5045        coladvance(curwin, MAXCOL);
   5046      } else if (VIsual_mode == Ctrl_V) {
   5047        // Update curswant on the original line, that is where "col" is valid.
   5048        linenr_T lnum = curwin->w_cursor.lnum;
   5049        curwin->w_cursor.lnum = VIsual.lnum;
   5050        update_curswant_force();
   5051        assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
   5052        curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1;
   5053        curwin->w_cursor.lnum = lnum;
   5054        if (*p_sel == 'e') {
   5055          curwin->w_curswant++;
   5056        }
   5057        coladvance(curwin, curwin->w_curswant);
   5058      } else {
   5059        curwin->w_set_curswant = true;
   5060      }
   5061      redraw_curbuf_later(UPD_INVERTED);  // show the inversion
   5062    } else {
   5063      if (!cap->arg) {
   5064        // start Select mode when 'selectmode' contains "cmd"
   5065        may_start_select('c');
   5066      }
   5067      n_start_visual_mode(cap->cmdchar);
   5068      if (VIsual_mode != 'V' && *p_sel == 'e') {
   5069        cap->count1++;          // include one more char
   5070      } else {
   5071        VIsual_select_exclu_adj = false;
   5072      }
   5073      if (cap->count0 > 0 && --cap->count1 > 0) {
   5074        // With a count select that many characters or lines.
   5075        if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V) {
   5076          nv_right(cap);
   5077        } else if (VIsual_mode == 'V') {
   5078          nv_down(cap);
   5079        }
   5080      }
   5081    }
   5082  }
   5083 }
   5084 
   5085 /// Start selection for Shift-movement keys.
   5086 void start_selection(void)
   5087 {
   5088  // if 'selectmode' contains "key", start Select mode
   5089  may_start_select('k');
   5090  n_start_visual_mode('v');
   5091 }
   5092 
   5093 /// Start Select mode, if "c" is in 'selectmode' and not in a mapping or menu.
   5094 /// When "c" is 'o' (checking for "mouse") then also when mapped.
   5095 void may_start_select(int c)
   5096 {
   5097  VIsual_select = (c == 'o' || (stuff_empty() && typebuf_typed()))
   5098                  && vim_strchr(p_slm, c) != NULL;
   5099 }
   5100 
   5101 /// Start Visual mode "c".
   5102 /// Should set VIsual_select before calling this.
   5103 static void n_start_visual_mode(int c)
   5104 {
   5105  VIsual_mode = c;
   5106  VIsual_active = true;
   5107  VIsual_reselect = true;
   5108  // Corner case: the 0 position in a tab may change when going into
   5109  // virtualedit.  Recalculate curwin->w_cursor to avoid bad highlighting.
   5110  //
   5111  if (c == Ctrl_V && (get_ve_flags(curwin) & kOptVeFlagBlock) && gchar_cursor() == TAB) {
   5112    validate_virtcol(curwin);
   5113    coladvance(curwin, curwin->w_virtcol);
   5114  }
   5115  VIsual = curwin->w_cursor;
   5116 
   5117  foldAdjustVisual();
   5118 
   5119  may_trigger_modechanged();
   5120  setmouse();
   5121  // Check for redraw after changing the state.
   5122  conceal_check_cursor_line();
   5123 
   5124  if (p_smd && msg_silent == 0) {
   5125    redraw_cmdline = true;      // show visual mode later
   5126  }
   5127  // Only need to redraw this line, unless still need to redraw an old
   5128  // Visual area (when 'lazyredraw' is set).
   5129  if (curwin->w_redr_type < UPD_INVERTED) {
   5130    curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
   5131    curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
   5132  }
   5133  redraw_curbuf_later(UPD_VALID);
   5134 }
   5135 
   5136 /// CTRL-W: Window commands
   5137 static void nv_window(cmdarg_T *cap)
   5138 {
   5139  if (cap->nchar == ':') {
   5140    // "CTRL-W :" is the same as typing ":"; useful in a terminal window
   5141    cap->cmdchar = ':';
   5142    cap->nchar = NUL;
   5143    nv_colon(cap);
   5144  } else if (!checkclearop(cap->oap)) {
   5145    do_window(cap->nchar, cap->count0, NUL);  // everything is in window.c
   5146  }
   5147 }
   5148 
   5149 /// CTRL-Z: Suspend
   5150 static void nv_suspend(cmdarg_T *cap)
   5151 {
   5152  clearop(cap->oap);
   5153  if (VIsual_active) {
   5154    end_visual_mode();                  // stop Visual mode
   5155  }
   5156  do_cmdline_cmd("st");
   5157 }
   5158 
   5159 /// "gv": Reselect the previous Visual area.  If Visual already active,
   5160 ///       exchange previous and current Visual area.
   5161 static void nv_gv_cmd(cmdarg_T *cap)
   5162 {
   5163  if (curbuf->b_visual.vi_start.lnum == 0
   5164      || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count
   5165      || curbuf->b_visual.vi_end.lnum == 0) {
   5166    beep_flush();
   5167    return;
   5168  }
   5169 
   5170  pos_T tpos;
   5171  // set w_cursor to the start of the Visual area, tpos to the end
   5172  if (VIsual_active) {
   5173    int i = VIsual_mode;
   5174    VIsual_mode = curbuf->b_visual.vi_mode;
   5175    curbuf->b_visual.vi_mode = i;
   5176    curbuf->b_visual_mode_eval = i;
   5177    i = curwin->w_curswant;
   5178    curwin->w_curswant = curbuf->b_visual.vi_curswant;
   5179    curbuf->b_visual.vi_curswant = i;
   5180 
   5181    tpos = curbuf->b_visual.vi_end;
   5182    curbuf->b_visual.vi_end = curwin->w_cursor;
   5183    curwin->w_cursor = curbuf->b_visual.vi_start;
   5184    curbuf->b_visual.vi_start = VIsual;
   5185  } else {
   5186    VIsual_mode = curbuf->b_visual.vi_mode;
   5187    curwin->w_curswant = curbuf->b_visual.vi_curswant;
   5188    tpos = curbuf->b_visual.vi_end;
   5189    curwin->w_cursor = curbuf->b_visual.vi_start;
   5190  }
   5191 
   5192  VIsual_active = true;
   5193  VIsual_reselect = true;
   5194 
   5195  // Set Visual to the start and w_cursor to the end of the Visual
   5196  // area.  Make sure they are on an existing character.
   5197  check_cursor(curwin);
   5198  VIsual = curwin->w_cursor;
   5199  curwin->w_cursor = tpos;
   5200  check_cursor(curwin);
   5201  update_topline(curwin);
   5202 
   5203  // When called from normal "g" command: start Select mode when
   5204  // 'selectmode' contains "cmd".  When called for K_SELECT, always
   5205  // start Select mode.
   5206  if (cap->arg) {
   5207    VIsual_select = true;
   5208    VIsual_select_reg = 0;
   5209  } else {
   5210    may_start_select('c');
   5211  }
   5212  setmouse();
   5213  redraw_curbuf_later(UPD_INVERTED);
   5214  showmode();
   5215 }
   5216 
   5217 /// "g0", "g^" : Like "0" and "^" but for screen lines.
   5218 /// "gm": middle of "g0" and "g$".
   5219 void nv_g_home_m_cmd(cmdarg_T *cap)
   5220 {
   5221  int i;
   5222  const bool flag = cap->nchar == '^';
   5223 
   5224  cap->oap->motion_type = kMTCharWise;
   5225  cap->oap->inclusive = false;
   5226  if (curwin->w_p_wrap && curwin->w_view_width != 0) {
   5227    int width1 = curwin->w_view_width - win_col_off(curwin);
   5228    int width2 = width1 + win_col_off2(curwin);
   5229 
   5230    validate_virtcol(curwin);
   5231    i = 0;
   5232    if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) {
   5233      i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
   5234    }
   5235 
   5236    // When ending up below 'smoothscroll' marker, move just beyond it so
   5237    // that skipcol is not adjusted later.
   5238    if (curwin->w_skipcol > 0 && curwin->w_cursor.lnum == curwin->w_topline) {
   5239      int overlap = sms_marker_overlap(curwin, curwin->w_view_width - width2);
   5240      if (overlap > 0 && i == curwin->w_skipcol) {
   5241        i += overlap;
   5242      }
   5243    }
   5244  } else {
   5245    i = curwin->w_leftcol;
   5246  }
   5247  // Go to the middle of the screen line.  When 'number' or
   5248  // 'relativenumber' is on and lines are wrapping the middle can be more
   5249  // to the left.
   5250  if (cap->nchar == 'm') {
   5251    i += (curwin->w_view_width - win_col_off(curwin)
   5252          + ((curwin->w_p_wrap && i > 0) ? win_col_off2(curwin) : 0)) / 2;
   5253  }
   5254  coladvance(curwin, (colnr_T)i);
   5255  if (flag) {
   5256    do {
   5257      i = gchar_cursor();
   5258    } while (ascii_iswhite(i) && oneright() == OK);
   5259    curwin->w_valid &= ~VALID_WCOL;
   5260  }
   5261  curwin->w_set_curswant = true;
   5262  if (hasAnyFolding(curwin)) {
   5263    validate_cheight(curwin);
   5264    if (curwin->w_cline_folded) {
   5265      update_curswant_force();
   5266    }
   5267  }
   5268  adjust_skipcol();
   5269 }
   5270 
   5271 /// "g_": to the last non-blank character in the line or <count> lines downward.
   5272 static void nv_g_underscore_cmd(cmdarg_T *cap)
   5273 {
   5274  cap->oap->motion_type = kMTCharWise;
   5275  cap->oap->inclusive = true;
   5276  curwin->w_curswant = MAXCOL;
   5277  if (cursor_down(cap->count1 - 1, cap->oap->op_type == OP_NOP) == false) {
   5278    clearopbeep(cap->oap);
   5279    return;
   5280  }
   5281 
   5282  char *ptr = get_cursor_line_ptr();
   5283 
   5284  // In Visual mode we may end up after the line.
   5285  if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) {
   5286    curwin->w_cursor.col--;
   5287  }
   5288 
   5289  // Decrease the cursor column until it's on a non-blank.
   5290  while (curwin->w_cursor.col > 0 && ascii_iswhite(ptr[curwin->w_cursor.col])) {
   5291    curwin->w_cursor.col--;
   5292  }
   5293  curwin->w_set_curswant = true;
   5294  adjust_for_sel(cap);
   5295 }
   5296 
   5297 /// "g$" : Like "$" but for screen lines.
   5298 static void nv_g_dollar_cmd(cmdarg_T *cap)
   5299 {
   5300  oparg_T *oap = cap->oap;
   5301  int i;
   5302  int col_off = win_col_off(curwin);
   5303  const bool flag = cap->nchar == K_END || cap->nchar == K_KEND;
   5304 
   5305  oap->motion_type = kMTCharWise;
   5306  oap->inclusive = true;
   5307  if (curwin->w_p_wrap && curwin->w_view_width != 0) {
   5308    curwin->w_curswant = MAXCOL;              // so we stay at the end
   5309    if (cap->count1 == 1) {
   5310      int width1 = curwin->w_view_width - col_off;
   5311      int width2 = width1 + win_col_off2(curwin);
   5312 
   5313      validate_virtcol(curwin);
   5314      i = width1 - 1;
   5315      if (curwin->w_virtcol >= (colnr_T)width1) {
   5316        i += ((curwin->w_virtcol - width1) / width2 + 1) * width2;
   5317      }
   5318      coladvance(curwin, (colnr_T)i);
   5319 
   5320      // Make sure we stick in this column.
   5321      update_curswant_force();
   5322      if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) {
   5323        // Check for landing on a character that got split at
   5324        // the end of the line.  We do not want to advance to
   5325        // the next screen line.
   5326        if (curwin->w_virtcol > (colnr_T)i) {
   5327          curwin->w_cursor.col--;
   5328        }
   5329      }
   5330    } else if (nv_screengo(oap, FORWARD, cap->count1 - 1, false) == false) {
   5331      clearopbeep(oap);
   5332    }
   5333  } else {
   5334    if (cap->count1 > 1) {
   5335      // if it fails, let the cursor still move to the last char
   5336      cursor_down(cap->count1 - 1, false);
   5337    }
   5338    i = curwin->w_leftcol + curwin->w_view_width - col_off - 1;
   5339    coladvance(curwin, (colnr_T)i);
   5340 
   5341    // if the character doesn't fit move one back
   5342    if (curwin->w_cursor.col > 0 && utf_ptr2cells(get_cursor_pos_ptr()) > 1) {
   5343      colnr_T vcol;
   5344 
   5345      getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol);
   5346      if (vcol >= curwin->w_leftcol + curwin->w_view_width - col_off) {
   5347        curwin->w_cursor.col--;
   5348      }
   5349    }
   5350 
   5351    // Make sure we stick in this column.
   5352    update_curswant_force();
   5353  }
   5354  if (flag) {
   5355    do {
   5356      i = gchar_cursor();
   5357    } while (ascii_iswhite_or_nul(i) && oneleft() == OK);
   5358    curwin->w_valid &= ~VALID_WCOL;
   5359  }
   5360 }
   5361 
   5362 /// "gi": start Insert at the last position.
   5363 static void nv_gi_cmd(cmdarg_T *cap)
   5364 {
   5365  if (curbuf->b_last_insert.mark.lnum != 0) {
   5366    curwin->w_cursor = curbuf->b_last_insert.mark;
   5367    check_cursor_lnum(curwin);
   5368    int i = (int)get_cursor_line_len();
   5369    if (curwin->w_cursor.col > (colnr_T)i) {
   5370      if (virtual_active(curwin)) {
   5371        curwin->w_cursor.coladd += curwin->w_cursor.col - i;
   5372      }
   5373      curwin->w_cursor.col = i;
   5374    }
   5375  }
   5376  cap->cmdchar = 'i';
   5377  nv_edit(cap);
   5378 }
   5379 
   5380 /// Commands starting with "g".
   5381 static void nv_g_cmd(cmdarg_T *cap)
   5382 {
   5383  oparg_T *oap = cap->oap;
   5384  int i;
   5385 
   5386  switch (cap->nchar) {
   5387  // "g^A/g^X": Sequentially increment visually selected region.
   5388  case Ctrl_A:
   5389  case Ctrl_X:
   5390    if (VIsual_active) {
   5391      cap->arg = true;
   5392      cap->cmdchar = cap->nchar;
   5393      cap->nchar = NUL;
   5394      nv_addsub(cap);
   5395    } else {
   5396      clearopbeep(oap);
   5397    }
   5398    break;
   5399 
   5400  // "gR": Enter virtual replace mode.
   5401  case 'R':
   5402    cap->arg = true;
   5403    nv_Replace(cap);
   5404    break;
   5405 
   5406  case 'r':
   5407    nv_vreplace(cap);
   5408    break;
   5409 
   5410  case '&':
   5411    do_cmdline_cmd("%s//~/&");
   5412    break;
   5413 
   5414  // "gv": Reselect the previous Visual area.  If Visual already active,
   5415  //       exchange previous and current Visual area.
   5416  case 'v':
   5417    nv_gv_cmd(cap);
   5418    break;
   5419  // "gV": Don't reselect the previous Visual area after a Select mode mapping of menu.
   5420  case 'V':
   5421    VIsual_reselect = false;
   5422    break;
   5423 
   5424  // "gh":  start Select mode.
   5425  // "gH":  start Select line mode.
   5426  // "g^H": start Select block mode.
   5427  case K_BS:
   5428    cap->nchar = Ctrl_H;
   5429    FALLTHROUGH;
   5430  case 'h':
   5431  case 'H':
   5432  case Ctrl_H:
   5433    cap->cmdchar = cap->nchar + ('v' - 'h');
   5434    cap->arg = true;
   5435    nv_visual(cap);
   5436    break;
   5437 
   5438  // "gn", "gN" visually select next/previous search match
   5439  // "gn" selects next match
   5440  // "gN" selects previous match
   5441  case 'N':
   5442  case 'n':
   5443    if (!current_search(cap->count1, cap->nchar == 'n')) {
   5444      clearopbeep(oap);
   5445    }
   5446    break;
   5447 
   5448  // "gj" and "gk" two new funny movement keys -- up and down
   5449  // movement based on *screen* line rather than *file* line.
   5450  case 'j':
   5451  case K_DOWN:
   5452    // with 'nowrap' it works just like the normal "j" command.
   5453    if (!curwin->w_p_wrap) {
   5454      oap->motion_type = kMTLineWise;
   5455      i = cursor_down(cap->count1, oap->op_type == OP_NOP);
   5456    } else {
   5457      i = nv_screengo(oap, FORWARD, cap->count1, false);
   5458    }
   5459    if (!i) {
   5460      clearopbeep(oap);
   5461    }
   5462    break;
   5463 
   5464  case 'k':
   5465  case K_UP:
   5466    // with 'nowrap' it works just like the normal "k" command.
   5467    if (!curwin->w_p_wrap) {
   5468      oap->motion_type = kMTLineWise;
   5469      i = cursor_up(cap->count1, oap->op_type == OP_NOP);
   5470    } else {
   5471      i = nv_screengo(oap, BACKWARD, cap->count1, false);
   5472    }
   5473    if (!i) {
   5474      clearopbeep(oap);
   5475    }
   5476    break;
   5477 
   5478  // "gJ": join two lines without inserting a space.
   5479  case 'J':
   5480    nv_join(cap);
   5481    break;
   5482 
   5483  // "g0", "g^" : Like "0" and "^" but for screen lines.
   5484  // "gm": middle of "g0" and "g$".
   5485  case '^':
   5486  case '0':
   5487  case 'm':
   5488  case K_HOME:
   5489  case K_KHOME:
   5490    nv_g_home_m_cmd(cap);
   5491    break;
   5492 
   5493  case 'M':
   5494    oap->motion_type = kMTCharWise;
   5495    oap->inclusive = false;
   5496    i = linetabsize(curwin, curwin->w_cursor.lnum);
   5497    if (cap->count0 > 0 && cap->count0 <= 100) {
   5498      coladvance(curwin, (colnr_T)(i * cap->count0 / 100));
   5499    } else {
   5500      coladvance(curwin, (colnr_T)(i / 2));
   5501    }
   5502    curwin->w_set_curswant = true;
   5503    break;
   5504 
   5505  // "g_": to the last non-blank character in the line or <count> lines downward.
   5506  case '_':
   5507    nv_g_underscore_cmd(cap);
   5508    break;
   5509 
   5510  // "g$" : Like "$" but for screen lines.
   5511  case '$':
   5512  case K_END:
   5513  case K_KEND:
   5514    nv_g_dollar_cmd(cap);
   5515    break;
   5516 
   5517  // "g*" and "g#", like "*" and "#" but without using "\<" and "\>"
   5518  case '*':
   5519  case '#':
   5520 #if POUND != '#'
   5521  case POUND:           // pound sign (sometimes equal to '#')
   5522 #endif
   5523  case Ctrl_RSB:                // :tag or :tselect for current identifier
   5524  case ']':                     // :tselect for current identifier
   5525    nv_ident(cap);
   5526    break;
   5527 
   5528  // ge and gE: go back to end of word
   5529  case 'e':
   5530  case 'E':
   5531    oap->motion_type = kMTCharWise;
   5532    curwin->w_set_curswant = true;
   5533    oap->inclusive = true;
   5534    if (bckend_word(cap->count1, cap->nchar == 'E', false) == false) {
   5535      clearopbeep(oap);
   5536    }
   5537    break;
   5538 
   5539  // "g CTRL-G": display info about cursor position
   5540  case Ctrl_G:
   5541    cursor_pos_info(NULL);
   5542    break;
   5543 
   5544  // "gi": start Insert at the last position.
   5545  case 'i':
   5546    nv_gi_cmd(cap);
   5547    break;
   5548 
   5549  // "gI": Start insert in column 1.
   5550  case 'I':
   5551    beginline(0);
   5552    if (!checkclearopq(oap)) {
   5553      invoke_edit(cap, false, 'g', false);
   5554    }
   5555    break;
   5556 
   5557  // "gf": goto file, edit file under cursor
   5558  // "]f" and "[f": can also be used.
   5559  case 'f':
   5560  case 'F':
   5561    nv_gotofile(cap);
   5562    break;
   5563 
   5564  // "g'm" and "g`m": jump to mark without setting pcmark
   5565  case '\'':
   5566    cap->arg = true;
   5567    FALLTHROUGH;
   5568  case '`':
   5569    nv_gomark(cap);
   5570    break;
   5571 
   5572  // "gs": Goto sleep.
   5573  case 's':
   5574    do_sleep(cap->count1 * 1000, false);
   5575    break;
   5576 
   5577  // "ga": Display the ascii value of the character under the
   5578  // cursor.    It is displayed in decimal, hex, and octal. -- webb
   5579  case 'a':
   5580    do_ascii(NULL);
   5581    break;
   5582 
   5583  // "g8": Display the bytes used for the UTF-8 character under the
   5584  // cursor.    It is displayed in hex.
   5585  // "8g8" finds illegal byte sequence.
   5586  case '8':
   5587    if (cap->count0 == 8) {
   5588      utf_find_illegal();
   5589    } else {
   5590      show_utf8();
   5591    }
   5592    break;
   5593  // "g<": show scrollback text
   5594  case '<':
   5595    show_sb_text();
   5596    break;
   5597 
   5598  // "gg": Goto the first line in file.  With a count it goes to
   5599  // that line number like for "G". -- webb
   5600  case 'g':
   5601    cap->arg = false;
   5602    nv_goto(cap);
   5603    break;
   5604 
   5605  //  Two-character operators:
   5606  //  "gq"       Format text
   5607  //  "gw"       Format text and keep cursor position
   5608  //  "g~"       Toggle the case of the text.
   5609  //  "gu"       Change text to lower case.
   5610  //  "gU"       Change text to upper case.
   5611  //  "g?"       rot13 encoding
   5612  //  "g@"       call 'operatorfunc'
   5613  case 'q':
   5614  case 'w':
   5615    oap->cursor_start = curwin->w_cursor;
   5616    FALLTHROUGH;
   5617  case '~':
   5618  case 'u':
   5619  case 'U':
   5620  case '?':
   5621  case '@':
   5622    nv_operator(cap);
   5623    break;
   5624 
   5625  // "gd": Find first occurrence of pattern under the cursor in the current function
   5626  // "gD": idem, but in the current file.
   5627  case 'd':
   5628  case 'D':
   5629    nv_gd(oap, cap->nchar, cap->count0);
   5630    break;
   5631 
   5632  // g<*Mouse> : <C-*mouse>
   5633  case K_MIDDLEMOUSE:
   5634  case K_MIDDLEDRAG:
   5635  case K_MIDDLERELEASE:
   5636  case K_LEFTMOUSE:
   5637  case K_LEFTDRAG:
   5638  case K_LEFTRELEASE:
   5639  case K_MOUSEMOVE:
   5640  case K_RIGHTMOUSE:
   5641  case K_RIGHTDRAG:
   5642  case K_RIGHTRELEASE:
   5643  case K_X1MOUSE:
   5644  case K_X1DRAG:
   5645  case K_X1RELEASE:
   5646  case K_X2MOUSE:
   5647  case K_X2DRAG:
   5648  case K_X2RELEASE:
   5649    mod_mask = MOD_MASK_CTRL;
   5650    do_mouse(oap, cap->nchar, BACKWARD, cap->count1, 0);
   5651    break;
   5652 
   5653  case K_IGNORE:
   5654    break;
   5655 
   5656  // "gP" and "gp": same as "P" and "p" but leave cursor just after new text
   5657  case 'p':
   5658  case 'P':
   5659    nv_put(cap);
   5660    break;
   5661 
   5662  // "go": goto byte count from start of buffer
   5663  case 'o':
   5664    oap->inclusive = false;
   5665    goto_byte(cap->count0);
   5666    break;
   5667 
   5668  // "gQ": improved Ex mode
   5669  case 'Q':
   5670    if (!check_text_locked(cap->oap) && !checkclearopq(oap)) {
   5671      do_exmode();
   5672    }
   5673    break;
   5674 
   5675  case ',':
   5676    nv_pcmark(cap);
   5677    break;
   5678 
   5679  case ';':
   5680    cap->count1 = -cap->count1;
   5681    nv_pcmark(cap);
   5682    break;
   5683 
   5684  case 't':
   5685    if (!checkclearop(oap)) {
   5686      goto_tabpage(cap->count0);
   5687    }
   5688    break;
   5689  case 'T':
   5690    if (!checkclearop(oap)) {
   5691      goto_tabpage(-cap->count1);
   5692    }
   5693    break;
   5694 
   5695  case TAB:
   5696    if (!checkclearop(oap) && !goto_tabpage_lastused()) {
   5697      clearopbeep(oap);
   5698    }
   5699    break;
   5700 
   5701  case '+':
   5702  case '-':   // "g+" and "g-": undo or redo along the timeline
   5703    if (!checkclearopq(oap)) {
   5704      undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1,
   5705                false, false, false);
   5706    }
   5707    break;
   5708 
   5709  default:
   5710    clearopbeep(oap);
   5711    break;
   5712  }
   5713 }
   5714 
   5715 /// Handle "o" and "O" commands.
   5716 static void n_opencmd(cmdarg_T *cap)
   5717 {
   5718  if (checkclearopq(cap->oap)) {
   5719    return;
   5720  }
   5721 
   5722  if (cap->cmdchar == 'O') {
   5723    // Open above the first line of a folded sequence of lines
   5724    hasFolding(curwin, curwin->w_cursor.lnum,
   5725               &curwin->w_cursor.lnum, NULL);
   5726  } else {
   5727    // Open below the last line of a folded sequence of lines
   5728    hasFolding(curwin, curwin->w_cursor.lnum,
   5729               NULL, &curwin->w_cursor.lnum);
   5730  }
   5731  // trigger TextChangedI for the 'o/O' command
   5732  curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf);
   5733  if (u_save(curwin->w_cursor.lnum - (cap->cmdchar == 'O' ? 1 : 0),
   5734             curwin->w_cursor.lnum + (cap->cmdchar == 'o' ? 1 : 0))
   5735      && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
   5736                   has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0,
   5737                   0, NULL)) {
   5738    if (win_cursorline_standout(curwin)) {
   5739      // force redraw of cursorline
   5740      curwin->w_valid &= ~VALID_CROW;
   5741    }
   5742    invoke_edit(cap, false, cap->cmdchar, true);
   5743  }
   5744 }
   5745 
   5746 /// "." command: redo last change.
   5747 static void nv_dot(cmdarg_T *cap)
   5748 {
   5749  if (checkclearopq(cap->oap)) {
   5750    return;
   5751  }
   5752 
   5753  // If "restart_edit" is true, the last but one command is repeated
   5754  // instead of the last command (inserting text). This is used for
   5755  // CTRL-O <.> in insert mode.
   5756  if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == false) {
   5757    clearopbeep(cap->oap);
   5758  }
   5759 }
   5760 
   5761 /// CTRL-R: undo undo or specify register in select mode
   5762 static void nv_redo_or_register(cmdarg_T *cap)
   5763 {
   5764  if (VIsual_select && VIsual_active) {
   5765    // Get register name
   5766    no_mapping++;
   5767    int reg = plain_vgetc();
   5768    LANGMAP_ADJUST(reg, true);
   5769    no_mapping--;
   5770 
   5771    if (reg == '"') {
   5772      // the unnamed register is 0
   5773      reg = 0;
   5774    }
   5775 
   5776    VIsual_select_reg = valid_yank_reg(reg, true) ? reg : 0;
   5777    return;
   5778  }
   5779 
   5780  if (checkclearopq(cap->oap)) {
   5781    return;
   5782  }
   5783 
   5784  u_redo(cap->count1);
   5785  curwin->w_set_curswant = true;
   5786 }
   5787 
   5788 /// Handle "U" command.
   5789 static void nv_Undo(cmdarg_T *cap)
   5790 {
   5791  // In Visual mode and typing "gUU" triggers an operator
   5792  if (cap->oap->op_type == OP_UPPER || VIsual_active) {
   5793    // translate "gUU" to "gUgU"
   5794    cap->cmdchar = 'g';
   5795    cap->nchar = 'U';
   5796    nv_operator(cap);
   5797    return;
   5798  }
   5799 
   5800  if (checkclearopq(cap->oap)) {
   5801    return;
   5802  }
   5803 
   5804  u_undoline();
   5805  curwin->w_set_curswant = true;
   5806 }
   5807 
   5808 /// '~' command: If tilde is not an operator and Visual is off: swap case of a
   5809 /// single character.
   5810 static void nv_tilde(cmdarg_T *cap)
   5811 {
   5812  if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE) {
   5813    if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
   5814      clearopbeep(cap->oap);
   5815      return;
   5816    }
   5817    n_swapchar(cap);
   5818  } else {
   5819    nv_operator(cap);
   5820  }
   5821 }
   5822 
   5823 /// Handle an operator command.
   5824 /// The actual work is done by do_pending_operator().
   5825 static void nv_operator(cmdarg_T *cap)
   5826 {
   5827  int op_type = get_op_type(cap->cmdchar, cap->nchar);
   5828 
   5829  if (bt_prompt(curbuf) && op_is_change(op_type)
   5830      && !prompt_curpos_editable()) {
   5831    clearopbeep(cap->oap);
   5832    return;
   5833  }
   5834 
   5835  if (op_type == cap->oap->op_type) {       // double operator works on lines
   5836    nv_lineop(cap);
   5837  } else if (!checkclearop(cap->oap)) {
   5838    cap->oap->start = curwin->w_cursor;
   5839    cap->oap->op_type = op_type;
   5840    set_op_var(op_type);
   5841  }
   5842 }
   5843 
   5844 /// Set v:operator to the characters for "optype".
   5845 static void set_op_var(int optype)
   5846 {
   5847  if (optype == OP_NOP) {
   5848    set_vim_var_string(VV_OP, NULL, 0);
   5849  } else {
   5850    char opchars[3];
   5851    int opchar0 = get_op_char(optype);
   5852    assert(opchar0 >= 0 && opchar0 <= UCHAR_MAX);
   5853    opchars[0] = (char)opchar0;
   5854 
   5855    int opchar1 = get_extra_op_char(optype);
   5856    assert(opchar1 >= 0 && opchar1 <= UCHAR_MAX);
   5857    opchars[1] = (char)opchar1;
   5858 
   5859    opchars[2] = NUL;
   5860    set_vim_var_string(VV_OP, opchars, -1);
   5861  }
   5862 }
   5863 
   5864 /// Handle linewise operator "dd", "yy", etc.
   5865 ///
   5866 /// "_" is is a strange motion command that helps make operators more logical.
   5867 /// It is actually implemented, but not documented in the real Vi.  This motion
   5868 /// command actually refers to "the current line".  Commands like "dd" and "yy"
   5869 /// are really an alternate form of "d_" and "y_".  It does accept a count, so
   5870 /// "d3_" works to delete 3 lines.
   5871 static void nv_lineop(cmdarg_T *cap)
   5872 {
   5873  cap->oap->motion_type = kMTLineWise;
   5874  if (cursor_down(cap->count1 - 1, cap->oap->op_type == OP_NOP) == false) {
   5875    clearopbeep(cap->oap);
   5876  } else if ((cap->oap->op_type == OP_DELETE
   5877              // only with linewise motions
   5878              && cap->oap->motion_force != 'v'
   5879              && cap->oap->motion_force != Ctrl_V)
   5880             || cap->oap->op_type == OP_LSHIFT
   5881             || cap->oap->op_type == OP_RSHIFT) {
   5882    beginline(BL_SOL | BL_FIX);
   5883  } else if (cap->oap->op_type != OP_YANK) {  // 'Y' does not move cursor
   5884    beginline(BL_WHITE | BL_FIX);
   5885  }
   5886 }
   5887 
   5888 /// <Home> command.
   5889 static void nv_home(cmdarg_T *cap)
   5890 {
   5891  // CTRL-HOME is like "gg"
   5892  if (mod_mask & MOD_MASK_CTRL) {
   5893    nv_goto(cap);
   5894  } else {
   5895    cap->count0 = 1;
   5896    nv_pipe(cap);
   5897  }
   5898  ins_at_eol = false;       // Don't move cursor past eol (only necessary in a
   5899                            // one-character line).
   5900 }
   5901 
   5902 /// "|" command.
   5903 static void nv_pipe(cmdarg_T *cap)
   5904 {
   5905  cap->oap->motion_type = kMTCharWise;
   5906  cap->oap->inclusive = false;
   5907  beginline(0);
   5908  if (cap->count0 > 0) {
   5909    coladvance(curwin, (colnr_T)(cap->count0 - 1));
   5910    curwin->w_curswant = (colnr_T)(cap->count0 - 1);
   5911  } else {
   5912    curwin->w_curswant = 0;
   5913  }
   5914  // keep curswant at the column where we wanted to go, not where
   5915  // we ended; differs if line is too short
   5916  curwin->w_set_curswant = false;
   5917 }
   5918 
   5919 /// Handle back-word command "b" and "B".
   5920 /// cap->arg is 1 for "B"
   5921 static void nv_bck_word(cmdarg_T *cap)
   5922 {
   5923  cap->oap->motion_type = kMTCharWise;
   5924  cap->oap->inclusive = false;
   5925  curwin->w_set_curswant = true;
   5926  if (bck_word(cap->count1, cap->arg, false) == false) {
   5927    clearopbeep(cap->oap);
   5928  } else if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
   5929    foldOpenCursor();
   5930  }
   5931 }
   5932 
   5933 /// Handle word motion commands "e", "E", "w" and "W".
   5934 /// cap->arg is true for "E" and "W".
   5935 static void nv_wordcmd(cmdarg_T *cap)
   5936 {
   5937  int n;
   5938  bool word_end;
   5939  bool flag = false;
   5940  pos_T startpos = curwin->w_cursor;
   5941 
   5942  // Set inclusive for the "E" and "e" command.
   5943  if (cap->cmdchar == 'e' || cap->cmdchar == 'E') {
   5944    word_end = true;
   5945  } else {
   5946    word_end = false;
   5947  }
   5948  cap->oap->inclusive = word_end;
   5949 
   5950  // "cw" and "cW" are a special case.
   5951  if (!word_end && cap->oap->op_type == OP_CHANGE) {
   5952    n = gchar_cursor();
   5953    if (n != NUL && !ascii_iswhite(n)) {
   5954      // This is a little strange.  To match what the real Vi does, we
   5955      // effectively map "cw" to "ce", and "cW" to "cE", provided that we are
   5956      // not on a space or a TAB.  This seems impolite at first, but it's
   5957      // really more what we mean when we say "cw".
   5958      //
   5959      // Another strangeness: When standing on the end of a word "ce" will
   5960      // change until the end of the next word, but "cw" will change only one
   5961      // character!  This is done by setting "flag".
   5962      if (vim_strchr(p_cpo, CPO_CHANGEW) != NULL) {
   5963        cap->oap->inclusive = true;
   5964        word_end = true;
   5965      }
   5966      flag = true;
   5967    }
   5968  }
   5969 
   5970  cap->oap->motion_type = kMTCharWise;
   5971  curwin->w_set_curswant = true;
   5972  if (word_end) {
   5973    n = end_word(cap->count1, cap->arg, flag, false);
   5974  } else {
   5975    n = fwd_word(cap->count1, cap->arg, cap->oap->op_type != OP_NOP);
   5976  }
   5977 
   5978  // Don't leave the cursor on the NUL past the end of line. Unless we
   5979  // didn't move it forward.
   5980  if (lt(startpos, curwin->w_cursor)) {
   5981    adjust_cursor(cap->oap);
   5982  }
   5983 
   5984  if (n == false && cap->oap->op_type == OP_NOP) {
   5985    clearopbeep(cap->oap);
   5986  } else {
   5987    adjust_for_sel(cap);
   5988    if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
   5989      foldOpenCursor();
   5990    }
   5991  }
   5992 }
   5993 
   5994 /// Used after a movement command: If the cursor ends up on the NUL after the
   5995 /// end of the line, may move it back to the last character and make the motion
   5996 /// inclusive.
   5997 static void adjust_cursor(oparg_T *oap)
   5998 {
   5999  // The cursor cannot remain on the NUL when:
   6000  // - the column is > 0
   6001  // - not in Visual mode or 'selection' is "o"
   6002  // - 'virtualedit' is not "all" and not "onemore".
   6003  if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
   6004      && (!VIsual_active || *p_sel == 'o')
   6005      && !virtual_active(curwin)
   6006      && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
   6007    curwin->w_cursor.col--;
   6008    // prevent cursor from moving on the trail byte
   6009    mb_adjust_cursor();
   6010    oap->inclusive = true;
   6011  }
   6012 }
   6013 
   6014 /// "0" and "^" commands.
   6015 /// cap->arg is the argument for beginline().
   6016 static void nv_beginline(cmdarg_T *cap)
   6017 {
   6018  cap->oap->motion_type = kMTCharWise;
   6019  cap->oap->inclusive = false;
   6020  beginline(cap->arg);
   6021  if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
   6022    foldOpenCursor();
   6023  }
   6024  ins_at_eol = false;       // Don't move cursor past eol (only necessary in a
   6025                            // one-character line).
   6026 }
   6027 
   6028 /// In exclusive Visual mode, may include the last character.
   6029 static void adjust_for_sel(cmdarg_T *cap)
   6030 {
   6031  if (VIsual_active && cap->oap->inclusive && *p_sel == 'e'
   6032      && gchar_cursor() != NUL && lt(VIsual, curwin->w_cursor)) {
   6033    inc_cursor();
   6034    cap->oap->inclusive = false;
   6035    VIsual_select_exclu_adj = true;
   6036  }
   6037 }
   6038 
   6039 /// Exclude last character at end of Visual area for 'selection' == "exclusive".
   6040 /// Should check VIsual_mode before calling this.
   6041 ///
   6042 /// @return  true when backed up to the previous line.
   6043 bool unadjust_for_sel(void)
   6044 {
   6045  if (*p_sel == 'e' && !equalpos(VIsual, curwin->w_cursor)) {
   6046    return unadjust_for_sel_inner(lt(VIsual, curwin->w_cursor)
   6047                                  ? &curwin->w_cursor : &VIsual);
   6048  }
   6049  return false;
   6050 }
   6051 
   6052 /// Move position "*pp" back one character for 'selection' == "exclusive".
   6053 ///
   6054 /// @return  true when backed up to the previous line.
   6055 bool unadjust_for_sel_inner(pos_T *pp)
   6056 {
   6057  VIsual_select_exclu_adj = false;
   6058 
   6059  if (pp->coladd > 0) {
   6060    pp->coladd--;
   6061  } else if (pp->col > 0) {
   6062    pp->col--;
   6063    mark_mb_adjustpos(curbuf, pp);
   6064    if (virtual_active(curwin)) {
   6065      colnr_T cs, ce;
   6066      getvcol(curwin, pp, &cs, NULL, &ce);
   6067      pp->coladd = ce - cs;
   6068    }
   6069  } else if (pp->lnum > 1) {
   6070    pp->lnum--;
   6071    pp->col = ml_get_len(pp->lnum);
   6072    return true;
   6073  }
   6074 
   6075  return false;
   6076 }
   6077 
   6078 /// SELECT key in Normal or Visual mode: end of Select mode mapping.
   6079 static void nv_select(cmdarg_T *cap)
   6080 {
   6081  if (VIsual_active) {
   6082    VIsual_select = true;
   6083    VIsual_select_reg = 0;
   6084  } else if (VIsual_reselect) {
   6085    cap->nchar = 'v';               // fake "gv" command
   6086    cap->arg = true;
   6087    nv_g_cmd(cap);
   6088  }
   6089 }
   6090 
   6091 /// "G", "gg", CTRL-END, CTRL-HOME.
   6092 /// cap->arg is true for "G".
   6093 static void nv_goto(cmdarg_T *cap)
   6094 {
   6095  linenr_T lnum;
   6096 
   6097  if (cap->arg) {
   6098    lnum = curbuf->b_ml.ml_line_count;
   6099  } else {
   6100    lnum = 1;
   6101  }
   6102  cap->oap->motion_type = kMTLineWise;
   6103  setpcmark();
   6104 
   6105  // When a count is given, use it instead of the default lnum
   6106  if (cap->count0 != 0) {
   6107    lnum = cap->count0;
   6108  }
   6109  lnum = MIN(MAX(lnum, 1), curbuf->b_ml.ml_line_count);
   6110  curwin->w_cursor.lnum = lnum;
   6111  beginline(BL_SOL | BL_FIX);
   6112  if ((fdo_flags & kOptFdoFlagJump) && KeyTyped && cap->oap->op_type == OP_NOP) {
   6113    foldOpenCursor();
   6114  }
   6115 }
   6116 
   6117 /// CTRL-\ in Normal mode.
   6118 static void nv_normal(cmdarg_T *cap)
   6119 {
   6120  if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G) {
   6121    clearop(cap->oap);
   6122    if (restart_edit != 0 && mode_displayed) {
   6123      clear_cmdline = true;                     // unshow mode later
   6124    }
   6125    restart_edit = 0;
   6126    if (cmdwin_type != 0) {
   6127      cmdwin_result = Ctrl_C;
   6128    }
   6129    if (VIsual_active) {
   6130      end_visual_mode();                // stop Visual
   6131      redraw_curbuf_later(UPD_INVERTED);
   6132    }
   6133  } else {
   6134    clearopbeep(cap->oap);
   6135  }
   6136 }
   6137 
   6138 /// ESC in Normal mode: beep, but don't flush buffers.
   6139 /// Don't even beep if we are canceling a command.
   6140 static void nv_esc(cmdarg_T *cap)
   6141 {
   6142  bool no_reason = (cap->oap->op_type == OP_NOP
   6143                    && cap->opcount == 0
   6144                    && cap->count0 == 0
   6145                    && cap->oap->regname == 0);
   6146 
   6147  if (cap->arg) {               // true for CTRL-C
   6148    if (restart_edit == 0 && cmdwin_type == 0 && !VIsual_active && no_reason) {
   6149      if (anyBufIsChanged()) {
   6150        msg(_("Type  :qa!  and press <Enter> to abandon all changes"
   6151              " and exit Nvim"), 0);
   6152      } else {
   6153        msg(_("Type  :qa  and press <Enter> to exit Nvim"), 0);
   6154      }
   6155    }
   6156 
   6157    if (restart_edit != 0) {
   6158      redraw_mode = true;  // remove "-- (insert) --"
   6159    }
   6160 
   6161    restart_edit = 0;
   6162 
   6163    if (cmdwin_type != 0) {
   6164      cmdwin_result = K_IGNORE;
   6165      got_int = false;          // don't stop executing autocommands et al.
   6166      return;
   6167    }
   6168  } else if (cmdwin_type != 0 && ex_normal_busy && typebuf_was_empty) {
   6169    // When :normal runs out of characters while in the command line window
   6170    // vgetorpeek() will repeatedly return ESC.  Exit the cmdline window to
   6171    // break the loop.
   6172    cmdwin_result = K_IGNORE;
   6173    return;
   6174  }
   6175 
   6176  if (VIsual_active) {
   6177    end_visual_mode();          // stop Visual
   6178    check_cursor_col(curwin);         // make sure cursor is not beyond EOL
   6179    curwin->w_set_curswant = true;
   6180    redraw_curbuf_later(UPD_INVERTED);
   6181  } else if (no_reason) {
   6182    vim_beep(kOptBoFlagEsc);
   6183  }
   6184  clearop(cap->oap);
   6185 }
   6186 
   6187 /// Move the cursor for the "A" command.
   6188 void set_cursor_for_append_to_line(void)
   6189 {
   6190  curwin->w_set_curswant = true;
   6191  if (get_ve_flags(curwin) == kOptVeFlagAll) {
   6192    const int save_State = State;
   6193    // Pretend Insert mode here to allow the cursor on the
   6194    // character past the end of the line
   6195    State = MODE_INSERT;
   6196    coladvance(curwin, MAXCOL);
   6197    State = save_State;
   6198  } else {
   6199    curwin->w_cursor.col += (colnr_T)strlen(get_cursor_pos_ptr());
   6200  }
   6201 }
   6202 
   6203 /// Handle "A", "a", "I", "i" and <Insert> commands.
   6204 static void nv_edit(cmdarg_T *cap)
   6205 {
   6206  // <Insert> is equal to "i"
   6207  if (cap->cmdchar == K_INS || cap->cmdchar == K_KINS) {
   6208    cap->cmdchar = 'i';
   6209  }
   6210 
   6211  // in Visual mode "A" and "I" are an operator
   6212  if (VIsual_active && (cap->cmdchar == 'A' || cap->cmdchar == 'I')) {
   6213    v_visop(cap);
   6214    // in Visual mode and after an operator "a" and "i" are for text objects
   6215  } else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i')
   6216             && (cap->oap->op_type != OP_NOP || VIsual_active)) {
   6217    nv_object(cap);
   6218  } else if (!curbuf->b_p_ma && !curbuf->terminal) {
   6219    emsg(_(e_modifiable));
   6220    clearop(cap->oap);
   6221  } else if (!checkclearopq(cap->oap)) {
   6222    switch (cap->cmdchar) {
   6223    case 'A':           // "A"ppend after the line
   6224      set_cursor_for_append_to_line();
   6225      break;
   6226 
   6227    case 'I':           // "I"nsert before the first non-blank
   6228      beginline(BL_WHITE);
   6229      break;
   6230 
   6231    case 'a':           // "a"ppend is like "i"nsert on the next character.
   6232      // increment coladd when in virtual space, increment the
   6233      // column otherwise, also to append after an unprintable char
   6234      if (virtual_active(curwin)
   6235          && (curwin->w_cursor.coladd > 0
   6236              || *get_cursor_pos_ptr() == NUL
   6237              || *get_cursor_pos_ptr() == TAB)) {
   6238        curwin->w_cursor.coladd++;
   6239      } else if (*get_cursor_pos_ptr() != NUL) {
   6240        inc_cursor();
   6241      }
   6242      break;
   6243    }
   6244 
   6245    if (curwin->w_cursor.coladd && cap->cmdchar != 'A') {
   6246      int save_State = State;
   6247 
   6248      // Pretend Insert mode here to allow the cursor on the
   6249      // character past the end of the line
   6250      State = MODE_INSERT;
   6251      coladvance(curwin, getviscol());
   6252      State = save_State;
   6253    }
   6254 
   6255    invoke_edit(cap, false, cap->cmdchar, false);
   6256  }
   6257 }
   6258 
   6259 /// Invoke edit() and take care of "restart_edit" and the return value.
   6260 ///
   6261 /// @param repl  "r" or "gr" command
   6262 static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln)
   6263 {
   6264  int restart_edit_save = 0;
   6265 
   6266  // Complicated: When the user types "a<C-O>a" we don't want to do Insert
   6267  // mode recursively.  But when doing "a<C-O>." or "a<C-O>rx" we do allow
   6268  // it.
   6269  if (repl || !stuff_empty()) {
   6270    restart_edit_save = restart_edit;
   6271  } else {
   6272    restart_edit_save = 0;
   6273  }
   6274 
   6275  // Always reset "restart_edit", this is not a restarted edit.
   6276  restart_edit = 0;
   6277 
   6278  // Reset Changedtick_i, so that TextChangedI will only be triggered for stuff
   6279  // from insert mode, for 'o/O' this has already been done in n_opencmd
   6280  if (cap->cmdchar != 'O' && cap->cmdchar != 'o') {
   6281    curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf);
   6282  }
   6283  if (edit(cmd, startln, cap->count1)) {
   6284    cap->retval |= CA_COMMAND_BUSY;
   6285  }
   6286 
   6287  if (restart_edit == 0) {
   6288    restart_edit = restart_edit_save;
   6289  }
   6290 }
   6291 
   6292 /// "a" or "i" while an operator is pending or in Visual mode: object motion.
   6293 static void nv_object(cmdarg_T *cap)
   6294 {
   6295  bool flag;
   6296  bool include;
   6297 
   6298  if (cap->cmdchar == 'i') {
   6299    include = false;        // "ix" = inner object: exclude white space
   6300  } else {
   6301    include = true;         // "ax" = an object: include white space
   6302  }
   6303  // Make sure (), [], {} and <> are in 'matchpairs'
   6304  char *mps_save = curbuf->b_p_mps;
   6305  curbuf->b_p_mps = "(:),{:},[:],<:>";
   6306 
   6307  switch (cap->nchar) {
   6308  case 'w':       // "aw" = a word
   6309    flag = current_word(cap->oap, cap->count1, include, false);
   6310    break;
   6311  case 'W':       // "aW" = a WORD
   6312    flag = current_word(cap->oap, cap->count1, include, true);
   6313    break;
   6314  case 'b':       // "ab" = a braces block
   6315  case '(':
   6316  case ')':
   6317    flag = current_block(cap->oap, cap->count1, include, '(', ')');
   6318    break;
   6319  case 'B':       // "aB" = a Brackets block
   6320  case '{':
   6321  case '}':
   6322    flag = current_block(cap->oap, cap->count1, include, '{', '}');
   6323    break;
   6324  case '[':       // "a[" = a [] block
   6325  case ']':
   6326    flag = current_block(cap->oap, cap->count1, include, '[', ']');
   6327    break;
   6328  case '<':       // "a<" = a <> block
   6329  case '>':
   6330    flag = current_block(cap->oap, cap->count1, include, '<', '>');
   6331    break;
   6332  case 't':       // "at" = a tag block (xml and html)
   6333    // Do not adjust oap->end in do_pending_operator()
   6334    // otherwise there are different results for 'dit'
   6335    // (note leading whitespace in last line):
   6336    // 1) <b>      2) <b>
   6337    //    foobar      foobar
   6338    //    </b>            </b>
   6339    cap->retval |= CA_NO_ADJ_OP_END;
   6340    flag = current_tagblock(cap->oap, cap->count1, include);
   6341    break;
   6342  case 'p':       // "ap" = a paragraph
   6343    flag = current_par(cap->oap, cap->count1, include, 'p');
   6344    break;
   6345  case 's':       // "as" = a sentence
   6346    flag = current_sent(cap->oap, cap->count1, include);
   6347    break;
   6348  case '"':       // "a"" = a double quoted string
   6349  case '\'':       // "a'" = a single quoted string
   6350  case '`':       // "a`" = a backtick quoted string
   6351    flag = current_quote(cap->oap, cap->count1, include,
   6352                         cap->nchar);
   6353    break;
   6354  default:
   6355    flag = false;
   6356    break;
   6357  }
   6358 
   6359  curbuf->b_p_mps = mps_save;
   6360  if (!flag) {
   6361    clearopbeep(cap->oap);
   6362  }
   6363  adjust_cursor_col();
   6364  curwin->w_set_curswant = true;
   6365 }
   6366 
   6367 /// "q" command: Start/stop recording.
   6368 /// "q:", "q/", "q?": edit command-line in command-line window.
   6369 static void nv_record(cmdarg_T *cap)
   6370 {
   6371  if (cap->oap->op_type == OP_FORMAT) {
   6372    // "gqq" is the same as "gqgq": format line
   6373    cap->cmdchar = 'g';
   6374    cap->nchar = 'q';
   6375    nv_operator(cap);
   6376    return;
   6377  }
   6378 
   6379  if (checkclearop(cap->oap)) {
   6380    return;
   6381  }
   6382 
   6383  if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?') {
   6384    if (cmdwin_type != 0) {
   6385      emsg(_(e_cmdline_window_already_open));
   6386      return;
   6387    }
   6388    stuffcharReadbuff(cap->nchar);
   6389    stuffcharReadbuff(K_CMDWIN);
   6390  } else {
   6391    // (stop) recording into a named register, unless executing a
   6392    // register.
   6393    if (reg_executing == 0 && do_record(cap->nchar) == FAIL) {
   6394      clearopbeep(cap->oap);
   6395    }
   6396  }
   6397 }
   6398 
   6399 /// Handle the "@r" command.
   6400 static void nv_at(cmdarg_T *cap)
   6401 {
   6402  if (checkclearop(cap->oap)) {
   6403    return;
   6404  }
   6405  if (cap->nchar == '=') {
   6406    if (get_expr_register() == NUL) {
   6407      return;
   6408    }
   6409  }
   6410  while (cap->count1-- && !got_int) {
   6411    if (do_execreg(cap->nchar, false, false, false) == false) {
   6412      clearopbeep(cap->oap);
   6413      break;
   6414    }
   6415    line_breakcheck();
   6416  }
   6417 }
   6418 
   6419 /// Handle the CTRL-U and CTRL-D commands.
   6420 static void nv_halfpage(cmdarg_T *cap)
   6421 {
   6422  if (!checkclearop(cap->oap)) {
   6423    pagescroll(cap->cmdchar == Ctrl_D ? FORWARD : BACKWARD, cap->count0, true);
   6424  }
   6425 }
   6426 
   6427 /// Handle "J" or "gJ" command.
   6428 static void nv_join(cmdarg_T *cap)
   6429 {
   6430  if (VIsual_active) {  // join the visual lines
   6431    nv_operator(cap);
   6432    return;
   6433  }
   6434 
   6435  if (checkclearop(cap->oap)) {
   6436    return;
   6437  }
   6438 
   6439  cap->count0 = MAX(cap->count0, 2);  // default for join is two lines!
   6440 
   6441  if (curwin->w_cursor.lnum + cap->count0 - 1 >
   6442      curbuf->b_ml.ml_line_count) {
   6443    // can't join when on the last line
   6444    if (cap->count0 <= 2) {
   6445      clearopbeep(cap->oap);
   6446      return;
   6447    }
   6448    cap->count0 = curbuf->b_ml.ml_line_count - curwin->w_cursor.lnum + 1;
   6449  }
   6450 
   6451  prep_redo(cap->oap->regname, cap->count0,
   6452            NUL, cap->cmdchar, NUL, NUL, cap->nchar);
   6453  do_join((size_t)cap->count0, cap->nchar == NUL, true, true, true);
   6454 }
   6455 
   6456 /// "P", "gP", "p" and "gp" commands.
   6457 static void nv_put(cmdarg_T *cap)
   6458 {
   6459  nv_put_opt(cap, false);
   6460 }
   6461 
   6462 /// "P", "gP", "p" and "gp" commands.
   6463 ///
   6464 /// @param fix_indent  true for "[p", "[P", "]p" and "]P".
   6465 static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
   6466 {
   6467  yankreg_T *savereg = NULL;
   6468  bool empty = false;
   6469  bool was_visual = false;
   6470  int dir;
   6471  int flags = 0;
   6472  const int save_fen = curwin->w_p_fen;
   6473 
   6474  if (cap->oap->op_type != OP_NOP) {
   6475    // "dp" is ":diffput"
   6476    if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'p') {
   6477      clearop(cap->oap);
   6478      assert(cap->opcount >= 0);
   6479      nv_diffgetput(true, (size_t)cap->opcount);
   6480    } else {
   6481      clearopbeep(cap->oap);
   6482    }
   6483    return;
   6484  }
   6485 
   6486  if (bt_prompt(curbuf) && !prompt_curpos_editable()) {
   6487    if (curwin->w_cursor.lnum == curbuf->b_prompt_start.mark.lnum) {
   6488      curwin->w_cursor.col = curbuf->b_prompt_start.mark.col;
   6489      // Since we've shifted the cursor to the first editable char. We want to
   6490      // paste before that.
   6491      cap->cmdchar = 'P';
   6492    } else {
   6493      clearopbeep(cap->oap);
   6494      return;
   6495    }
   6496  }
   6497 
   6498  if (fix_indent) {
   6499    dir = (cap->cmdchar == ']' && cap->nchar == 'p')
   6500          ? FORWARD : BACKWARD;
   6501    flags |= PUT_FIXINDENT;
   6502  } else {
   6503    dir = (cap->cmdchar == 'P'
   6504           || ((cap->cmdchar == 'g' || cap->cmdchar == 'z')
   6505               && cap->nchar == 'P')) ? BACKWARD : FORWARD;
   6506  }
   6507  prep_redo_cmd(cap);
   6508  if (cap->cmdchar == 'g') {
   6509    flags |= PUT_CURSEND;
   6510  } else if (cap->cmdchar == 'z') {
   6511    flags |= PUT_BLOCK_INNER;
   6512  }
   6513 
   6514  if (VIsual_active) {
   6515    // Putting in Visual mode: The put text replaces the selected
   6516    // text.  First delete the selected text, then put the new text.
   6517    // Need to save and restore the registers that the delete
   6518    // overwrites if the old contents is being put.
   6519    was_visual = true;
   6520    int regname = cap->oap->regname;
   6521    bool keep_registers = cap->cmdchar == 'P';
   6522    // '+' and '*' could be the same selection
   6523    bool clipoverwrite = (regname == '+' || regname == '*')
   6524                         && (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus));
   6525    if (regname == 0 || regname == '"' || clipoverwrite
   6526        || ascii_isdigit(regname) || regname == '-') {
   6527      // The delete might overwrite the register we want to put, save it first
   6528      savereg = copy_register(regname);
   6529    }
   6530 
   6531    // Temporarily disable folding, as deleting a fold marker may cause
   6532    // the cursor to be included in a fold.
   6533    curwin->w_p_fen = false;
   6534 
   6535    // To place the cursor correctly after a blockwise put, and to leave the
   6536    // text in the correct position when putting over a selection with
   6537    // 'virtualedit' and past the end of the line, we use the 'c' operator in
   6538    // do_put(), which requires the visual selection to still be active.
   6539    if (!VIsual_active || VIsual_mode == 'V' || regname != '.') {
   6540      // Now delete the selected text. Avoid messages here.
   6541      cap->cmdchar = 'd';
   6542      cap->nchar = NUL;
   6543      cap->oap->regname = keep_registers ? '_' : NUL;
   6544      msg_silent++;
   6545      nv_operator(cap);
   6546      do_pending_operator(cap, 0, false);
   6547      empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
   6548      msg_silent--;
   6549 
   6550      // delete PUT_LINE_BACKWARD;
   6551      cap->oap->regname = regname;
   6552    }
   6553 
   6554    // When deleted a linewise Visual area, put the register as
   6555    // lines to avoid it joined with the next line.  When deletion was
   6556    // charwise, split a line when putting lines.
   6557    if (VIsual_mode == 'V') {
   6558      flags |= PUT_LINE;
   6559    } else if (VIsual_mode == 'v') {
   6560      flags |= PUT_LINE_SPLIT;
   6561    }
   6562    if (VIsual_mode == Ctrl_V && dir == FORWARD) {
   6563      flags |= PUT_LINE_FORWARD;
   6564    }
   6565    dir = BACKWARD;
   6566    if ((VIsual_mode != 'V'
   6567         && curwin->w_cursor.col < curbuf->b_op_start.col)
   6568        || (VIsual_mode == 'V'
   6569            && curwin->w_cursor.lnum < curbuf->b_op_start.lnum)) {
   6570      // cursor is at the end of the line or end of file, put
   6571      // forward.
   6572      dir = FORWARD;
   6573    }
   6574    // May have been reset in do_put().
   6575    VIsual_active = true;
   6576  }
   6577  do_put(cap->oap->regname, savereg, dir, cap->count1, flags);
   6578 
   6579  // If a register was saved, free it
   6580  if (savereg != NULL) {
   6581    free_register(savereg);
   6582    xfree(savereg);
   6583  }
   6584 
   6585  if (was_visual) {
   6586    if (save_fen) {
   6587      curwin->w_p_fen = true;
   6588    }
   6589    // What to reselect with "gv"?  Selecting the just put text seems to
   6590    // be the most useful, since the original text was removed.
   6591    curbuf->b_visual.vi_start = curbuf->b_op_start;
   6592    curbuf->b_visual.vi_end = curbuf->b_op_end;
   6593    // need to adjust cursor position
   6594    if (*p_sel == 'e') {
   6595      inc(&curbuf->b_visual.vi_end);
   6596    }
   6597  }
   6598 
   6599  // When all lines were selected and deleted do_put() leaves an empty
   6600  // line that needs to be deleted now.
   6601  if (empty && *ml_get(curbuf->b_ml.ml_line_count) == NUL) {
   6602    ml_delete_flags(curbuf->b_ml.ml_line_count, ML_DEL_MESSAGE);
   6603    deleted_lines(curbuf->b_ml.ml_line_count + 1, 1);
   6604 
   6605    // If the cursor was in that line, move it to the end of the last
   6606    // line.
   6607    if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
   6608      curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
   6609      coladvance(curwin, MAXCOL);
   6610    }
   6611  }
   6612  auto_format(false, true);
   6613 }
   6614 
   6615 /// "o" and "O" commands.
   6616 static void nv_open(cmdarg_T *cap)
   6617 {
   6618  // "do" is ":diffget"
   6619  if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'o') {
   6620    clearop(cap->oap);
   6621    assert(cap->opcount >= 0);
   6622    nv_diffgetput(false, (size_t)cap->opcount);
   6623  } else if (VIsual_active) {
   6624    // switch start and end of visual/
   6625    v_swap_corners(cap->cmdchar);
   6626  } else if (bt_prompt(curbuf) && curwin->w_cursor.lnum < curbuf->b_prompt_start.mark.lnum) {
   6627    clearopbeep(cap->oap);
   6628  } else {
   6629    n_opencmd(cap);
   6630  }
   6631 }
   6632 
   6633 /// Handles K_PASTE_START, repeats pasted text.
   6634 static void nv_paste(cmdarg_T *cap)
   6635 {
   6636  paste_repeat(cap->count1);
   6637 }
   6638 
   6639 /// Handle an arbitrary event in normal mode
   6640 static void nv_event(cmdarg_T *cap)
   6641 {
   6642  // Garbage collection should have been executed before blocking for events in
   6643  // the `input_get` in `state_enter`, but we also disable it here in case the
   6644  // `input_get` branch was not executed (!multiqueue_empty(loop.events), which
   6645  // could have `may_garbage_collect` set to true in `normal_check`).
   6646  //
   6647  // That is because here we may run code that calls `input_get` later
   6648  // (`f_confirm` or `get_keystroke` for example), but in these cases it is
   6649  // not safe to perform garbage collection because there could be unreferenced
   6650  // lists or dicts being used.
   6651  may_garbage_collect = false;
   6652  bool may_restart = (restart_edit != 0 || restart_VIsual_select != 0);
   6653  state_handle_k_event();
   6654  finish_op = false;
   6655  if (may_restart) {
   6656    // Tricky: if restart_edit was set before the handler we are in ctrl-o mode,
   6657    // but if not, the event should be allowed to trigger :startinsert.
   6658    cap->retval |= CA_COMMAND_BUSY;  // don't call edit() or restart Select now
   6659  }
   6660 }
   6661 
   6662 void normal_cmd(oparg_T *oap, bool toplevel)
   6663 {
   6664  NormalState s;
   6665  normal_state_init(&s);
   6666  s.toplevel = toplevel;
   6667  s.oa = *oap;
   6668  normal_prepare(&s);
   6669  normal_execute(&s.state, safe_vgetc());
   6670  *oap = s.oa;
   6671 }