diff options
Diffstat (limited to 'src/normal.c')
-rw-r--r-- | src/normal.c | 8508 |
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 |