📄 ex_getln.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.
*/
/*
* ex_getln.c: Functions for entering and editing an Ex command line.
*/
#include "vim.h"
/*
* Variables shared between getcmdline(), redrawcmdline() and others.
* These need to be saved when using CTRL-R |, that's why they are in a
* structure.
*/
struct cmdline_info
{
char_u *cmdbuff; /* pointer to command line buffer */
int cmdbufflen; /* length of cmdbuff */
int cmdlen; /* number of chars on command line */
int cmdpos; /* current cursor position */
int cmdspos; /* cursor column on screen */
int cmdfirstc; /* ':', '/', '?', '=' or NUL */
int cmdindent; /* number of spaces before cmdline */
char_u *cmdprompt; /* message in front of cmdline */
int cmdattr; /* attributes for prompt */
int overstrike; /* Typing mode on the command line. Shared by
getcmdline() and put_on_cmdline(). */
};
static struct cmdline_info ccline; /* current cmdline_info */
static int cmd_numfiles = -1; /* number of files found by
file name completion */
static char_u **(history[HIST_COUNT]) = {NULL, NULL, NULL, NULL};
static int hisidx[HIST_COUNT] = {-1, -1, -1, -1}; /* last entered entry */
static int hislen = 0; /* actual length of history tables */
#ifdef RIGHTLEFT
static int cmd_hkmap = 0; /* Hebrew mapping during command line */
#endif
#ifdef FKMAP
static int cmd_fkmap = 0; /* Farsi mapping during command line */
#endif
static int hist_char2type __ARGS((int c));
static void init_history __ARGS((void));
static int in_history __ARGS((int, char_u *, int));
static void set_cmdspos __ARGS((void));
static void alloc_cmdbuff __ARGS((int len));
static int realloc_cmdbuff __ARGS((int len));
static void putcmdline __ARGS((int));
static void redrawcmdprompt __ARGS((void));
static void redrawcmd __ARGS((void));
static void cursorcmd __ARGS((void));
static int ccheck_abbr __ARGS((int));
static int nextwild __ARGS((int, int));
static int showmatches __ARGS((void));
static void set_expand_context __ARGS((void));
static int ExpandFromContext __ARGS((char_u *, int *, char_u ***, int, int));
/*
* getcmdline() - accept a command line starting with firstc.
*
* firstc == ':' get ":" command line.
* firstc == '/' or '?' get search pattern
* firstc == '=' get expression
* firstc == '@' get text for input() function
* firstc == NUL get text for :insert command
*
* The line is collected in ccline.cmdbuff, which is reallocated to fit the
* command line.
*
* Careful: getcmdline() can be called recursively!
*
* Return pointer to allocated string if there is a commandline, NULL
* otherwise.
*/
char_u *
getcmdline(firstc, count, indent)
int firstc;
long count; /* only used for incremental search */
int indent; /* indent for inside conditionals */
{
int c;
#ifdef DIGRAPHS
int cc;
#endif
int i;
int j;
char_u *p;
int hiscnt; /* current history line in use */
char_u *lookfor = NULL; /* string to match */
int gotesc = FALSE; /* TRUE when <ESC> just typed */
int do_abbr; /* when TRUE check for abbr. */
int histype; /* history type to be used */
#ifdef EXTRA_SEARCH
FPOS old_cursor;
colnr_t old_curswant;
colnr_t old_leftcol;
linenr_t old_topline;
linenr_t old_botline;
int did_incsearch = FALSE;
int incsearch_postponed = FALSE;
#endif
int did_wild_list = FALSE; /* did wild_list() recently */
int wim_index = 0; /* index in wim_flags[] */
int res;
int save_msg_scroll = msg_scroll;
int save_State = State; /* remember State when called */
int some_key_typed = FALSE; /* one of the keys was typed */
#ifdef USE_MOUSE
/* mouse drag and release events are ignored, unless they are
* preceded with a mouse down event */
int ignore_drag_release = TRUE;
#endif
#ifdef USE_SNIFF
want_sniff_request = 0;
#endif
ccline.overstrike = FALSE; /* always start in insert mode */
#ifdef EXTRA_SEARCH
old_cursor = curwin->w_cursor; /* needs to be restored later */
old_curswant = curwin->w_curswant;
old_leftcol = curwin->w_leftcol;
old_topline = curwin->w_topline;
old_botline = curwin->w_botline;
#endif
/*
* set some variables for redrawcmd()
*/
ccline.cmdfirstc = (firstc == '@' ? 0 : firstc);
ccline.cmdindent = indent;
alloc_cmdbuff(exmode_active ? 250 : 0); /* alloc initial ccline.cmdbuff */
if (ccline.cmdbuff == NULL)
return NULL; /* out of memory */
ccline.cmdlen = ccline.cmdpos = 0;
redir_off = TRUE; /* don't redirect the typed command */
i = msg_scrolled;
msg_scrolled = 0; /* avoid wait_return message */
gotocmdline(TRUE);
msg_scrolled += i;
redrawcmdprompt(); /* draw prompt or indent */
set_cmdspos();
/*
* Avoid scrolling when called by a recursive do_cmdline(), e.g. when doing
* ":@0" when register 0 doesn't contain a CR.
*/
msg_scroll = FALSE;
State = CMDLINE;
#ifdef USE_MOUSE
setmouse();
#endif
init_history();
hiscnt = hislen; /* set hiscnt to impossible history value */
histype = hist_char2type(firstc);
#ifdef DIGRAPHS
do_digraph(-1); /* init digraph typahead */
#endif
/* collect the command string, handling editing keys */
for (;;)
{
#ifdef USE_GUI_WIN32
dont_scroll = FALSE; /* allow scrolling here */
#endif
quit_more = FALSE; /* reset after CTRL-D which had a more-prompt */
cursorcmd(); /* set the cursor on the right spot */
c = vgetc();
if (KeyTyped)
{
some_key_typed = TRUE;
#ifdef RIGHTLEFT
if (cmd_hkmap)
c = hkmap(c);
# ifdef FKMAP
if (cmd_fkmap)
c = cmdl_fkmap(c);
# endif
#endif
}
/*
* Ignore got_int when CTRL-C was typed here.
* Don't ignore it in :global, we really need to break then, e.g., for
* ":g/pat/normal /pat" (without the <CR>).
* Don't ignore it for the input() function.
*/
if ((c == Ctrl('C')
#ifdef UNIX
|| c == intr_char
#endif
)
#ifdef WANT_EVAL
&& firstc != '@'
#endif
&& !global_busy)
got_int = FALSE;
/* free old command line when finished moving around in the history
* list */
if (lookfor
&& c != K_S_DOWN && c != K_S_UP && c != K_DOWN && c != K_UP
&& c != K_PAGEDOWN && c != K_PAGEUP
&& c != K_KPAGEDOWN && c != K_KPAGEUP
&& c != K_LEFT && c != K_RIGHT
&& (cmd_numfiles > 0 || (c != Ctrl('P') && c != Ctrl('N'))))
{
vim_free(lookfor);
lookfor = NULL;
}
/*
* <S-Tab> works like CTRL-P (unless 'wc' is <S-Tab>).
*/
if (c != p_wc && c == K_S_TAB && cmd_numfiles != -1)
c = Ctrl('P');
/* free expanded names when finished walking through matches */
if (cmd_numfiles != -1 && !(c == p_wc && KeyTyped)
&& c != Ctrl('N') && c != Ctrl('P') && c != Ctrl('A')
&& c != Ctrl('L'))
{
(void)ExpandOne(NULL, NULL, 0, WILD_FREE);
did_wild_list = FALSE;
wim_index = 0;
}
#ifdef DIGRAPHS
c = do_digraph(c);
#endif
if (c == '\n' || c == '\r' || c == K_KENTER || (c == ESC
&& (!KeyTyped || vim_strchr(p_cpo, CPO_ESC) != NULL)))
{
gotesc = FALSE; /* Might have typed ESC previously, don't
truncate the cmdline now. */
if (ccheck_abbr(c + ABBR_OFF))
goto cmdline_changed;
windgoto(msg_row, 0);
out_flush();
break;
}
/*
* Completion for 'wildchar' key.
* - hitting <ESC> twice means: abandon command line.
* - wildcard expansion is only done when the key is really typed, not
* when it comes from a macro
*/
if (c == p_wc && !gotesc && KeyTyped)
{
if (cmd_numfiles > 0) /* typed p_wc at least twice */
{
/* if 'wildmode' contains "list", may still need to list */
if (cmd_numfiles > 1
&& !did_wild_list
&& (wim_flags[wim_index] & WIM_LIST))
{
showmatches();
redrawcmd();
did_wild_list = TRUE;
}
if (wim_flags[wim_index] & WIM_LONGEST)
res = nextwild(WILD_LONGEST, WILD_NO_BEEP);
else if (wim_flags[wim_index] & WIM_FULL)
res = nextwild(WILD_NEXT, WILD_NO_BEEP);
else
res = OK; /* don't insert 'wildchar' now */
}
else /* typed p_wc first time */
{
j = ccline.cmdpos;
/* if 'wildmode' first contains "longest", get longest
* common part */
if (wim_flags[0] & WIM_LONGEST)
res = nextwild(WILD_LONGEST, WILD_NO_BEEP);
else
res = nextwild(WILD_EXPAND_KEEP, WILD_NO_BEEP);
/* when more than one match, and 'wildmode' first contains
* "list", or no change and 'wildmode' contains "longest,list",
* list all matches */
if (res == OK && cmd_numfiles > 1)
{
/* a "longest" that didn't do anything is skipped (but not
* "list:longest") */
if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j)
wim_index = 1;
if (wim_flags[wim_index] & WIM_LIST)
{
if (!(wim_flags[0] & WIM_LONGEST))
nextwild(WILD_PREV, 0); /* remove match */
showmatches();
redrawcmd();
did_wild_list = TRUE;
if (wim_flags[wim_index] & WIM_LONGEST)
nextwild(WILD_LONGEST, WILD_NO_BEEP);
else if (wim_flags[wim_index] & WIM_FULL)
nextwild(WILD_NEXT, WILD_NO_BEEP);
}
else
vim_beep();
}
}
if (wim_index < 3)
++wim_index;
if (c == ESC)
gotesc = TRUE;
if (res == OK)
goto cmdline_changed;
}
gotesc = FALSE;
/* <S-Tab> goes to last match, in a clumsy way */
if (c == K_S_TAB && KeyTyped)
{
if (nextwild(WILD_EXPAND_KEEP, 0) == OK
&& nextwild(WILD_PREV, 0) == OK
&& nextwild(WILD_PREV, 0) == OK)
goto cmdline_changed;
}
if (c == NUL || c == K_ZERO) /* NUL is stored as NL */
c = NL;
do_abbr = TRUE; /* default: check for abbreviation */
switch (c)
{
case K_BS:
case Ctrl('H'):
case K_DEL:
case Ctrl('W'):
#ifdef FKMAP
if (cmd_fkmap && c == K_BS)
c = K_DEL;
#endif
/*
* delete current character is the same as backspace on next
* character, except at end of line
*/
if (c == K_DEL && ccline.cmdpos != ccline.cmdlen)
++ccline.cmdpos;
if (ccline.cmdpos > 0)
{
j = ccline.cmdpos;
if (c == Ctrl('W'))
{
while (ccline.cmdpos &&
vim_isspace(ccline.cmdbuff[ccline.cmdpos - 1]))
--ccline.cmdpos;
i = vim_iswordc(ccline.cmdbuff[ccline.cmdpos - 1]);
while (ccline.cmdpos && !vim_isspace(
ccline.cmdbuff[ccline.cmdpos - 1]) &&
vim_iswordc(
ccline.cmdbuff[ccline.cmdpos - 1]) == i)
--ccline.cmdpos;
}
else
--ccline.cmdpos;
ccline.cmdlen -= j - ccline.cmdpos;
i = ccline.cmdpos;
while (i < ccline.cmdlen)
ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
redrawcmd();
}
else if (ccline.cmdlen == 0 && c != Ctrl('W')
&& ccline.cmdprompt == NULL && indent == 0)
{
vim_free(ccline.cmdbuff); /* no commandline to return */
ccline.cmdbuff = NULL;
msg_col = 0;
msg_putchar(' '); /* delete ':' */
redraw_cmdline = TRUE;
goto returncmd; /* back to cmd mode */
}
goto cmdline_changed;
case K_INS:
#ifdef FKMAP
/* if Farsi mode set, we are in reverse insert mode -
Do not change the mode */
if (cmd_fkmap)
beep_flush();
else
#endif
ccline.overstrike = !ccline.overstrike;
#ifdef CURSOR_SHAPE
ui_cursor_shape(); /* may show different cursor shape */
#endif
goto cmdline_not_changed;
/* case '@': only in very old vi */
case Ctrl('U'):
ccline.cmdpos = 0;
ccline.cmdlen = 0;
set_cmdspos();
redrawcmd();
goto cmdline_changed;
case ESC: /* get here if p_wc != ESC or when ESC typed twice */
case Ctrl('C'):
gotesc = TRUE; /* will free ccline.cmdbuff after putting
it in history */
goto returncmd; /* back to cmd mode */
case Ctrl('R'): /* insert register */
#ifdef USE_GUI_WIN32
dont_scroll = TRUE; /* disallow scrolling here */
#endif
putcmdline('"');
++no_mapping;
c = vgetc();
--no_mapping;
#ifdef WANT_EVAL
/*
* Insert the result of an expression.
* Need to save the current command line, to be able to enter
* a new one...
*/
if (c == '=')
{
struct cmdline_info save_ccline;
if (ccline.cmdfirstc == '=')/* can't do this recursively */
{
beep_flush();
c = ESC;
}
else
{
save_ccline = ccline;
c = get_expr_register();
ccline = save_ccline;
}
}
#endif
if (c != ESC) /* use ESC to cancel inserting register */
cmdline_paste(c);
redrawcmd();
goto cmdline_changed;
case Ctrl('D'):
if (showmatches() == FAIL)
break; /* Use ^D as normal char instead */
redrawcmd();
continue; /* don't do incremental search now */
case K_RIGHT:
case K_S_RIGHT:
do
{
if (ccline.cmdpos >= ccline.cmdlen)
break;
ccline.cmdspos += charsize(ccline.cmdbuff[ccline.cmdpos]);
++ccline.cmdpos;
}
while ((c == K_S_RIGHT || (mod_mask & MOD_MASK_CTRL))
&& ccline.cmdbuff[ccline.cmdpos] != ' ');
goto cmdline_not_changed;
case K_LEFT:
case K_S_LEFT:
do
{
if (ccline.cmdpos <= 0)
break;
--ccline.cmdpos;
ccline.cmdspos -= charsize(ccline.cmdbuff[ccline.cmdpos]);
}
while ((c == K_S_LEFT || (mod_mask & MOD_MASK_CTRL))
&& ccline.cmdbuff[ccline.cmdpos - 1] != ' ');
goto cmdline_not_changed;
#if defined(USE_MOUSE) || defined(FKMAP)
case K_IGNORE:
#endif
#ifdef USE_MOUSE
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
goto cmdline_not_changed; /* Ignore mouse */
case K_MIDDLEMOUSE:
# ifdef USE_GUI
/* When GUI is active, also paste when 'mouse' is empty */
if (!gui.in_use)
# endif
if (!mouse_has(MOUSE_COMMAND))
goto cmdline_not_changed; /* Ignore mouse */
# ifdef USE_GUI
if (gui.in_use)
cmdline_paste('*');
else
# endif
cmdline_paste(0);
redrawcmd();
goto cmdline_changed;
case K_LEFTDRAG:
case K_LEFTRELEASE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
if (ignore_drag_release)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -