diff options
Diffstat (limited to 'src/nvim/normal.c')
| -rw-r--r-- | src/nvim/normal.c | 7474 |
1 files changed, 7474 insertions, 0 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c new file mode 100644 index 0000000000..dc0035fe85 --- /dev/null +++ b/src/nvim/normal.c @@ -0,0 +1,7474 @@ +/* + * 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 <string.h> +#include <stdlib.h> + +#include "vim.h" +#include "normal.h" +#include "buffer.h" +#include "charset.h" +#include "diff.h" +#include "digraph.h" +#include "edit.h" +#include "eval.h" +#include "ex_cmds.h" +#include "ex_cmds2.h" +#include "ex_docmd.h" +#include "ex_getln.h" +#include "farsi.h" +#include "fileio.h" +#include "fold.h" +#include "getchar.h" +#include "indent.h" +#include "indent_c.h" +#include "main.h" +#include "mark.h" +#include "memline.h" +#include "memory.h" +#include "message.h" +#include "misc1.h" +#include "misc2.h" +#include "keymap.h" +#include "move.h" +#include "ops.h" +#include "option.h" +#include "quickfix.h" +#include "screen.h" +#include "search.h" +#include "spell.h" +#include "syntax.h" +#include "tag.h" +#include "term.h" +#include "ui.h" +#include "undo.h" +#include "window.h" +#include "os/event.h" + +/* + * 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_vcol; /* nr of cols or end col */ +static int VIsual_mode_orig = NUL; /* saved Visual mode */ + +static int restart_VIsual_select = 0; + +static void set_vcount_ca(cmdarg_T *cap, int *set_prevcount); +static int +nv_compare(const void *s1, const void *s2); +static int find_command(int cmdchar); +static void op_colon(oparg_T *oap); +static void op_function(oparg_T *oap); +static void find_start_of_word(pos_T *); +static void find_end_of_word(pos_T *); +static int get_mouse_class(char_u *p); +static void prep_redo_cmd(cmdarg_T *cap); +static void prep_redo(int regname, long, int, int, int, int, int); +static int checkclearop(oparg_T *oap); +static int checkclearopq(oparg_T *oap); +static void clearop(oparg_T *oap); +static void clearopbeep(oparg_T *oap); +static void unshift_special(cmdarg_T *cap); +static void del_from_showcmd(int); + +/* + * 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(cmdarg_T *cap); +static void nv_nop(cmdarg_T *cap); +static void nv_error(cmdarg_T *cap); +static void nv_help(cmdarg_T *cap); +static void nv_addsub(cmdarg_T *cap); +static void nv_page(cmdarg_T *cap); +static void nv_gd(oparg_T *oap, int nchar, int thisblock); +static int nv_screengo(oparg_T *oap, int dir, long dist); +static void nv_mousescroll(cmdarg_T *cap); +static void nv_mouse(cmdarg_T *cap); +static void nv_scroll_line(cmdarg_T *cap); +static void nv_zet(cmdarg_T *cap); +static void nv_exmode(cmdarg_T *cap); +static void nv_colon(cmdarg_T *cap); +static void nv_ctrlg(cmdarg_T *cap); +static void nv_ctrlh(cmdarg_T *cap); +static void nv_clear(cmdarg_T *cap); +static void nv_ctrlo(cmdarg_T *cap); +static void nv_hat(cmdarg_T *cap); +static void nv_Zet(cmdarg_T *cap); +static void nv_ident(cmdarg_T *cap); +static void nv_tagpop(cmdarg_T *cap); +static void nv_scroll(cmdarg_T *cap); +static void nv_right(cmdarg_T *cap); +static void nv_left(cmdarg_T *cap); +static void nv_up(cmdarg_T *cap); +static void nv_down(cmdarg_T *cap); +static void nv_gotofile(cmdarg_T *cap); +static void nv_end(cmdarg_T *cap); +static void nv_dollar(cmdarg_T *cap); +static void nv_search(cmdarg_T *cap); +static void nv_next(cmdarg_T *cap); +static void normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt); +static void nv_csearch(cmdarg_T *cap); +static void nv_brackets(cmdarg_T *cap); +static void nv_percent(cmdarg_T *cap); +static void nv_brace(cmdarg_T *cap); +static void nv_mark(cmdarg_T *cap); +static void nv_findpar(cmdarg_T *cap); +static void nv_undo(cmdarg_T *cap); +static void nv_kundo(cmdarg_T *cap); +static void nv_Replace(cmdarg_T *cap); +static void nv_vreplace(cmdarg_T *cap); +static void v_swap_corners(int cmdchar); +static void nv_replace(cmdarg_T *cap); +static void n_swapchar(cmdarg_T *cap); +static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos); +static void v_visop(cmdarg_T *cap); +static void nv_subst(cmdarg_T *cap); +static void nv_abbrev(cmdarg_T *cap); +static void nv_optrans(cmdarg_T *cap); +static void nv_gomark(cmdarg_T *cap); +static void nv_pcmark(cmdarg_T *cap); +static void nv_regname(cmdarg_T *cap); +static void nv_visual(cmdarg_T *cap); +static void n_start_visual_mode(int c); +static void nv_window(cmdarg_T *cap); +static void nv_suspend(cmdarg_T *cap); +static void nv_g_cmd(cmdarg_T *cap); +static void n_opencmd(cmdarg_T *cap); +static void nv_dot(cmdarg_T *cap); +static void nv_redo(cmdarg_T *cap); +static void nv_Undo(cmdarg_T *cap); +static void nv_tilde(cmdarg_T *cap); +static void nv_operator(cmdarg_T *cap); +static void set_op_var(int optype); +static void nv_lineop(cmdarg_T *cap); +static void nv_home(cmdarg_T *cap); +static void nv_pipe(cmdarg_T *cap); +static void nv_bck_word(cmdarg_T *cap); +static void nv_wordcmd(cmdarg_T *cap); +static void nv_beginline(cmdarg_T *cap); +static void adjust_cursor(oparg_T *oap); +static void adjust_for_sel(cmdarg_T *cap); +static int unadjust_for_sel(void); +static void nv_select(cmdarg_T *cap); +static void nv_goto(cmdarg_T *cap); +static void nv_normal(cmdarg_T *cap); +static void nv_esc(cmdarg_T *oap); +static void nv_edit(cmdarg_T *cap); +static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln); +static void nv_object(cmdarg_T *cap); +static void nv_record(cmdarg_T *cap); +static void nv_at(cmdarg_T *cap); +static void nv_halfpage(cmdarg_T *cap); +static void nv_join(cmdarg_T *cap); +static void nv_put(cmdarg_T *cap); +static void nv_open(cmdarg_T *cap); +static void nv_cursorhold(cmdarg_T *cap); +static void nv_event(cmdarg_T *cap); + +static char *e_noident = N_("E349: No identifier under cursor"); + +/* + * Function to be called for a Normal or Visual mode command. + * The argument is a cmdarg_T. + */ +typedef void (*nv_func_T)(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 */ + uint16_t 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}, + {Ctrl_M, 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_visual, 0, FALSE}, + {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}, + {Ctrl_V, nv_visual, 0, FALSE}, + {'V', nv_visual, 0, FALSE}, + {'v', nv_visual, 0, FALSE}, + {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}, + {K_MOUSEUP, nv_mousescroll, 0, MSCR_UP}, + {K_MOUSEDOWN, nv_mousescroll, 0, MSCR_DOWN}, + {K_MOUSELEFT, nv_mousescroll, 0, MSCR_LEFT}, + {K_MOUSERIGHT, nv_mousescroll, 0, MSCR_RIGHT}, + {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}, + {K_IGNORE, nv_ignore, NV_KEEPREG, 0}, + {K_NOP, nv_nop, 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_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_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}, + {K_SELECT, nv_select, 0, 0}, + {K_F8, farsi_fkey, 0, 0}, + {K_F9, farsi_fkey, 0, 0}, + {K_CURSORHOLD, nv_cursorhold, NV_KEEPREG, 0}, + {K_EVENT, nv_event, NV_KEEPREG, 0}, +}; + +/* 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 nv_compare(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(void) +{ + int i; + + /* Fill the index table with a one to one relation. */ + for (i = 0; i < (int)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 < (int)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(int cmdchar) +{ + int i; + int idx; + int top, bot; + int c; + + /* A multi-byte character is never a command. */ + if (cmdchar >= 0x100) + return -1; + + /* 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. + */ +void +normal_cmd ( + oparg_T *oap, + int toplevel /* TRUE when called from main() */ +) +{ + cmdarg_T ca; /* command arguments */ + int c; + int ctrl_w = FALSE; /* got CTRL-W command */ + int old_col = curwin->w_curswant; + int need_flushbuf; /* need to call out_flush() */ + pos_T old_pos; /* cursor position before command */ + int mapped_len; + static int old_mapped_len = 0; + int idx; + int set_prevcount = FALSE; + + memset(&ca, 0, sizeof(ca)); /* also resets ca.retval */ + ca.oap = oap; + + /* Use a count remembered from before entering an operator. After typing + * "3d" we return from normal_cmd() and come back here, the "3" is + * remembered in "opcount". */ + ca.opcount = opcount; + + + /* + * 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). + */ + c = finish_op; + finish_op = (oap->op_type != OP_NOP); + if (finish_op != c) { + ui_cursor_shape(); /* may show different cursor shape */ + } + + /* When not finishing an operator and no register name typed, reset the + * count. */ + if (!finish_op && !oap->regname) { + ca.opcount = 0; + set_prevcount = TRUE; + } + + /* Restore counts from before receiving K_CURSORHOLD. This means after + * typing "3", handling K_CURSORHOLD and then typing "2" we get "32", not + * "3 * 2". */ + if (oap->prev_opcount > 0 || oap->prev_count0 > 0) { + ca.opcount = oap->prev_opcount; + ca.count0 = oap->prev_count0; + oap->prev_opcount = 0; + oap->prev_count0 = 0; + } + + mapped_len = typebuf_maplen(); + + State = NORMAL_BUSY; +#ifdef USE_ON_FLY_SCROLL + dont_scroll = FALSE; /* allow scrolling here */ +#endif + + /* Set v:count here, when called from main() and not a stuffed + * command, so that v:count can be used in an expression mapping + * when there is no count. Do set it for redo. */ + if (toplevel && readbuf1_empty()) + set_vcount_ca(&ca, &set_prevcount); + + /* + * Get the command character from the user. + */ + c = safe_vgetc(); + LANGMAP_ADJUST(c, TRUE); + + /* + * 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 + || (VIsual_active && mapped_len == 0 && typebuf_maplen() > 0)) + old_mapped_len = typebuf_maplen(); + + if (c == NUL) + c = K_ZERO; + + /* + * In Select mode, typed text replaces the selection. + */ + if (VIsual_active + && VIsual_select + && (vim_isprintc(c) || c == NL || c == CAR || c == K_KENTER)) { + /* Fake a "c"hange command. When "restart_edit" is set (e.g., because + * 'insertmode' is set) fake a "d"elete command, Insert mode will + * restart automatically. + * Insert the typed character in the typeahead buffer, so that it can + * be mapped in Insert mode. Required for ":lmap" to work. */ + ins_char_typebuf(c); + if (restart_edit != 0) + c = 'd'; + else + c = 'c'; + msg_nowait = TRUE; /* don't delay going to insert mode */ + old_mapped_len = 0; /* do go to Insert mode */ + } + + need_flushbuf = add_to_showcmd(c); + +getcount: + if (!(VIsual_active && VIsual_select)) { + /* + * 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; + del_from_showcmd(4); /* delete the digit and ~@% */ + } else + ca.count0 = ca.count0 * 10 + (c - '0'); + if (ca.count0 < 0) /* got too large! */ + ca.count0 = 999999999L; + /* Set v:count here, when called from main() and not a stuffed + * command, so that v:count can be used in an expression mapping + * right after the count. Do set it for redo. */ + if (toplevel && readbuf1_empty()) + set_vcount_ca(&ca, &set_prevcount); + if (ctrl_w) { + ++no_mapping; + ++allow_keys; /* no mapping for nchar, but keys */ + } + ++no_zero_mapping; /* don't map zero here */ + c = plain_vgetc(); + LANGMAP_ADJUST(c, TRUE); + --no_zero_mapping; + if (ctrl_w) { + --no_mapping; + --allow_keys; + } + need_flushbuf |= add_to_showcmd(c); + } + + /* + * 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 = plain_vgetc(); /* get next character */ + LANGMAP_ADJUST(c, TRUE); + --no_mapping; + --allow_keys; + need_flushbuf |= add_to_showcmd(c); + goto getcount; /* jump back */ + } + } + + if (c == K_CURSORHOLD) { + /* Save the count values so that ca.opcount and ca.count0 are exactly + * the same when coming back here after handling K_CURSORHOLD. */ + oap->prev_opcount = ca.opcount; + oap->prev_count0 = ca.count0; + } else if (ca.opcount != 0) { + /* + * 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.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); + + /* + * Only set v:count when called from main() and not a stuffed command. + * Do set it for redo. + */ + if (toplevel && readbuf1_empty()) + set_vcount(ca.count0, ca.count1, set_prevcount); + + /* + * 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; + } + + if (text_locked() && (nv_cmds[idx].cmd_flags & NV_NCW)) { + /* This command is not allowed while editing a ccmdline: beep. */ + clearopbeep(oap); + text_locked_msg(); + goto normal_end; + } + if ((nv_cmds[idx].cmd_flags & NV_NCW) && curbuf_locked()) + goto normal_end; + + /* + * 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); + if (idx < 0) { + /* Just in case */ + clearopbeep(oap); + goto normal_end; + } + } else if ((nv_cmds[idx].cmd_flags & NV_SSS) + && (mod_mask & MOD_MASK_SHIFT)) { + mod_mask &= ~MOD_MASK_SHIFT; + } + } + } + + 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); + } + + /* + * 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 + || VIsual_active + )))) { + 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 */ + /* Don't generate a CursorHold event here, most commands can't handle + * it, e.g., nv_replace(), nv_csearch(). */ + did_cursorhold = TRUE; + if (ca.cmdchar == 'g') { + /* + * For 'g' get the next character now, so that we can check for + * "gr", "g'" and "g`". + */ + ca.nchar = plain_vgetc(); + LANGMAP_ADJUST(ca.nchar, TRUE); + need_flushbuf |= add_to_showcmd(ca.nchar); + 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) { + if (repl) { + State = REPLACE; /* pretend Replace mode */ + ui_cursor_shape(); /* show different cursor shape */ + } + 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 = plain_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 + State = NORMAL_BUSY; + need_flushbuf |= add_to_showcmd(*cp); + + if (!lit) { + /* 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; + /* Guessing how to update showcmd here... */ + del_from_showcmd(3); + need_flushbuf |= add_to_showcmd(*cp); + } + } + + /* adjust chars > 127, except after "tTfFr" commands */ + LANGMAP_ADJUST(*cp, !lang); + /* adjust Hebrew mapped char */ + if (p_hkmap && lang && KeyTyped) + *cp = hkmap(*cp); + /* adjust Farsi mapped char */ + if (p_fkmap && lang && KeyTyped) + *cp = fkmap(*cp); + } + + /* + * 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 ((ca.nchar == 'n' || ca.nchar == 'N') && ca.cmdchar == 'g') + ca.oap->op_type = get_op_type(*cp, NUL); + 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 = plain_vgetc(); + if (c != Ctrl_N && c != Ctrl_G) + vungetc(c); + else { + ca.cmdchar = Ctrl_BSL; + ca.nchar = c; + idx = find_command(ca.cmdchar); + } + } + } + + /* 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 = plain_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; + } + } + --no_mapping; + --allow_keys; + } + + /* + * 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. < |