📄 normal.c
字号:
/* 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.
*/
/*
* 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"
/*
* 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 */
/*
* Operator characters; The order must correspond to the defines in vim.h!
*/
static char_u *op_chars = (char_u *)"dyc<>!~=q:UuJ";
static void op_colon __ARGS((OPARG *oap));
#ifdef USE_MOUSE
static void find_start_of_word __ARGS((FPOS *));
static void find_end_of_word __ARGS((FPOS *));
static int get_mouse_class __ARGS((int));
#endif
static void prep_redo_cmd __ARGS((CMDARG *cap));
static void prep_redo __ARGS((int regname, long, int, int, int, int));
static int checkclearop __ARGS((OPARG *oap));
static int checkclearopq __ARGS((OPARG *oap));
static void clearop __ARGS((OPARG *oap));
static void clearopbeep __ARGS((OPARG *oap));
#ifdef SHOWCMD
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_gd __ARGS((OPARG *oap, int nchar));
static int nv_screengo __ARGS((OPARG *oap, int dir, long dist));
static void nv_scroll_line __ARGS((CMDARG *cap, int is_ctrl_e));
static void nv_zet __ARGS((CMDARG *cap));
static void nv_colon __ARGS((CMDARG *cap));
static void nv_ctrlg __ARGS((CMDARG *cap));
static void nv_zzet __ARGS((CMDARG *cap));
static void nv_ident __ARGS((CMDARG *cap, char_u **searchp));
static void nv_scroll __ARGS((CMDARG *cap));
static void nv_right __ARGS((CMDARG *cap));
static int nv_left __ARGS((CMDARG *cap));
#ifdef FILE_IN_PATH
static void nv_gotofile __ARGS((CMDARG *cap));
#endif
static void nv_search __ARGS((CMDARG *cap, char_u **searchp, int dont_set_mark));
static void nv_next __ARGS((CMDARG *cap, int flag));
static void nv_csearch __ARGS((CMDARG *cap, int dir, int type));
static void nv_brackets __ARGS((CMDARG *cap, int dir));
static void nv_percent __ARGS((CMDARG *cap));
static void nv_brace __ARGS((CMDARG *cap, int dir));
static int n_replace __ARGS((CMDARG *cap));
static void v_swap_corners __ARGS((CMDARG *cap));
static int nv_replace __ARGS((CMDARG *cap));
static void n_swapchar __ARGS((CMDARG *cap));
static void nv_cursormark __ARGS((CMDARG *cap, int flag, FPOS *pos));
static void v_visop __ARGS((CMDARG *cap));
static void nv_optrans __ARGS((CMDARG *cap));
static void nv_gomark __ARGS((CMDARG *cap, int flag));
static void nv_pcmark __ARGS((CMDARG *cap));
static void nv_regname __ARGS((CMDARG *cap, linenr_t *opnump));
static void nv_visual __ARGS((CMDARG *cap, int selectmode));
static void n_start_visual_mode __ARGS((int c));
static int nv_g_cmd __ARGS((CMDARG *cap, char_u **searchp));
static int n_opencmd __ARGS((CMDARG *cap));
static void nv_Undo __ARGS((CMDARG *cap));
static void nv_operator __ARGS((CMDARG *cap));
static void nv_lineop __ARGS((CMDARG *cap));
static void nv_pipe __ARGS((CMDARG *cap));
static void nv_bck_word __ARGS((CMDARG *cap, int type));
static void nv_wordcmd __ARGS((CMDARG *cap, int type));
static void adjust_for_sel __ARGS((CMDARG *cap));
static void nv_goto __ARGS((OPARG *oap, long lnum));
static void nv_select __ARGS((CMDARG *cap));
static void nv_esc __ARGS((CMDARG *oap, linenr_t opnum));
static int nv_append __ARGS((CMDARG *cap));
#ifdef TEXT_OBJECTS
static void nv_object __ARGS((CMDARG *cap));
#endif
static void nv_at __ARGS((CMDARG *cap));
static void nv_halfpage __ARGS((CMDARG *cap));
static void nv_join __ARGS((CMDARG *cap));
static void nv_put __ARGS((CMDARG *cap));
/*
* normal
*
* Execute a command in Normal mode.
*
* This is basically a big switch with the cases arranged in rough categories
* in the following order:
*
* 0. Macros (q, @)
* 1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
* 2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
* 3. Cursor motions (G, H, M, L, l, K_RIGHT, , h, K_LEFT, ^H, k, K_UP,
* ^P, +, CR, LF, j, K_DOWN, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
* 4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
* 5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
* 6. Inserts (A, a, I, i, o, O, R)
* 7. Operators (~, d, c, y, >, <, !, =)
* 8. Abbreviations (x, X, D, C, s, S, Y, &)
* 9. Marks (m, ', `, ^O, ^I)
* 10. Register name setting ('"')
* 11. Visual (v, V, ^V)
* 12. Suspend (^Z)
* 13. Window commands (^W)
* 14. extended commands (starting with 'g')
* 15. mouse click
* 16. scrollbar movement
* 17. The end (ESC)
*/
void
normal_cmd(oap, toplevel)
OPARG *oap;
int toplevel; /* TRUE when called from main() */
{
static linenr_t opnum = 0; /* count before an operator */
CMDARG ca; /* command arguments */
int c;
int flag = FALSE;
int type = 0; /* type of operation */
int dir = FORWARD; /* search direction */
char_u *searchbuff = NULL; /* buffer for search string */
int command_busy = FALSE;
int ctrl_w = FALSE; /* got CTRL-W command */
int old_col = curwin->w_curswant;
int dont_adjust_op_end = FALSE;
FPOS old_pos; /* cursor position before command */
#ifdef SHOWCMD
int need_flushbuf; /* need to call out_flush() */
#endif
static int restart_VIsual_select = 0;
int mapped_len;
static int old_mapped_len = 0;
vim_memset(&ca, 0, sizeof(ca));
ca.oap = oap;
#ifdef USE_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 */
#endif
if (!finish_op && !oap->regname)
opnum = 0;
mapped_len = typebuf_maplen();
State = NORMAL_BUSY;
#ifdef USE_GUI_WIN32
dont_scroll = FALSE; /* allow scrolling here */
#endif
c = vgetc();
#ifdef HAVE_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)
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;
else if (c == K_KMULTIPLY) /* K_KMULTIPLY is same as '*' */
c = '*';
else if (c == K_KMINUS) /* K_KMINUS is same as '-' */
c = '-';
else if (c == K_KPLUS) /* K_KPLUS is same as '+' */
c = '+';
else if (c == K_KDIVIDE) /* K_KDIVIDE is same as '/' */
c = '/';
/*
* In Visual/Select mode, a few keys are handled in a special way.
*/
if (VIsual_active)
{
/* In Select mode, typed text replaces the selection */
if (VIsual_select)
{
if (vim_isprintc(c) || c == NL || c == CR || c == K_KENTER)
{
stuffcharReadbuff(c);
c = 'c';
}
}
/* when 'keymode' contains "stopsel" may stop Select/Visual mode */
if (vim_strchr(p_km, 'o') != NULL)
{
switch (c)
{
case K_UP:
case Ctrl('P'):
case K_DOWN:
case Ctrl('N'):
case K_LEFT:
case K_RIGHT:
case Ctrl('B'):
case K_PAGEUP:
case K_KPAGEUP:
case Ctrl('F'):
case K_PAGEDOWN:
case K_KPAGEDOWN:
case K_HOME:
case K_KHOME:
case K_END:
case K_KEND:
if (!(mod_mask & MOD_MASK_SHIFT))
{
end_visual_mode();
redraw_curbuf_later(NOT_VALID);
}
break;
}
}
/* Keys that work different when 'keymode' contains "startsel" */
if (vim_strchr(p_km, 'a') != NULL)
{
switch (c)
{
case K_S_UP: c = K_UP; break;
case K_S_DOWN: c = K_DOWN; break;
case K_S_LEFT: c = K_LEFT; break;
case K_S_RIGHT: c = K_RIGHT; break;
case K_S_HOME: c = K_HOME; break;
case K_S_END: c = K_END; break;
case K_PAGEUP:
case K_KPAGEUP:
case K_PAGEDOWN:
case K_KPAGEDOWN:
case K_KHOME:
case K_KEND:
mod_mask &= !MOD_MASK_SHIFT; break;
}
}
}
#ifdef SHOWCMD
need_flushbuf = add_to_showcmd(c);
#endif
getcount:
if (!(VIsual_active && VIsual_select))
{
/* Pick up any leading digits and compute ca.count0 */
while ( (c >= '1' && c <= '9')
|| (ca.count0 != 0 && (c == K_DEL || c == '0')))
{
if (c == K_DEL)
{
ca.count0 /= 10;
#ifdef SHOWCMD
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 = 999999999;
if (ctrl_w)
{
++no_mapping;
++allow_keys; /* no mapping for nchar, but keys */
}
c = vgetc();
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
if (ctrl_w)
{
--no_mapping;
--allow_keys;
}
#ifdef SHOWCMD
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;
opnum = ca.count0; /* remember first count */
ca.count0 = 0;
++no_mapping;
++allow_keys; /* no mapping for nchar, but keys */
c = vgetc(); /* get next character */
#ifdef HAVE_LANGMAP
LANGMAP_ADJUST(c, TRUE);
#endif
--no_mapping;
--allow_keys;
#ifdef SHOWCMD
need_flushbuf |= add_to_showcmd(c);
#endif
goto getcount; /* jump back */
}
}
ca.cmdchar = c;
/*
* 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 (opnum != 0)
{
if (ca.count0)
ca.count0 *= opnum;
else
ca.count0 = opnum;
}
/*
* 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.
*/
opnum = ca.count0;
if (toplevel)
global_opnum = opnum;
ca.count1 = (ca.count0 == 0 ? 1 : ca.count0);
/*
* Get an additional character if we need one.
* For CTRL-W we already got it when looking for a count.
*/
if (ctrl_w)
{
ca.nchar = ca.cmdchar;
ca.cmdchar = Ctrl('W');
}
else if ( (oap->op_type == OP_NOP
&& vim_strchr((char_u *)"@zm\"", ca.cmdchar) != NULL)
|| (oap->op_type == OP_NOP
&& !VIsual_active
&& (ca.cmdchar == 'r' || ca.cmdchar == 'Z'))
|| vim_strchr((char_u *)"tTfF[]g'`", ca.cmdchar) != NULL
|| (ca.cmdchar == 'q'
&& oap->op_type == OP_NOP
&& !Recording
&& !Exec_reg)
|| ((ca.cmdchar == 'a' || ca.cmdchar == 'i')
&& (oap->op_type != OP_NOP || VIsual_active)))
{
++no_mapping;
++allow_keys; /* no mapping for nchar, but allow key codes */
#ifdef CURSOR_SHAPE
if (ca.cmdchar == 'r')
{
State = REPLACE; /* pretend Replace mode, for cursor shape */
ui_cursor_shape(); /* show different cursor shape */
}
#endif
ca.nchar = vgetc();
#ifdef CURSOR_SHAPE
State = NORMAL_BUSY;
#endif
#ifdef HAVE_LANGMAP
/* adjust chars > 127, except after tTfFr command */
LANGMAP_ADJUST(ca.nchar,
vim_strchr((char_u *)"tTfFr", ca.cmdchar) == NULL);
#endif
#ifdef RIGHTLEFT
/* adjust Hebrew mapped char */
if (p_hkmap && vim_strchr((char_u *)"tTfFr", ca.cmdchar) && KeyTyped)
ca.nchar = hkmap(ca.nchar);
# ifdef FKMAP
/* adjust Farsi mapped char */
if (p_fkmap && strchr("tTfFr", ca.cmdchar) && KeyTyped)
ca.nchar = fkmap(ca.nchar);
# endif
#endif
--no_mapping;
--allow_keys;
#ifdef SHOWCMD
need_flushbuf |= add_to_showcmd(ca.nchar);
#endif
}
#ifdef SHOWCMD
/*
* 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 (p_im && !restart_edit)
restart_edit = 'a';
goto normal_end;
}
#ifdef RIGHTLEFT
if (curwin->w_p_rl && KeyTyped) /* invert horizontal operations */
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 'h': ca.cmdchar = 'l'; break;
case K_LEFT: ca.cmdchar = K_RIGHT; break;
case K_S_LEFT: ca.cmdchar = K_S_RIGHT; break;
case '>': ca.cmdchar = '<'; break;
case '<': ca.cmdchar = '>'; break;
}
#endif
/* when 'keymode' contains "startsel" some keys start Select/Visual mode */
if (!VIsual_active && vim_strchr(p_km, 'a') != NULL)
{
static int seltab[] =
{
/* key unshifted shift included */
K_S_RIGHT, K_RIGHT, TRUE,
K_S_LEFT, K_LEFT, TRUE,
K_S_UP, K_UP, TRUE,
K_S_DOWN, K_DOWN, TRUE,
K_S_HOME, K_HOME, TRUE,
K_S_END, K_END, TRUE,
K_KHOME, K_KHOME, FALSE,
K_KEND, K_KEND, FALSE,
K_PAGEUP, K_PAGEUP, FALSE,
K_KPAGEUP, K_KPAGEUP, FALSE,
K_PAGEDOWN, K_PAGEDOWN, FALSE,
K_KPAGEDOWN, K_KPAGEDOWN, FALSE,
};
int i;
for (i = 0; i < sizeof(seltab) / sizeof(int); i += 3)
{
if (seltab[i] == ca.cmdchar
&& (seltab[i + 2] || (mod_mask & MOD_MASK_SHIFT)))
{
ca.cmdchar = seltab[i + 1];
start_selection();
break;
}
}
}
msg_didout = FALSE; /* don't scroll screen up for normal command */
msg_col = 0;
old_pos = curwin->w_cursor; /* remember where cursor was */
/*
* Generally speaking, every command below 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.
*/
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -