From da51dc9cf202772f60bd2da975dbef257bd9237c Mon Sep 17 00:00:00 2001 From: Eliseo Martínez Date: Mon, 12 May 2014 02:25:17 +0200 Subject: Introduce nvim namespace: Move files. Move files from src/ to src/nvim/. - src/nvim/ becomes the new root dir for nvim executable sources. - src/libnvim/ is planned to become root dir of the neovim library. --- src/nvim/normal.c | 7474 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 7474 insertions(+) create mode 100644 src/nvim/normal.c (limited to 'src/nvim/normal.c') 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 +#include + +#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" 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. + */ + if (need_flushbuf) + out_flush(); + if (ca.cmdchar != K_IGNORE) + did_cursorhold = FALSE; + + State = NORMAL; + + if (ca.nchar == ESC) { + clearop(oap); + if (restart_edit == 0 && goto_im()) + restart_edit = 'a'; + goto normal_end; + } + + if (ca.cmdchar != K_IGNORE) { + msg_didout = FALSE; /* don't scroll screen up for normal command */ + msg_col = 0; + } + + 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; + } + } + + /* + * 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); + { + int regname = 0; + + /* Adjust the register according to 'clipboard', so that when + * "unnamed" is present it becomes '*' or '+' instead of '"'. */ + set_reg_var(regname); + } + } + + /* Get the length of mapped chars again after typing a count, second + * character or "z333". */ + if (old_mapped_len > 0) + old_mapped_len = typebuf_maplen(); + + /* + * 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 + && msg_silent == 0 + && (restart_edit != 0 + || (VIsual_active + && old_pos.lnum == curwin->w_cursor.lnum + && old_pos.col == curwin->w_cursor.col) + ) + && (clear_cmdline + || redraw_cmdline) + && (msg_didout || (msg_didany && msg_scroll)) + && !msg_nowait + && KeyTyped) + || (restart_edit != 0 + && !VIsual_active + && (msg_scroll + || emsg_on_display))) + && oap->regname == 0 + && !(ca.retval & CA_COMMAND_BUSY) + && stuff_empty() + && typebuf_typed() + && emsg_silent == 0 + && !did_wait_return + && oap->op_type == OP_NOP) { + int save_State = State; + + /* Draw the cursor with the right shape here */ + if (restart_edit != 0) + State = INSERT; + + /* If need to redraw, and there is a "keep_msg", redraw before the + * delay */ + if (must_redraw && keep_msg != NULL && !emsg_on_display) { + char_u *kmsg; + + kmsg = keep_msg; + keep_msg = NULL; + /* showmode() will clear keep_msg, but we want to use it anyway */ + update_screen(0); + /* now reset it, otherwise it's put in the history again */ + keep_msg = kmsg; + msg_attr(kmsg, keep_msg_attr); + free(kmsg); + } + setcursor(); + cursor_on(); + out_flush(); + if (msg_scroll || emsg_on_display) + ui_delay(1000L, TRUE); /* wait at least one second */ + ui_delay(3000L, FALSE); /* wait up to three seconds */ + State = save_State; + + msg_scroll = FALSE; + emsg_on_display = FALSE; + } + + /* + * Finish up after executing a Normal mode command. + */ +normal_end: + + msg_nowait = FALSE; + + /* Reset finish_op, in case it was set */ + c = finish_op; + finish_op = FALSE; + /* Redraw the cursor with another shape, if we were in Operator-pending + * mode or did a replace command. */ + if (c || ca.cmdchar == 'r') { + ui_cursor_shape(); /* may show different cursor shape */ + } + + if (oap->op_type == OP_NOP && oap->regname == 0 + && ca.cmdchar != K_CURSORHOLD + ) + clear_showcmd(); + + checkpcmark(); /* check if we moved since setting pcmark */ + free(ca.searchbuf); + + if (has_mbyte) + mb_adjust_cursor(); + + if (curwin->w_p_scb && toplevel) { + validate_cursor(); /* may need to update w_leftcol */ + do_check_scrollbind(TRUE); + } + + if (curwin->w_p_crb && toplevel) { + validate_cursor(); /* may need to update w_leftcol */ + do_check_cursorbind(); + } + + /* + * May restart edit(), if we got here with CTRL-O in Insert mode (but not + * if still inside a mapping that started in Visual mode). + * May switch from Visual to Select mode after CTRL-O command. + */ + if ( oap->op_type == OP_NOP + && ((restart_edit != 0 && !VIsual_active && old_mapped_len == 0) + || restart_VIsual_select == 1) + && !(ca.retval & CA_COMMAND_BUSY) + && stuff_empty() + && oap->regname == 0) { + if (restart_VIsual_select == 1) { + VIsual_select = TRUE; + showmode(); + restart_VIsual_select = 0; + } + if (restart_edit != 0 + && !VIsual_active && old_mapped_len == 0 + ) + (void)edit(restart_edit, FALSE, 1L); + } + + if (restart_VIsual_select == 2) + restart_VIsual_select = 1; + + /* Save count before an operator for next time. */ + opcount = ca.opcount; +} + +/* + * Set v:count and v:count1 according to "cap". + * Set v:prevcount only when "set_prevcount" is TRUE. + */ +static void set_vcount_ca(cmdarg_T *cap, int *set_prevcount) +{ + long count = cap->count0; + + /* multiply with cap->opcount the same way as above */ + if (cap->opcount != 0) + count = cap->opcount * (count == 0 ? 1 : count); + set_vcount(count, count == 0 ? 1 : count, *set_prevcount); + *set_prevcount = FALSE; /* only set v:prevcount once */ +} + +/* + * Handle an operator after visual mode or when the movement is finished + */ +void do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank) +{ + oparg_T *oap = cap->oap; + pos_T old_cursor; + int empty_region_error; + int restart_edit_save; + + /* The visual area is remembered for redo */ + static int redo_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */ + static linenr_T redo_VIsual_line_count; /* number of lines */ + static colnr_T redo_VIsual_vcol; /* number of cols or end column */ + static long redo_VIsual_count; /* count for Visual operator */ + int include_line_break = FALSE; + + old_cursor = curwin->w_cursor; + + /* + * If an operation is pending, handle it... + */ + if ((finish_op + || VIsual_active + ) && oap->op_type != OP_NOP) { + oap->is_VIsual = VIsual_active; + if (oap->motion_force == 'V') + oap->motion_type = MLINE; + else if (oap->motion_force == 'v') { + /* If the motion was linewise, "inclusive" will not have been set. + * Use "exclusive" to be consistent. Makes "dvj" work nice. */ + if (oap->motion_type == MLINE) + oap->inclusive = FALSE; + /* If the motion already was characterwise, toggle "inclusive" */ + else if (oap->motion_type == MCHAR) + oap->inclusive = !oap->inclusive; + oap->motion_type = MCHAR; + } else if (oap->motion_force == Ctrl_V) { + /* Change line- or characterwise motion into Visual block mode. */ + VIsual_active = TRUE; + VIsual = oap->start; + VIsual_mode = Ctrl_V; + VIsual_select = FALSE; + VIsual_reselect = FALSE; + } + + /* Only redo yank when 'y' flag is in 'cpoptions'. */ + /* Never redo "zf" (define fold). */ + if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK) + && ((!VIsual_active || oap->motion_force) + /* Also redo Operator-pending Visual mode mappings */ + || (VIsual_active && cap->cmdchar == ':' + && oap->op_type != OP_COLON)) + && cap->cmdchar != 'D' + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC + ) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + if (cap->cmdchar == '/' || cap->cmdchar == '?') { /* was a search */ + /* + * If 'cpoptions' does not contain 'r', insert the search + * pattern to really repeat the same command. + */ + if (vim_strchr(p_cpo, CPO_REDO) == NULL) + AppendToRedobuffLit(cap->searchbuf, -1); + AppendToRedobuff(NL_STR); + } else if (cap->cmdchar == ':') { + /* do_cmdline() has stored the first typed line in + * "repeat_cmdline". When several lines are typed repeating + * won't be possible. */ + if (repeat_cmdline == NULL) + ResetRedobuff(); + else { + AppendToRedobuffLit(repeat_cmdline, -1); + AppendToRedobuff(NL_STR); + free(repeat_cmdline); + repeat_cmdline = NULL; + } + } + } + + if (redo_VIsual_busy) { + /* Redo of an operation on a Visual area. Use the same size from + * redo_VIsual_line_count and redo_VIsual_vcol. */ + oap->start = curwin->w_cursor; + curwin->w_cursor.lnum += redo_VIsual_line_count - 1; + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + VIsual_mode = redo_VIsual_mode; + if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { + if (VIsual_mode == 'v') { + if (redo_VIsual_line_count <= 1) { + validate_virtcol(); + curwin->w_curswant = + curwin->w_virtcol + redo_VIsual_vcol - 1; + } else + curwin->w_curswant = redo_VIsual_vcol; + } else { + curwin->w_curswant = MAXCOL; + } + coladvance(curwin->w_curswant); + } + cap->count0 = redo_VIsual_count; + if (redo_VIsual_count != 0) + cap->count1 = redo_VIsual_count; + else + cap->count1 = 1; + } else if (VIsual_active) { + if (!gui_yank) { + /* Save the current VIsual area for '< and '> marks, and "gv" */ + curbuf->b_visual.vi_start = VIsual; + curbuf->b_visual.vi_end = curwin->w_cursor; + curbuf->b_visual.vi_mode = VIsual_mode; + if (VIsual_mode_orig != NUL) { + curbuf->b_visual.vi_mode = VIsual_mode_orig; + VIsual_mode_orig = NUL; + } + curbuf->b_visual.vi_curswant = curwin->w_curswant; + curbuf->b_visual_mode_eval = VIsual_mode; + } + + /* In Select mode, a linewise selection is operated upon like a + * characterwise selection. */ + if (VIsual_select && VIsual_mode == 'V') { + if (lt(VIsual, curwin->w_cursor)) { + VIsual.col = 0; + curwin->w_cursor.col = + (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); + } else { + curwin->w_cursor.col = 0; + VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); + } + VIsual_mode = 'v'; + } + /* If 'selection' is "exclusive", backup one character for + * charwise selections. */ + else if (VIsual_mode == 'v') { + include_line_break = + unadjust_for_sel(); + } + + oap->start = VIsual; + if (VIsual_mode == 'V') + oap->start.col = 0; + } + + /* + * Set oap->start to the first position of the operated text, oap->end + * to the end of the operated text. w_cursor is equal to oap->start. + */ + if (lt(oap->start, curwin->w_cursor)) { + /* Include folded lines completely. */ + if (!VIsual_active) { + if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) + oap->start.col = 0; + if (hasFolding(curwin->w_cursor.lnum, NULL, + &curwin->w_cursor.lnum)) + curwin->w_cursor.col = (colnr_T)STRLEN(ml_get_curline()); + } + oap->end = curwin->w_cursor; + curwin->w_cursor = oap->start; + + /* w_virtcol may have been updated; if the cursor goes back to its + * previous position w_virtcol becomes invalid and isn't updated + * automatically. */ + curwin->w_valid &= ~VALID_VIRTCOL; + } else { + /* Include folded lines completely. */ + if (!VIsual_active && oap->motion_type == MLINE) { + if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + NULL)) + curwin->w_cursor.col = 0; + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) + oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } + oap->end = oap->start; + oap->start = curwin->w_cursor; + } + + oap->line_count = oap->end.lnum - oap->start.lnum + 1; + + /* Set "virtual_op" before resetting VIsual_active. */ + virtual_op = virtual_active(); + + if (VIsual_active || redo_VIsual_busy) { + if (VIsual_mode == Ctrl_V) { /* block mode */ + colnr_T start, end; + + oap->block_mode = TRUE; + + getvvcol(curwin, &(oap->start), + &oap->start_vcol, NULL, &oap->end_vcol); + if (!redo_VIsual_busy) { + getvvcol(curwin, &(oap->end), &start, NULL, &end); + + if (start < oap->start_vcol) + oap->start_vcol = start; + if (end > oap->end_vcol) { + if (*p_sel == 'e' && start >= 1 + && start - 1 >= oap->end_vcol) + oap->end_vcol = start - 1; + else + oap->end_vcol = end; + } + } + + /* if '$' was used, get oap->end_vcol from longest line */ + if (curwin->w_curswant == MAXCOL) { + curwin->w_cursor.col = MAXCOL; + oap->end_vcol = 0; + for (curwin->w_cursor.lnum = oap->start.lnum; + curwin->w_cursor.lnum <= oap->end.lnum; + ++curwin->w_cursor.lnum) { + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); + if (end > oap->end_vcol) + oap->end_vcol = end; + } + } else if (redo_VIsual_busy) + oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; + /* + * Correct oap->end.col and oap->start.col to be the + * upper-left and lower-right corner of the block area. + * + * (Actually, this does convert column positions into character + * positions) + */ + curwin->w_cursor.lnum = oap->end.lnum; + coladvance(oap->end_vcol); + oap->end = curwin->w_cursor; + + curwin->w_cursor = oap->start; + coladvance(oap->start_vcol); + oap->start = curwin->w_cursor; + } + + if (!redo_VIsual_busy && !gui_yank) { + /* + * Prepare to reselect and redo Visual: this is based on the + * size of the Visual text + */ + resel_VIsual_mode = VIsual_mode; + if (curwin->w_curswant == MAXCOL) + resel_VIsual_vcol = MAXCOL; + else { + if (VIsual_mode != Ctrl_V) + getvvcol(curwin, &(oap->end), + NULL, NULL, &oap->end_vcol); + if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { + if (VIsual_mode != Ctrl_V) + getvvcol(curwin, &(oap->start), + &oap->start_vcol, NULL, NULL); + resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; + } else + resel_VIsual_vcol = oap->end_vcol; + } + resel_VIsual_line_count = oap->line_count; + } + + /* can't redo yank (unless 'y' is in 'cpoptions') and ":" */ + if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK) + && oap->op_type != OP_COLON + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC + && oap->motion_force == NUL + ) { + /* Prepare for redoing. Only use the nchar field for "r", + * otherwise it might be the second char of the operator. */ + if (cap->cmdchar == 'g' && (cap->nchar == 'n' + || cap->nchar == 'N')) + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + else if (cap->cmdchar != ':') + prep_redo(oap->regname, 0L, NUL, 'v', + get_op_char(oap->op_type), + get_extra_op_char(oap->op_type), + oap->op_type == OP_REPLACE + ? cap->nchar : NUL); + if (!redo_VIsual_busy) { + redo_VIsual_mode = resel_VIsual_mode; + redo_VIsual_vcol = resel_VIsual_vcol; + redo_VIsual_line_count = resel_VIsual_line_count; + redo_VIsual_count = cap->count0; + } + } + + /* + * oap->inclusive defaults to TRUE. + * If oap->end is on a NUL (empty line) oap->inclusive becomes + * FALSE. This makes "d}P" and "v}dP" work the same. + */ + if (oap->motion_force == NUL || oap->motion_type == MLINE) + oap->inclusive = TRUE; + if (VIsual_mode == 'V') + oap->motion_type = MLINE; + else { + oap->motion_type = MCHAR; + if (VIsual_mode != Ctrl_V && *ml_get_pos(&(oap->end)) == NUL + && (include_line_break || !virtual_op) + ) { + oap->inclusive = FALSE; + /* Try to include the newline, unless it's an operator + * that works on lines only. */ + if (*p_sel != 'o' && !op_on_lines(oap->op_type)) { + if (oap->end.lnum < curbuf->b_ml.ml_line_count) { + ++oap->end.lnum; + oap->end.col = 0; + oap->end.coladd = 0; + ++oap->line_count; + } else { + /* Cannot move below the last line, make the op + * inclusive to tell the operation to include the + * line break. */ + oap->inclusive = TRUE; + } + } + } + } + + redo_VIsual_busy = FALSE; + + /* + * Switch Visual off now, so screen updating does + * not show inverted text when the screen is redrawn. + * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is + * no screen redraw, so it is done here to remove the inverted + * part. + */ + if (!gui_yank) { + VIsual_active = FALSE; + setmouse(); + mouse_dragging = 0; + if (mode_displayed) + clear_cmdline = TRUE; /* unshow visual mode later */ + else + clear_showcmd(); + if ((oap->op_type == OP_YANK + || oap->op_type == OP_COLON + || oap->op_type == OP_FUNCTION + || oap->op_type == OP_FILTER) + && oap->motion_force == NUL) + redraw_curbuf_later(INVERTED); + } + } + + /* Include the trailing byte of a multi-byte char. */ + if (has_mbyte && oap->inclusive) { + int l; + + l = (*mb_ptr2len)(ml_get_pos(&oap->end)); + if (l > 1) + oap->end.col += l - 1; + } + curwin->w_set_curswant = TRUE; + + /* + * oap->empty is set when start and end are the same. The inclusive + * flag affects this too, unless yanking and the end is on a NUL. + */ + oap->empty = (oap->motion_type == MCHAR + && (!oap->inclusive + || (oap->op_type == OP_YANK + && gchar_pos(&oap->end) == NUL)) + && equalpos(oap->start, oap->end) + && !(virtual_op && oap->start.coladd != oap->end.coladd) + ); + /* + * For delete, change and yank, it's an error to operate on an + * empty region, when 'E' included in 'cpoptions' (Vi compatible). + */ + empty_region_error = (oap->empty + && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); + + /* Force a redraw when operating on an empty Visual region, when + * 'modifiable is off or creating a fold. */ + if (oap->is_VIsual && (oap->empty || !curbuf->b_p_ma + || oap->op_type == OP_FOLD + )) + redraw_curbuf_later(INVERTED); + + /* + * If the end of an operator is in column one while oap->motion_type + * is MCHAR and oap->inclusive is FALSE, we put op_end after the last + * character in the previous line. If op_start is on or before the + * first non-blank in the line, the operator becomes linewise + * (strange, but that's the way vi does it). + */ + if ( oap->motion_type == MCHAR + && oap->inclusive == FALSE + && !(cap->retval & CA_NO_ADJ_OP_END) + && oap->end.col == 0 + && (!oap->is_VIsual || *p_sel == 'o') + && !oap->block_mode + && oap->line_count > 1) { + oap->end_adjusted = TRUE; /* remember that we did this */ + --oap->line_count; + --oap->end.lnum; + if (inindent(0)) + oap->motion_type = MLINE; + else { + oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col) { + --oap->end.col; + oap->inclusive = TRUE; + } + } + } else + oap->end_adjusted = FALSE; + + switch (oap->op_type) { + case OP_LSHIFT: + case OP_RSHIFT: + op_shift(oap, TRUE, + oap->is_VIsual ? (int)cap->count1 : + 1); + auto_format(FALSE, TRUE); + break; + + case OP_JOIN_NS: + case OP_JOIN: + if (oap->line_count < 2) + oap->line_count = 2; + if (curwin->w_cursor.lnum + oap->line_count - 1 > + curbuf->b_ml.ml_line_count) + beep_flush(); + else { + (void)do_join(oap->line_count, oap->op_type == OP_JOIN, TRUE, TRUE); + auto_format(FALSE, TRUE); + } + break; + + case OP_DELETE: + VIsual_reselect = FALSE; /* don't reselect now */ + if (empty_region_error) { + vim_beep(); + CancelRedo(); + } else { + (void)op_delete(oap); + if (oap->motion_type == MLINE && has_format_option(FO_AUTO)) + u_save_cursor(); /* cursor line wasn't saved yet */ + auto_format(FALSE, TRUE); + } + break; + + case OP_YANK: + if (empty_region_error) { + if (!gui_yank) { + vim_beep(); + CancelRedo(); + } + } else + (void)op_yank(oap, FALSE, !gui_yank); + check_cursor_col(); + break; + + case OP_CHANGE: + VIsual_reselect = FALSE; /* don't reselect now */ + if (empty_region_error) { + vim_beep(); + CancelRedo(); + } else { + /* This is a new edit command, not a restart. Need to + * remember it to make 'insertmode' work with mappings for + * Visual mode. But do this only once and not when typed and + * 'insertmode' isn't set. */ + if (p_im || !KeyTyped) + restart_edit_save = restart_edit; + else + restart_edit_save = 0; + restart_edit = 0; + /* Reset finish_op now, don't want it set inside edit(). */ + finish_op = FALSE; + if (op_change(oap)) /* will call edit() */ + cap->retval |= CA_COMMAND_BUSY; + if (restart_edit == 0) + restart_edit = restart_edit_save; + } + break; + + case OP_FILTER: + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) + AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */ + else + bangredo = TRUE; /* do_bang() will put cmd in redo buffer */ + + case OP_INDENT: + case OP_COLON: + + /* + * If 'equalprg' is empty, do the indenting internally. + */ + if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { + if (curbuf->b_p_lisp) { + op_reindent(oap, get_lisp_indent); + break; + } + op_reindent(oap, + *curbuf->b_p_inde != NUL ? get_expr_indent : + get_c_indent); + break; + } + + op_colon(oap); + break; + + case OP_TILDE: + case OP_UPPER: + case OP_LOWER: + case OP_ROT13: + if (empty_region_error) { + vim_beep(); + CancelRedo(); + } else + op_tilde(oap); + check_cursor_col(); + break; + + case OP_FORMAT: + if (*curbuf->b_p_fex != NUL) + op_formatexpr(oap); /* use expression */ + else if (*p_fp != NUL) + op_colon(oap); /* use external command */ + else + op_format(oap, FALSE); /* use internal function */ + break; + + case OP_FORMAT2: + op_format(oap, TRUE); /* use internal function */ + break; + + case OP_FUNCTION: + op_function(oap); /* call 'operatorfunc' */ + break; + + case OP_INSERT: + case OP_APPEND: + VIsual_reselect = FALSE; /* don't reselect now */ + if (empty_region_error) { + vim_beep(); + CancelRedo(); + } else { + /* This is a new edit command, not a restart. Need to + * remember it to make 'insertmode' work with mappings for + * Visual mode. But do this only once. */ + restart_edit_save = restart_edit; + restart_edit = 0; + + op_insert(oap, cap->count1); + + /* TODO: when inserting in several lines, should format all + * the lines. */ + auto_format(FALSE, TRUE); + + if (restart_edit == 0) + restart_edit = restart_edit_save; + } + break; + + case OP_REPLACE: + VIsual_reselect = FALSE; /* don't reselect now */ + if (empty_region_error) { + vim_beep(); + CancelRedo(); + } else + op_replace(oap, cap->nchar); +