summaryrefslogtreecommitdiffstats
path: root/src/normal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/normal.c')
-rw-r--r--src/normal.c8508
1 files changed, 8508 insertions, 0 deletions
diff --git a/src/normal.c b/src/normal.c
new file mode 100644
index 0000000000..89196da0f4
--- /dev/null
+++ b/src/normal.c
@@ -0,0 +1,8508 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+/*
+ * normal.c: Contains the main routine for processing characters in command
+ * mode. Communicates closely with the code in ops.c to handle
+ * the operators.
+ */
+
+#include "vim.h"
+
+#ifdef FEAT_VISUAL
+/*
+ * The Visual area is remembered for reselection.
+ */
+static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */
+static linenr_T resel_VIsual_line_count; /* number of lines */
+static colnr_T resel_VIsual_col; /* nr of cols or end col */
+
+static int restart_VIsual_select = 0;
+#endif
+
+static int
+# ifdef __BORLANDC__
+_RTLENTRYF
+# endif
+ nv_compare __ARGS((const void *s1, const void *s2));
+static int find_command __ARGS((int cmdchar));
+static void op_colon __ARGS((oparg_T *oap));
+#if defined(FEAT_MOUSE) && defined(FEAT_VISUAL)
+static void find_start_of_word __ARGS((pos_T *));
+static void find_end_of_word __ARGS((pos_T *));
+static int get_mouse_class __ARGS((char_u *p));
+#endif
+static void prep_redo_cmd __ARGS((cmdarg_T *cap));
+static void prep_redo __ARGS((int regname, long, int, int, int, int, int));
+static int checkclearop __ARGS((oparg_T *oap));
+static int checkclearopq __ARGS((oparg_T *oap));
+static void clearop __ARGS((oparg_T *oap));
+static void clearopbeep __ARGS((oparg_T *oap));
+#ifdef FEAT_VISUAL
+static void unshift_special __ARGS((cmdarg_T *cap));
+#endif
+#ifdef FEAT_CMDL_INFO
+static void del_from_showcmd __ARGS((int));
+#endif
+
+/*
+ * nv_*(): functions called to handle Normal and Visual mode commands.
+ * n_*(): functions called to handle Normal mode commands.
+ * v_*(): functions called to handle Visual mode commands.
+ */
+static void nv_ignore __ARGS((cmdarg_T *cap));
+static void nv_error __ARGS((cmdarg_T *cap));
+static void nv_help __ARGS((cmdarg_T *cap));
+static void nv_addsub __ARGS((cmdarg_T *cap));
+static void nv_page __ARGS((cmdarg_T *cap));
+static void nv_gd __ARGS((oparg_T *oap, int nchar));
+static int nv_screengo __ARGS((oparg_T *oap, int dir, long dist));
+#ifdef FEAT_MOUSE
+static void nv_mousescroll __ARGS((cmdarg_T *cap));
+static void nv_mouse __ARGS((cmdarg_T *cap));
+#endif
+static void nv_scroll_line __ARGS((cmdarg_T *cap));
+static void nv_zet __ARGS((cmdarg_T *cap));
+#ifdef FEAT_GUI
+static void nv_ver_scrollbar __ARGS((cmdarg_T *cap));
+static void nv_hor_scrollbar __ARGS((cmdarg_T *cap));
+#endif
+static void nv_exmode __ARGS((cmdarg_T *cap));
+static void nv_colon __ARGS((cmdarg_T *cap));
+static void nv_ctrlg __ARGS((cmdarg_T *cap));
+static void nv_ctrlh __ARGS((cmdarg_T *cap));
+static void nv_clear __ARGS((cmdarg_T *cap));
+static void nv_ctrlo __ARGS((cmdarg_T *cap));
+static void nv_hat __ARGS((cmdarg_T *cap));
+static void nv_Zet __ARGS((cmdarg_T *cap));
+static void nv_ident __ARGS((cmdarg_T *cap));
+#ifdef FEAT_VISUAL
+static int get_visual_text __ARGS((cmdarg_T *cap, char_u **pp, int *lenp));
+#endif
+static void nv_tagpop __ARGS((cmdarg_T *cap));
+static void nv_scroll __ARGS((cmdarg_T *cap));
+static void nv_right __ARGS((cmdarg_T *cap));
+static void nv_left __ARGS((cmdarg_T *cap));
+static void nv_up __ARGS((cmdarg_T *cap));
+static void nv_down __ARGS((cmdarg_T *cap));
+#ifdef FEAT_SEARCHPATH
+static void nv_gotofile __ARGS((cmdarg_T *cap));
+#endif
+static void nv_end __ARGS((cmdarg_T *cap));
+static void nv_dollar __ARGS((cmdarg_T *cap));
+static void nv_search __ARGS((cmdarg_T *cap));
+static void nv_next __ARGS((cmdarg_T *cap));
+static void normal_search __ARGS((cmdarg_T *cap, int dir, char_u *pat, int opt));
+static void nv_csearch __ARGS((cmdarg_T *cap));
+static void nv_brackets __ARGS((cmdarg_T *cap));
+static void nv_percent __ARGS((cmdarg_T *cap));
+static void nv_brace __ARGS((cmdarg_T *cap));
+static void nv_mark __ARGS((cmdarg_T *cap));
+static void nv_findpar __ARGS((cmdarg_T *cap));
+static void nv_undo __ARGS((cmdarg_T *cap));
+static void nv_kundo __ARGS((cmdarg_T *cap));
+static void nv_Replace __ARGS((cmdarg_T *cap));
+#ifdef FEAT_VREPLACE
+static void nv_vreplace __ARGS((cmdarg_T *cap));
+#endif
+#ifdef FEAT_VISUAL
+static void v_swap_corners __ARGS((int cmdchar));
+#endif
+static void nv_replace __ARGS((cmdarg_T *cap));
+static void n_swapchar __ARGS((cmdarg_T *cap));
+static void nv_cursormark __ARGS((cmdarg_T *cap, int flag, pos_T *pos));
+#ifdef FEAT_VISUAL
+static void v_visop __ARGS((cmdarg_T *cap));
+#endif
+static void nv_subst __ARGS((cmdarg_T *cap));
+static void nv_abbrev __ARGS((cmdarg_T *cap));
+static void nv_optrans __ARGS((cmdarg_T *cap));
+static void nv_gomark __ARGS((cmdarg_T *cap));
+static void nv_pcmark __ARGS((cmdarg_T *cap));
+static void nv_regname __ARGS((cmdarg_T *cap));
+#ifdef FEAT_VISUAL
+static void nv_visual __ARGS((cmdarg_T *cap));
+static void n_start_visual_mode __ARGS((int c));
+#endif
+static void nv_window __ARGS((cmdarg_T *cap));
+static void nv_suspend __ARGS((cmdarg_T *cap));
+static void nv_g_cmd __ARGS((cmdarg_T *cap));
+static void n_opencmd __ARGS((cmdarg_T *cap));
+static void nv_dot __ARGS((cmdarg_T *cap));
+static void nv_redo __ARGS((cmdarg_T *cap));
+static void nv_Undo __ARGS((cmdarg_T *cap));
+static void nv_tilde __ARGS((cmdarg_T *cap));
+static void nv_operator __ARGS((cmdarg_T *cap));
+static void nv_lineop __ARGS((cmdarg_T *cap));
+static void nv_home __ARGS((cmdarg_T *cap));
+static void nv_pipe __ARGS((cmdarg_T *cap));
+static void nv_bck_word __ARGS((cmdarg_T *cap));
+static void nv_wordcmd __ARGS((cmdarg_T *cap));
+static void nv_beginline __ARGS((cmdarg_T *cap));
+#ifdef FEAT_VISUAL
+static void adjust_for_sel __ARGS((cmdarg_T *cap));
+static int unadjust_for_sel __ARGS((void));
+static void nv_select __ARGS((cmdarg_T *cap));
+#endif
+static void nv_goto __ARGS((cmdarg_T *cap));
+static void nv_normal __ARGS((cmdarg_T *cap));
+static void nv_esc __ARGS((cmdarg_T *oap));
+static void nv_edit __ARGS((cmdarg_T *cap));
+static void invoke_edit __ARGS((cmdarg_T *cap, int repl, int cmd, int startln));
+#ifdef FEAT_TEXTOBJ
+static void nv_object __ARGS((cmdarg_T *cap));
+#endif
+static void nv_record __ARGS((cmdarg_T *cap));
+static void nv_at __ARGS((cmdarg_T *cap));
+static void nv_halfpage __ARGS((cmdarg_T *cap));
+static void nv_join __ARGS((cmdarg_T *cap));
+static void nv_put __ARGS((cmdarg_T *cap));
+static void nv_open __ARGS((cmdarg_T *cap));
+#ifdef FEAT_SNIFF
+static void nv_sniff __ARGS((cmdarg_T *cap));
+#endif
+#ifdef FEAT_NETBEANS_INTG
+static void nv_nbcmd __ARGS((cmdarg_T *cap));
+#endif
+#ifdef FEAT_DND
+static void nv_drop __ARGS((cmdarg_T *cap));
+#endif
+
+/*
+ * Function to be called for a Normal or Visual mode command.
+ * The argument is a cmdarg_T.
+ */
+typedef void (*nv_func_T) __ARGS((cmdarg_T *cap));
+
+/* Values for cmd_flags. */
+#define NV_NCH 0x01 /* may need to get a second char */
+#define NV_NCH_NOP (0x02|NV_NCH) /* get second char when no operator pending */
+#define NV_NCH_ALW (0x04|NV_NCH) /* always get a second char */
+#define NV_LANG 0x08 /* second char needs language adjustment */
+
+#define NV_SS 0x10 /* may start selection */
+#define NV_SSS 0x20 /* may start selection with shift modifier */
+#define NV_STS 0x40 /* may stop selection without shift modif. */
+#define NV_RL 0x80 /* 'rightleft' modifies command */
+#define NV_KEEPREG 0x100 /* don't clear regname */
+#define NV_NCW 0x200 /* not allowed in command-line window */
+
+/*
+ * Generally speaking, every Normal mode command should either clear any
+ * pending operator (with *clearop*()), or set the motion type variable
+ * oap->motion_type.
+ *
+ * When a cursor motion command is made, it is marked as being a character or
+ * line oriented motion. Then, if an operator is in effect, the operation
+ * becomes character or line oriented accordingly.
+ */
+
+/*
+ * This table contains one entry for every Normal or Visual mode command.
+ * The order doesn't matter, init_normal_cmds() will create a sorted index.
+ * It is faster when all keys from zero to '~' are present.
+ */
+static const struct nv_cmd
+{
+ int cmd_char; /* (first) command character */
+ nv_func_T cmd_func; /* function for this command */
+ short_u cmd_flags; /* NV_ flags */
+ short cmd_arg; /* value for ca.arg */
+} nv_cmds[] =
+{
+ {NUL, nv_error, 0, 0},
+ {Ctrl_A, nv_addsub, 0, 0},
+ {Ctrl_B, nv_page, NV_STS, BACKWARD},
+ {Ctrl_C, nv_esc, 0, TRUE},
+ {Ctrl_D, nv_halfpage, 0, 0},
+ {Ctrl_E, nv_scroll_line, 0, TRUE},
+ {Ctrl_F, nv_page, NV_STS, FORWARD},
+ {Ctrl_G, nv_ctrlg, 0, 0},
+ {Ctrl_H, nv_ctrlh, 0, 0},
+ {Ctrl_I, nv_pcmark, 0, 0},
+ {NL, nv_down, 0, FALSE},
+ {Ctrl_K, nv_error, 0, 0},
+ {Ctrl_L, nv_clear, 0, 0},
+ {CAR, nv_down, 0, TRUE},
+ {Ctrl_N, nv_down, NV_STS, FALSE},
+ {Ctrl_O, nv_ctrlo, 0, 0},
+ {Ctrl_P, nv_up, NV_STS, FALSE},
+ {Ctrl_Q, nv_ignore, 0, 0},
+ {Ctrl_R, nv_redo, 0, 0},
+ {Ctrl_S, nv_ignore, 0, 0},
+ {Ctrl_T, nv_tagpop, NV_NCW, 0},
+ {Ctrl_U, nv_halfpage, 0, 0},
+#ifdef FEAT_VISUAL
+ {Ctrl_V, nv_visual, 0, FALSE},
+ {'V', nv_visual, 0, FALSE},
+ {'v', nv_visual, 0, FALSE},
+#else
+ {Ctrl_V, nv_error, 0, 0},
+ {'V', nv_error, 0, 0},
+ {'v', nv_error, 0, 0},
+#endif
+ {Ctrl_W, nv_window, 0, 0},
+ {Ctrl_X, nv_addsub, 0, 0},
+ {Ctrl_Y, nv_scroll_line, 0, FALSE},
+ {Ctrl_Z, nv_suspend, 0, 0},
+ {ESC, nv_esc, 0, FALSE},
+ {Ctrl_BSL, nv_normal, NV_NCH_ALW, 0},
+ {Ctrl_RSB, nv_ident, NV_NCW, 0},
+ {Ctrl_HAT, nv_hat, NV_NCW, 0},
+ {Ctrl__, nv_error, 0, 0},
+ {' ', nv_right, 0, 0},
+ {'!', nv_operator, 0, 0},
+ {'"', nv_regname, NV_NCH_NOP|NV_KEEPREG, 0},
+ {'#', nv_ident, 0, 0},
+ {'$', nv_dollar, 0, 0},
+ {'%', nv_percent, 0, 0},
+ {'&', nv_optrans, 0, 0},
+ {'\'', nv_gomark, NV_NCH_ALW, TRUE},
+ {'(', nv_brace, 0, BACKWARD},
+ {')', nv_brace, 0, FORWARD},
+ {'*', nv_ident, 0, 0},
+ {'+', nv_down, 0, TRUE},
+ {',', nv_csearch, 0, TRUE},
+ {'-', nv_up, 0, TRUE},
+ {'.', nv_dot, NV_KEEPREG, 0},
+ {'/', nv_search, 0, FALSE},
+ {'0', nv_beginline, 0, 0},
+ {'1', nv_ignore, 0, 0},
+ {'2', nv_ignore, 0, 0},
+ {'3', nv_ignore, 0, 0},
+ {'4', nv_ignore, 0, 0},
+ {'5', nv_ignore, 0, 0},
+ {'6', nv_ignore, 0, 0},
+ {'7', nv_ignore, 0, 0},
+ {'8', nv_ignore, 0, 0},
+ {'9', nv_ignore, 0, 0},
+ {':', nv_colon, 0, 0},
+ {';', nv_csearch, 0, FALSE},
+ {'<', nv_operator, NV_RL, 0},
+ {'=', nv_operator, 0, 0},
+ {'>', nv_operator, NV_RL, 0},
+ {'?', nv_search, 0, FALSE},
+ {'@', nv_at, NV_NCH_NOP, FALSE},
+ {'A', nv_edit, 0, 0},
+ {'B', nv_bck_word, 0, 1},
+ {'C', nv_abbrev, NV_KEEPREG, 0},
+ {'D', nv_abbrev, NV_KEEPREG, 0},
+ {'E', nv_wordcmd, 0, TRUE},
+ {'F', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD},
+ {'G', nv_goto, 0, TRUE},
+ {'H', nv_scroll, 0, 0},
+ {'I', nv_edit, 0, 0},
+ {'J', nv_join, 0, 0},
+ {'K', nv_ident, 0, 0},
+ {'L', nv_scroll, 0, 0},
+ {'M', nv_scroll, 0, 0},
+ {'N', nv_next, 0, SEARCH_REV},
+ {'O', nv_open, 0, 0},
+ {'P', nv_put, 0, 0},
+ {'Q', nv_exmode, NV_NCW, 0},
+ {'R', nv_Replace, 0, FALSE},
+ {'S', nv_subst, NV_KEEPREG, 0},
+ {'T', nv_csearch, NV_NCH_ALW|NV_LANG, BACKWARD},
+ {'U', nv_Undo, 0, 0},
+ {'W', nv_wordcmd, 0, TRUE},
+ {'X', nv_abbrev, NV_KEEPREG, 0},
+ {'Y', nv_abbrev, NV_KEEPREG, 0},
+ {'Z', nv_Zet, NV_NCH_NOP|NV_NCW, 0},
+ {'[', nv_brackets, NV_NCH_ALW, BACKWARD},
+ {'\\', nv_error, 0, 0},
+ {']', nv_brackets, NV_NCH_ALW, FORWARD},
+ {'^', nv_beginline, 0, BL_WHITE | BL_FIX},
+ {'_', nv_lineop, 0, 0},
+ {'`', nv_gomark, NV_NCH_ALW, FALSE},
+ {'a', nv_edit, NV_NCH, 0},
+ {'b', nv_bck_word, 0, 0},
+ {'c', nv_operator, 0, 0},
+ {'d', nv_operator, 0, 0},
+ {'e', nv_wordcmd, 0, FALSE},
+ {'f', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD},
+ {'g', nv_g_cmd, NV_NCH_ALW, FALSE},
+ {'h', nv_left, NV_RL, 0},
+ {'i', nv_edit, NV_NCH, 0},
+ {'j', nv_down, 0, FALSE},
+ {'k', nv_up, 0, FALSE},
+ {'l', nv_right, NV_RL, 0},
+ {'m', nv_mark, NV_NCH_NOP, 0},
+ {'n', nv_next, 0, 0},
+ {'o', nv_open, 0, 0},
+ {'p', nv_put, 0, 0},
+ {'q', nv_record, NV_NCH, 0},
+ {'r', nv_replace, NV_NCH_NOP|NV_LANG, 0},
+ {'s', nv_subst, NV_KEEPREG, 0},
+ {'t', nv_csearch, NV_NCH_ALW|NV_LANG, FORWARD},
+ {'u', nv_undo, 0, 0},
+ {'w', nv_wordcmd, 0, FALSE},
+ {'x', nv_abbrev, NV_KEEPREG, 0},
+ {'y', nv_operator, 0, 0},
+ {'z', nv_zet, NV_NCH_ALW, 0},
+ {'{', nv_findpar, 0, BACKWARD},
+ {'|', nv_pipe, 0, 0},
+ {'}', nv_findpar, 0, FORWARD},
+ {'~', nv_tilde, 0, 0},
+
+ /* pound sign */
+ {POUND, nv_ident, 0, 0},
+#ifdef FEAT_MOUSE
+ {K_MOUSEUP, nv_mousescroll, 0, TRUE},
+ {K_MOUSEDOWN, nv_mousescroll, 0, FALSE},
+ {K_LEFTMOUSE, nv_mouse, 0, 0},
+ {K_LEFTMOUSE_NM, nv_mouse, 0, 0},
+ {K_LEFTDRAG, nv_mouse, 0, 0},
+ {K_LEFTRELEASE, nv_mouse, 0, 0},
+ {K_LEFTRELEASE_NM, nv_mouse, 0, 0},
+ {K_MIDDLEMOUSE, nv_mouse, 0, 0},
+ {K_MIDDLEDRAG, nv_mouse, 0, 0},
+ {K_MIDDLERELEASE, nv_mouse, 0, 0},
+ {K_RIGHTMOUSE, nv_mouse, 0, 0},
+ {K_RIGHTDRAG, nv_mouse, 0, 0},
+ {K_RIGHTRELEASE, nv_mouse, 0, 0},
+ {K_X1MOUSE, nv_mouse, 0, 0},
+ {K_X1DRAG, nv_mouse, 0, 0},
+ {K_X1RELEASE, nv_mouse, 0, 0},
+ {K_X2MOUSE, nv_mouse, 0, 0},
+ {K_X2DRAG, nv_mouse, 0, 0},
+ {K_X2RELEASE, nv_mouse, 0, 0},
+#endif
+ {K_IGNORE, nv_ignore, 0, 0},
+ {K_INS, nv_edit, 0, 0},
+ {K_KINS, nv_edit, 0, 0},
+ {K_BS, nv_ctrlh, 0, 0},
+ {K_UP, nv_up, NV_SSS|NV_STS, FALSE},
+ {K_S_UP, nv_page, NV_SS, BACKWARD},
+ {K_DOWN, nv_down, NV_SSS|NV_STS, FALSE},
+ {K_S_DOWN, nv_page, NV_SS, FORWARD},
+ {K_LEFT, nv_left, NV_SSS|NV_STS|NV_RL, 0},
+ {K_S_LEFT, nv_bck_word, NV_SS|NV_RL, 0},
+ {K_C_LEFT, nv_bck_word, NV_SSS|NV_RL|NV_STS, 1},
+ {K_RIGHT, nv_right, NV_SSS|NV_STS|NV_RL, 0},
+ {K_S_RIGHT, nv_wordcmd, NV_SS|NV_RL, FALSE},
+ {K_C_RIGHT, nv_wordcmd, NV_SSS|NV_RL|NV_STS, TRUE},
+ {K_PAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD},
+ {K_KPAGEUP, nv_page, NV_SSS|NV_STS, BACKWARD},
+ {K_PAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD},
+ {K_KPAGEDOWN, nv_page, NV_SSS|NV_STS, FORWARD},
+ {K_END, nv_end, NV_SSS|NV_STS, FALSE},
+ {K_KEND, nv_end, NV_SSS|NV_STS, FALSE},
+ {K_XEND, nv_end, NV_SSS|NV_STS, FALSE},
+ {K_S_END, nv_end, NV_SS, FALSE},
+ {K_C_END, nv_end, NV_SSS|NV_STS, TRUE},
+ {K_HOME, nv_home, NV_SSS|NV_STS, 0},
+ {K_KHOME, nv_home, NV_SSS|NV_STS, 0},
+ {K_XHOME, nv_home, NV_SSS|NV_STS, 0},
+ {K_S_HOME, nv_home, NV_SS, 0},
+ {K_C_HOME, nv_goto, NV_SSS|NV_STS, FALSE},
+ {K_DEL, nv_abbrev, 0, 0},
+ {K_KDEL, nv_abbrev, 0, 0},
+ {K_UNDO, nv_kundo, 0, 0},
+ {K_HELP, nv_help, NV_NCW, 0},
+ {K_F1, nv_help, NV_NCW, 0},
+ {K_XF1, nv_help, NV_NCW, 0},
+#ifdef FEAT_VISUAL
+ {K_SELECT, nv_select, 0, 0},
+#endif
+#ifdef FEAT_GUI
+ {K_VER_SCROLLBAR, nv_ver_scrollbar, 0, 0},
+ {K_HOR_SCROLLBAR, nv_hor_scrollbar, 0, 0},
+#endif
+#ifdef FEAT_FKMAP
+ {K_F8, farsi_fkey, 0, 0},
+ {K_F9, farsi_fkey, 0, 0},
+#endif
+#ifdef FEAT_SNIFF
+ {K_SNIFF, nv_sniff, 0, 0},
+#endif
+#ifdef FEAT_NETBEANS_INTG
+ {K_F21, nv_nbcmd, NV_NCH_ALW, 0},
+#endif
+#ifdef FEAT_DND
+ {K_DROP, nv_drop, NV_STS, 0},
+#endif
+};
+
+/* Number of commands in nv_cmds[]. */
+#define NV_CMDS_SIZE (sizeof(nv_cmds) / sizeof(struct nv_cmd))
+
+/* Sorted index of commands in nv_cmds[]. */
+static short nv_cmd_idx[NV_CMDS_SIZE];
+
+/* The highest index for which
+ * nv_cmds[idx].cmd_char == nv_cmd_idx[nv_cmds[idx].cmd_char] */
+static int nv_max_linear;
+
+/*
+ * Compare functions for qsort() below, that checks the command character
+ * through the index in nv_cmd_idx[].
+ */
+ static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+nv_compare(s1, s2)
+ const void *s1;
+ const void *s2;
+{
+ int c1, c2;
+
+ /* The commands are sorted on absolute value. */
+ c1 = nv_cmds[*(const short *)s1].cmd_char;
+ c2 = nv_cmds[*(const short *)s2].cmd_char;
+ if (c1 < 0)
+ c1 = -c1;
+ if (c2 < 0)
+ c2 = -c2;
+ return c1 - c2;
+}
+
+/*
+ * Initialize the nv_cmd_idx[] table.
+ */
+ void
+init_normal_cmds()
+{
+ int i;
+
+ /* Fill the index table with a one to one relation. */
+ for (i = 0; i < NV_CMDS_SIZE; ++i)
+ nv_cmd_idx[i] = i;
+
+ /* Sort the commands by the command character. */
+ qsort((void *)&nv_cmd_idx, (size_t)NV_CMDS_SIZE, sizeof(short), nv_compare);
+
+ /* Find the first entry that can't be indexed by the command character. */
+ for (i = 0; i < NV_CMDS_SIZE; ++i)
+ if (i != nv_cmds[nv_cmd_idx[i]].cmd_char)
+ break;
+ nv_max_linear = i - 1;
+}
+
+/*
+ * Search for a command in the commands table.
+ * Returns -1 for invalid command.
+ */
+ static int
+find_command(cmdchar)
+ int cmdchar;
+{
+ int i;
+ int idx;
+ int top, bot;
+ int c;
+
+#ifdef FEAT_MBYTE
+ /* A multi-byte character is never a command. */
+ if (cmdchar >= 0x100)
+ return -1;
+#endif
+
+ /* We use the absolute value of the character. Special keys have a
+ * negative value, but are sorted on their absolute value. */
+ if (cmdchar < 0)
+ cmdchar = -cmdchar;
+
+ /* If the character is in the first part: The character is the index into
+ * nv_cmd_idx[]. */
+ if (cmdchar <= nv_max_linear)
+ return nv_cmd_idx[cmdchar];
+
+ /* Perform a binary search. */
+ bot = nv_max_linear + 1;
+ top = NV_CMDS_SIZE - 1;
+ idx = -1;
+ while (bot <= top)
+ {
+ i = (top + bot) / 2;
+ c = nv_cmds[nv_cmd_idx[i]].cmd_char;
+ if (c < 0)
+ c = -c;
+ if (cmdchar == c)
+ {
+ idx = nv_cmd_idx[i];
+ break;
+ }
+ if (cmdchar > c)
+ bot = i + 1;
+ else
+ top = i - 1;
+ }
+ return idx;
+}
+
+/*
+ * Execute a command in Normal mode.
+ */
+/*ARGSUSED*/
+ void
+normal_cmd(oap, toplevel)
+ oparg_T *oap;
+ int toplevel; /* TRUE when called from main() */
+{
+ static long opcount = 0; /* ca.opcount saved here */
+ cmdarg_T ca; /* command arguments */
+ int c;
+ int ctrl_w = FALSE; /* got CTRL-W command */
+ int old_col = curwin->w_curswant;
+#ifdef FEAT_CMDL_INFO
+ int need_flushbuf; /* need to call out_flush() */
+#endif
+#ifdef FEAT_VISUAL
+ pos_T old_pos; /* cursor position before command */
+ int mapped_len;
+#endif
+ static int old_mapped_len = 0;
+ int idx;
+
+ vim_memset(&ca, 0, sizeof(ca)); /* also resets ca.retval */
+ ca.oap = oap;
+ ca.opcount = opcount;
+
+#ifdef FEAT_SNIFF
+ want_sniff_request = sniff_connected;
+#endif
+
+ /*
+ * If there is an operator pending, then the command we take this time
+ * will terminate it. Finish_op tells us to finish the operation before
+ * returning this time (unless the operation was cancelled).
+ */
+#ifdef CURSOR_SHAPE
+ c = finish_op;
+#endif
+ finish_op = (oap->op_type != OP_NOP);
+#ifdef CURSOR_SHAPE
+ if (finish_op != c)
+ {
+ ui_cursor_shape(); /* may show different cursor shape */
+# ifdef FEAT_MOUSESHAPE
+ update_mouseshape(-1);
+# endif
+ }
+#endif
+
+ if (!finish_op && !oap->regname)
+ ca.opcount = 0;
+
+#ifdef FEAT_VISUAL
+ mapped_len = typebuf_maplen();
+#endif
+
+ State = NORMAL_BUSY;
+#ifdef USE_ON_FLY_SCROLL
+ dont_scroll = FALSE; /* allow scrolling here */
+#endif
+
+ /*
+ * Get the command character from the user.
+ */
+ c = safe_vgetc();
+
+#ifdef FEAT_LANGMAP
+ LANGMAP_ADJUST(c, TRUE);
+#endif
+
+ /*
+ * If a mapping was started in Visual or Select mode, remember the length
+ * of the mapping. This is used below to not return to Insert mode for as
+ * long as the mapping is being executed.
+ */
+ if (restart_edit == 0)
+ old_mapped_len = 0;
+ else if (old_mapped_len
+#ifdef FEAT_VISUAL
+ || (VIsual_active && mapped_len == 0 && typebuf_maplen() > 0)
+#endif
+ )
+ old_mapped_len = typebuf_maplen();
+
+ if (c == NUL)
+ c = K_ZERO;
+
+#ifdef FEAT_VISUAL
+ /*
+ * In Select mode, typed text replaces the selection.
+ */
+ if (VIsual_active
+ && VIsual_select
+ && (vim_isprintc(c) || c == NL || c == CAR || c == K_KENTER))
+ {
+# ifdef FEAT_MBYTE
+ char_u buf[MB_MAXBYTES + 1];
+
+ buf[(*mb_char2bytes)(c, buf)] = NUL;
+# else
+ char_u buf[2];
+
+ buf[0] = c;
+ buf[1] = NUL;
+# endif
+ /* Fake a "c"hange command.
+ * Insert the typed character in the typeahead buffer, so that it will
+ * be mapped in Insert mode. Required for ":lmap" to work. May cause
+ * mapping a character from ":vnoremap"... */
+ (void)ins_typebuf(buf, REMAP_YES, 0, !KeyTyped, FALSE);
+ c = 'c';
+ }
+#endif
+
+#ifdef FEAT_CMDL_INFO
+ need_flushbuf = add_to_showcmd(c);
+#endif
+
+getcount:
+#ifdef FEAT_VISUAL
+ if (!(VIsual_active && VIsual_select))
+#endif
+ {
+ /*
+ * Handle a count before a command and compute ca.count0.
+ * Note that '0' is a command and not the start of a count, but it's
+ * part of a count after other digits.
+ */
+ while ( (c >= '1' && c <= '9')
+ || (ca.count0 != 0 && (c == K_DEL || c == K_KDEL || c == '0')))
+ {
+ if (c == K_DEL || c == K_KDEL)
+ {
+ ca.count0 /= 10;
+#ifdef FEAT_CMDL_INFO
+ del_from_showcmd(4); /* delete the digit and ~@% */
+#endif
+ }
+ else
+ ca.count0 = ca.count0 * 10 + (c - '0');
+ if (ca.count0 < 0) /* got too large! */
+ ca.count0 = 999999999L;
+ if (ctrl_w)
+ {
+ ++no_mapping;
+ ++allow_keys; /* no mapping for nchar, but keys */
+ }
+ ++no_zero_mapping; /* don't map zero here */
+ c = safe_vgetc();
+#ifdef FEAT_LANGMAP
+ LANGMAP_ADJUST(c, TRUE);
+#endif
+ --no_zero_mapping;
+ if (ctrl_w)
+ {
+ --no_mapping;
+ --allow_keys;
+ }
+#ifdef FEAT_CMDL_INFO
+ need_flushbuf |= add_to_showcmd(c);
+#endif
+ }
+
+ /*
+ * If we got CTRL-W there may be a/another count
+ */
+ if (c == Ctrl_W && !ctrl_w && oap->op_type == OP_NOP)
+ {
+ ctrl_w = TRUE;
+ ca.opcount = ca.count0; /* remember first count */
+ ca.count0 = 0;
+ ++no_mapping;
+ ++allow_keys; /* no mapping for nchar, but keys */
+ c = safe_vgetc(); /* get next character */
+#ifdef FEAT_LANGMAP
+ LANGMAP_ADJUST(c, TRUE);
+#endif
+ --no_mapping;
+ --allow_keys;
+#ifdef FEAT_CMDL_INFO
+ need_flushbuf |= add_to_showcmd(c);
+#endif
+ goto getcount; /* jump back */
+ }
+ }
+
+ /*
+ * If we're in the middle of an operator (including after entering a yank
+ * buffer with '"') AND we had a count before the operator, then that
+ * count overrides the current value of ca.count0.
+ * What this means effectively, is that commands like "3dw" get turned
+ * into "d3w" which makes things fall into place pretty neatly.
+ * If you give a count before AND after the operator, they are multiplied.
+ */
+ if (ca.opcount != 0)
+ {
+ if (ca.count0)
+ ca.count0 *= ca.opcount;
+ else
+ ca.count0 = ca.opcount;
+ }
+
+ /*
+ * Always remember the count. It will be set to zero (on the next call,
+ * above) when there is no pending operator.
+ * When called from main(), save the count for use by the "count" built-in
+ * variable.
+ */
+ ca.opcount = ca.count0;
+ ca.count1 = (ca.count0 == 0 ? 1 : ca.count0);
+
+#ifdef FEAT_EVAL
+ /*
+ * Only set v:count when called from main() and not a stuffed command.
+ */
+ if (toplevel && stuff_empty())
+ set_vcount(ca.count0, ca.count1);
+#endif
+
+ /*
+ * Find the command character in the table of commands.
+ * For CTRL-W we already got nchar when looking for a count.
+ */
+ if (ctrl_w)
+ {
+ ca.nchar = c;
+ ca.cmdchar = Ctrl_W;
+ }
+ else
+ ca.cmdchar = c;
+ idx = find_command(ca.cmdchar);
+ if (idx < 0)
+ {
+ /* Not a known command: beep. */
+ clearopbeep(oap);
+ goto normal_end;
+ }
+#ifdef FEAT_CMDWIN
+ if (cmdwin_type != 0 && (nv_cmds[idx].cmd_flags & NV_NCW))
+ {
+ /* This command is not allowed in the cmdline window: beep. */
+ clearopbeep(oap);
+ EMSG(_(e_cmdwin));
+ goto normal_end;
+ }
+#endif
+
+#ifdef FEAT_VISUAL
+ /*
+ * In Visual/Select mode, a few keys are handled in a special way.
+ */
+ if (VIsual_active)
+ {
+ /* when 'keymodel' contains "stopsel" may stop Select/Visual mode */
+ if (km_stopsel
+ && (nv_cmds[idx].cmd_flags & NV_STS)
+ && !(mod_mask & MOD_MASK_SHIFT))
+ {
+ end_visual_mode();
+ redraw_curbuf_later(INVERTED);
+ }
+
+ /* Keys that work different when 'keymodel' contains "startsel" */
+ if (km_startsel)
+ {
+ if (nv_cmds[idx].cmd_flags & NV_SS)
+ {
+ unshift_special(&ca);
+ idx = find_command(ca.cmdchar);
+ }
+ else if ((nv_cmds[idx].cmd_flags & NV_SSS)
+ && (mod_mask & MOD_MASK_SHIFT))
+ {
+ mod_mask &= ~MOD_MASK_SHIFT;
+ }
+ }
+ }
+#endif
+
+#ifdef FEAT_RIGHTLEFT
+ if (curwin->w_p_rl && KeyTyped && !KeyStuffed
+ && (nv_cmds[idx].cmd_flags & NV_RL))
+ {
+ /* Invert horizontal movements and operations. Only when typed by the
+ * user directly, not when the result of a mapping or "x" translated
+ * to "dl". */
+ switch (ca.cmdchar)
+ {
+ case 'l': ca.cmdchar = 'h'; break;
+ case K_RIGHT: ca.cmdchar = K_LEFT; break;
+ case K_S_RIGHT: ca.cmdchar = K_S_LEFT; break;
+ case K_C_RIGHT: ca.cmdchar = K_C_LEFT; break;
+ case 'h': ca.cmdchar = 'l'; break;
+ case K_LEFT: ca.cmdchar = K_RIGHT; break;
+ case K_S_LEFT: ca.cmdchar = K_S_RIGHT; break;
+ case K_C_LEFT: ca.cmdchar = K_C_RIGHT; break;
+ case '>': ca.cmdchar = '<'; break;
+ case '<': ca.cmdchar = '>'; break;
+ }
+ idx = find_command(ca.cmdchar);
+ }
+#endif
+
+ /*
+ * Get an additional character if we need one.
+ */
+ if ((nv_cmds[idx].cmd_flags & NV_NCH)
+ && (((nv_cmds[idx].cmd_flags & NV_NCH_NOP) == NV_NCH_NOP
+ && oap->op_type == OP_NOP)
+ || (nv_cmds[idx].cmd_flags & NV_NCH_ALW) == NV_NCH_ALW
+ || (ca.cmdchar == 'q'
+ && oap->op_type == OP_NOP
+ && !Recording
+ && !Exec_reg)
+ || ((ca.cmdchar == 'a' || ca.cmdchar == 'i')
+ && (oap->op_type != OP_NOP
+#ifdef FEAT_VISUAL
+ || VIsual_active
+#endif
+ ))))
+ {
+ int *cp;
+ int repl = FALSE; /* get character for replace mode */
+ int lit = FALSE; /* get extra character literally */
+ int langmap_active = FALSE; /* using :lmap mappings */
+ int lang; /* getting a text character */
+#ifdef USE_IM_CONTROL
+ int save_smd; /* saved value of p_smd */
+#endif
+
+ ++no_mapping;
+ ++allow_keys; /* no mapping for nchar, but allow key codes */
+ if (ca.cmdchar == 'g')
+ {
+ /*
+ * For 'g' get the next character now, so that we can check for
+ * "gr", "g'" and "g`".
+ */
+ ca.nchar = safe_vgetc();
+#ifdef FEAT_LANGMAP
+ LANGMAP_ADJUST(ca.nchar, TRUE);
+#endif
+#ifdef FEAT_CMDL_INFO
+ need_flushbuf |= add_to_showcmd(ca.nchar);
+#endif
+ if (ca.nchar == 'r' || ca.nchar == '\'' || ca.nchar == '`'
+ || ca.nchar == Ctrl_BSL)
+ {
+ cp = &ca.extra_char; /* need to get a third character */
+ if (ca.nchar != 'r')
+ lit = TRUE; /* get it literally */
+ else
+ repl = TRUE; /* get it in replace mode */
+ }
+ else
+ cp = NULL; /* no third character needed */
+ }
+ else
+ {
+ if (ca.cmdchar == 'r') /* get it in replace mode */
+ repl = TRUE;
+ cp = &ca.nchar;
+ }
+ lang = (repl || (nv_cmds[idx].cmd_flags & NV_LANG));
+
+ /*
+ * Get a second or third character.
+ */
+ if (cp != NULL)
+ {
+#ifdef CURSOR_SHAPE
+ if (repl)
+ {
+ State = REPLACE; /* pretend Replace mode */
+ ui_cursor_shape(); /* show different cursor shape */
+ }
+#endif
+ if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP)
+ {
+ /* Allow mappings defined with ":lmap". */
+ --no_mapping;
+ --allow_keys;
+ if (repl)
+ State = LREPLACE;
+ else
+ State = LANGMAP;
+ langmap_active = TRUE;
+ }
+#ifdef USE_IM_CONTROL
+ save_smd = p_smd;
+ p_smd = FALSE; /* Don't let the IM code show the mode here */
+ if (lang && curbuf->b_p_iminsert == B_IMODE_IM)
+ im_set_active(TRUE);
+#endif
+
+ *cp = safe_vgetc();
+
+ if (langmap_active)
+ {
+ /* Undo the decrement done above */
+ ++no_mapping;
+ ++allow_keys;
+ State = NORMAL_BUSY;
+ }
+#ifdef USE_IM_CONTROL
+ if (lang)
+ {
+ if (curbuf->b_p_iminsert != B_IMODE_LMAP)
+ im_save_status(&curbuf->b_p_iminsert);
+ im_set_active(FALSE);
+ }
+ p_smd = save_smd;
+#endif
+#ifdef CURSOR_SHAPE
+ State = NORMAL_BUSY;
+#endif
+#ifdef FEAT_CMDL_INFO
+ need_flushbuf |= add_to_showcmd(*cp);
+#endif
+
+ if (!lit)
+ {
+#ifdef FEAT_DIGRAPHS
+ /* Typing CTRL-K gets a digraph. */
+ if (*cp == Ctrl_K
+ && ((nv_cmds[idx].cmd_flags & NV_LANG)
+ || cp == &ca.extra_char)
+ && vim_strchr(p_cpo, CPO_DIGRAPH) == NULL)
+ {
+ c = get_digraph(FALSE);
+ if (c > 0)
+ {
+ *cp = c;
+# ifdef FEAT_CMDL_INFO
+ /* Guessing how to update showcmd here... */
+ del_from_showcmd(3);
+ need_flushbuf |= add_to_showcmd(*cp);
+# endif
+ }
+ }
+#endif
+
+#ifdef FEAT_LANGMAP
+ /* adjust chars > 127, except after "tTfFr" commands */
+ LANGMAP_ADJUST(*cp, !lang);
+#endif
+#ifdef FEAT_RIGHTLEFT
+ /* adjust Hebrew mapped char */
+ if (p_hkmap && lang && KeyTyped)
+ *cp = hkmap(*cp);
+# ifdef FEAT_FKMAP
+ /* adjust Farsi mapped char */
+ if (p_fkmap && lang && KeyTyped)
+ *cp = fkmap(*cp);
+# endif
+#endif
+ }
+
+ /*
+ * When the next character is CTRL-\ a following CTRL-N means the
+ * command is aborted and we go to Normal mode.
+ */
+ if (cp == &ca.extra_char
+ && ca.nchar == Ctrl_BSL
+ && (ca.extra_char == Ctrl_N || ca.extra_char == Ctrl_G))
+ {
+ ca.cmdchar = Ctrl_BSL;
+ ca.nchar = ca.extra_char;
+ idx = find_command(ca.cmdchar);
+ }
+ else if (*cp == Ctrl_BSL)
+ {
+ long towait = (p_ttm >= 0 ? p_ttm : p_tm);
+
+ /* There is a busy wait here when typing "f<C-\>" and then
+ * something different from CTRL-N. Can't be avoided. */
+ while ((c = vpeekc()) <= 0 && towait > 0L)
+ {
+ do_sleep(towait > 50L ? 50L : towait);
+ towait -= 50L;
+ }
+ if (c > 0)
+ {
+ c = safe_vgetc();
+ if (c != Ctrl_N && c != Ctrl_G)
+ vungetc(c);
+ else
+ {
+ ca.cmdchar = Ctrl_BSL;
+ ca.nchar = c;
+ idx = find_command(ca.cmdchar);
+ }
+ }
+ }
+
+#ifdef FEAT_MBYTE
+ /* When getting a text character and the next character is a
+ * multi-byte character, it could be a composing character.
+ * However, don't wait for it to arrive. */
+ while (enc_utf8 && lang && (c = vpeekc()) > 0
+ && (c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1))
+ {
+ c = safe_vgetc();
+ if (!utf_iscomposing(c))
+ {
+ vungetc(c); /* it wasn't, put it back */
+ break;
+ }
+ else if (ca.ncharC1 == 0)
+ ca.ncharC1 = c;
+ else
+ ca.ncharC2 = c;
+ }
+#endif
+ }
+ --no_mapping;
+ --allow_keys;
+ }
+
+#ifdef FEAT_CMDL_INFO
+ /*
+ * Flush the showcmd characters onto the screen so we can see them while
+ * the command is being executed. Only do this when the shown command was
+ * actually displayed, otherwise this will slow down a lot when executing
+ * mappings.
+ */
+ if (need_flushbuf)
+ out_flush();
+#endif
+
+ State = NORMAL;
+
+ if (ca.nchar == ESC)
+ {
+ clearop(oap);
+ if (restart_edit == 0 && goto_im())
+ restart_edit = 'a';
+ goto normal_end;
+ }
+
+ msg_didout = FALSE; /* don't scroll screen up for normal command */
+ msg_col = 0;
+
+#ifdef FEAT_VISUAL
+ old_pos = curwin->w_cursor; /* remember where cursor was */
+
+ /* When 'keymodel' contains "startsel" some keys start Select/Visual
+ * mode. */
+ if (!VIsual_active && km_startsel)
+ {
+ if (nv_cmds[idx].cmd_flags & NV_SS)
+ {
+ start_selection();
+ unshift_special(&ca);
+ idx = find_command(ca.cmdchar);
+ }
+ else if ((nv_cmds[idx].cmd_flags & NV_SSS)
+ && (mod_mask & MOD_MASK_SHIFT))
+ {
+ start_selection();
+ mod_mask &= ~MOD_MASK_SHIFT;
+ }
+ }
+#endif
+
+ /*
+ * Execute the command!
+ * Call the command function found in the commands table.
+ */
+ ca.arg = nv_cmds[idx].cmd_arg;
+ (nv_cmds[idx].cmd_func)(&ca);
+
+ /*
+ * If we didn't start or finish an operator, reset oap->regname, unless we
+ * need it later.
+ */
+ if (!finish_op
+ && !oap->op_type
+ && (idx < 0 || !(nv_cmds[idx].cmd_flags & NV_KEEPREG)))
+ {
+ clearop(oap);
+#ifdef FEAT_EVAL
+ set_reg_var('"');
+#endif
+ }
+
+ /*
+ * If an operation is pending, handle it...
+ */
+ do_pending_operator(&ca, old_col, FALSE);
+
+ /*
+ * Wait for a moment when a message is displayed that will be overwritten
+ * by the mode message.
+ * In Visual mode and with "^O" in Insert mode, a short message will be
+ * overwritten by the mode message. Wait a bit, until a key is hit.
+ * In Visual mode, it's more important to keep the Visual area updated
+ * than keeping a message (e.g. from a /pat search).
+ * Only do this if the command was typed, not from a mapping.
+ * Don't wait when emsg_silent is non-zero.
+ * Also wait a bit after an error message, e.g. for "^O:".
+ * Don't redraw the screen, it would remove the message.
+ */
+ if ( ((p_smd
+ && (restart_edit != 0
+#ifdef FEAT_VISUAL
+ || (VIsual_active
+ && old_pos.lnum == curwin->w_cursor.lnum