⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 normal.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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 + -