📄 ex_getln.c
字号:
* char_u *expand_pattern The start of the pattern to be expanded within
* the command line (ends at the cursor).
* int expand_context The type of thing to expand. Will be one of:
*
* EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
* the command line, like an unknown command. Caller
* should beep.
* EXPAND_NOTHING Unrecognised context for completion, use char like
* a normal char, rather than for completion. eg
* :s/^I/
* EXPAND_COMMANDS Cursor is still touching the command, so complete
* it.
* EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
* EXPAND_FILES After command with XFILE set, or after setting
* with P_EXPAND set. eg :e ^I, :w>>^I
* EXPAND_DIRECTORIES In some cases this is used instead of the latter
* when we know only directories are of interest. eg
* :set dir=^I
* EXPAND_SETTINGS Complete variable names. eg :set d^I
* EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
* EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
* EXPAND_HELP Complete tags from the file 'helpfile'/tags
* EXPAND_EVENTS Complete event names
* EXPAND_SYNTAX Complete :syntax command arguments
* EXPAND_HIGHLIGHT Complete highlight (syntax) group names
* EXPAND_AUGROUP Complete autocommand group names
* EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
*
* -- webb.
*/
static void
set_expand_context()
{
char_u *nextcomm;
int old_char = NUL;
if (ccline.cmdfirstc != ':') /* only expansion for ':' commands */
{
expand_context = EXPAND_NOTHING;
return;
}
/*
* Avoid a UMR warning from Purify, only save the character if it has been
* written before.
*/
if (ccline.cmdpos < ccline.cmdlen)
old_char = ccline.cmdbuff[ccline.cmdpos];
ccline.cmdbuff[ccline.cmdpos] = NUL;
nextcomm = ccline.cmdbuff;
while (nextcomm != NULL)
nextcomm = set_one_cmd_context(nextcomm);
ccline.cmdbuff[ccline.cmdpos] = old_char;
}
/*
* Do the expansion based on the global variables expand_context and
* expand_pattern -- webb.
*/
static int
ExpandFromContext(pat, num_file, file, files_only, options)
char_u *pat;
int *num_file;
char_u ***file;
int files_only;
int options;
{
vim_regexp *prog;
int ret;
int flags;
flags = 0;
if (!files_only)
flags |= EW_DIR;
if (options & WILD_LIST_NOTFOUND)
flags |= EW_NOTFOUND;
if (!expand_interactively || expand_context == EXPAND_FILES)
{
/*
* Expand file names.
*/
return expand_wildcards(1, &pat, num_file, file, flags|EW_FILE);
}
else if (expand_context == EXPAND_DIRECTORIES)
{
/*
* Expand directory names.
*/
int free_pat = FALSE;
int i;
/* for ":set path=" we need to remove backslashes for escaped space */
if (expand_set_path)
{
free_pat = TRUE;
pat = vim_strsave(pat);
for (i = 0; pat[i]; ++i)
if (pat[i] == '\\' && pat[i + 1] == '\\' &&
pat[i + 2] == '\\' && pat[i + 3] == ' ')
STRCPY(pat + i, pat + i + 3);
}
ret = expand_wildcards(1, &pat, num_file, file,
(flags | EW_DIR) & ~EW_FILE);
if (free_pat)
vim_free(pat);
return ret;
}
*file = (char_u **)"";
*num_file = 0;
if (expand_context == EXPAND_OLD_SETTING)
return ExpandOldSetting(num_file, file);
if (expand_context == EXPAND_HELP)
return find_help_tags(pat, num_file, file);
set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
if (expand_context == EXPAND_BUFFERS)
return ExpandBufnames(pat, num_file, file, options);
else if (expand_context == EXPAND_TAGS)
{
if (pat[0] == '^' && pat[1] == '/')
return find_tags(pat + 2, num_file, file,
TAG_REGEXP | TAG_NAMES | TAG_VERBOSE, MAXCOL);
return find_tags(pat, num_file, file,
TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NOIC, MAXCOL);
}
prog = vim_regcomp(pat, (int)p_magic);
if (prog == NULL)
return FAIL;
if (expand_context == EXPAND_COMMANDS)
ret = ExpandGeneric(prog, num_file, file, get_command_name);
else if (expand_context == EXPAND_SETTINGS ||
expand_context == EXPAND_BOOL_SETTINGS)
ret = ExpandSettings(prog, num_file, file);
#ifdef WANT_EVAL
else if (expand_context == EXPAND_USER_VARS)
ret = ExpandGeneric(prog, num_file, file, get_user_var_name);
#endif
#ifdef USE_GUI
else if (expand_context == EXPAND_MENUS)
ret = ExpandGeneric(prog, num_file, file, get_menu_name);
#endif
#ifdef SYNTAX_HL
else if (expand_context == EXPAND_SYNTAX)
{
reg_ic = TRUE;
ret = ExpandGeneric(prog, num_file, file, get_syntax_name);
}
#endif
else if (expand_context == EXPAND_HIGHLIGHT)
{
reg_ic = TRUE;
ret = ExpandGeneric(prog, num_file, file, get_highlight_name);
}
#ifdef AUTOCMD
else if (expand_context == EXPAND_EVENTS)
{
reg_ic = TRUE;
ret = ExpandGeneric(prog, num_file, file, get_event_name);
}
else if (expand_context == EXPAND_AUGROUP)
{
reg_ic = TRUE;
ret = ExpandGeneric(prog, num_file, file, get_augroup_name);
}
#endif
else
ret = FAIL;
vim_free(prog);
return ret;
}
/*
* Expand a list of names.
*
* Generic function for command line completion. It calls a function to
* obtain strings, one by one. The strings are matched against a regexp
* program. Matching strings are copied into an array, which is returned.
*
* Returns OK when no problems encountered, FAIL for error (out of memory).
*/
int
ExpandGeneric(prog, num_file, file, func)
vim_regexp *prog;
int *num_file;
char_u ***file;
char_u *((*func)__ARGS((int))); /* returns a string from the list */
{
int i;
int count = 0;
int loop;
char_u *str;
/* do this loop twice:
* loop == 0: count the number of matching names
* loop == 1: copy the matching names into allocated memory
*/
for (loop = 0; loop <= 1; ++loop)
{
for (i = 0; ; ++i)
{
str = (*func)(i);
if (str == NULL) /* end of list */
break;
if (*str == NUL) /* skip empty strings */
continue;
if (vim_regexec(prog, str, TRUE))
{
if (loop)
(*file)[count] = vim_strsave_escaped(str,
(char_u *)" \t\\.");
++count;
}
}
if (loop == 0)
{
if (count == 0)
return OK;
*num_file = count;
*file = (char_u **)alloc((unsigned)(count * sizeof(char_u *)));
if (*file == NULL)
{
*file = (char_u **)"";
return FAIL;
}
count = 0;
}
}
return OK;
}
/*********************************
* Command line history stuff *
*********************************/
/*
* Translate a history character to the associated type number.
*/
static int
hist_char2type(c)
int c;
{
if (c == ':')
return HIST_CMD;
if (c == '=')
return HIST_EXPR;
if (c == '@')
return HIST_INPUT;
return HIST_SEARCH; /* must be '?' or '/' */
}
/*
* init_history() - Initialize the command line history.
* Also used to re-allocate the history when the size changes.
*/
static void
init_history()
{
int newlen; /* new length of history table */
char_u **temp;
int i;
int j;
int type;
/*
* If size of history table changed, reallocate it
*/
newlen = (int)p_hi;
if (newlen != hislen) /* history length changed */
{
for (type = 0; type < HIST_COUNT; ++type) /* adjust the tables */
{
if (newlen)
{
temp = (char_u **)lalloc((long_u)(newlen * sizeof(char_u *)),
TRUE);
if (temp == NULL) /* out of memory! */
{
if (type == 0) /* first one: just keep the old length */
{
newlen = hislen;
break;
}
/* Already changed one table, now we can only have zero
* length for all tables. */
newlen = 0;
type = -1;
continue;
}
}
else
temp = NULL;
if (newlen == 0 || temp != NULL)
{
if (hisidx[type] < 0) /* there are no entries yet */
{
for (i = 0; i < newlen; ++i)
temp[i] = NULL;
}
else if (newlen > hislen) /* array becomes bigger */
{
for (i = 0; i <= hisidx[type]; ++i)
temp[i] = history[type][i];
j = i;
for ( ; i <= newlen - (hislen - hisidx[type]); ++i)
temp[i] = NULL;
for ( ; j < hislen; ++i, ++j)
temp[i] = history[type][j];
}
else /* array becomes smaller or 0 */
{
j = hisidx[type];
for (i = newlen - 1; ; --i)
{
if (i >= 0) /* copy newest entries */
temp[i] = history[type][j];
else /* remove older entries */
vim_free(history[type][j]);
if (--j < 0)
j = hislen - 1;
if (j == hisidx[type])
break;
}
hisidx[type] = newlen - 1;
}
vim_free(history[type]);
history[type] = temp;
}
}
hislen = newlen;
}
}
/*
* Check if command line 'str' is already in history.
* If 'move_to_front' is TRUE, matching entry is moved to end of history.
*/
static int
in_history(type, str, move_to_front)
int type;
char_u *str;
int move_to_front; /* Move the entry to the front if it exists */
{
int i;
int last_i = -1;
if (hisidx[type] < 0)
return FALSE;
i = hisidx[type];
do
{
if (history[type][i] == NULL)
return FALSE;
if (STRCMP(str, history[type][i]) == 0)
{
if (!move_to_front)
return TRUE;
last_i = i;
break;
}
if (--i < 0)
i = hislen - 1;
} while (i != hisidx[type]);
if (last_i >= 0)
{
str = history[type][i];
while (i != hisidx[type])
{
if (++i >= hislen)
i = 0;
history[type][last_i] = history[type][i];
last_i = i;
}
history[type][i] = str;
return TRUE;
}
return FALSE;
}
/*
* Add the given string to the given history. If the string is already in the
* history then it is moved to the front. "histype" may be HIST_CMD,
* HIST_SEARCH, HIST_EXPR or HIST_INPUT.
*/
void
add_to_history(histype, new_entry)
int histype;
char_u *new_entry;
{
static int last_maptick = -1; /* last seen maptick */
if (hislen == 0) /* no history */
return;
/*
* Searches inside the same mapping overwrite each other, so that only
* the last line is kept. Be careful not to remove a line that was moved
* down, only lines that were added.
*/
if (histype == HIST_SEARCH)
{
if (maptick == last_maptick)
{
/* Current line is from the same mapping, remove it */
vim_free(history[HIST_SEARCH][hisidx[HIST_SEARCH]]);
history[HIST_SEARCH][hisidx[HIST_SEARCH]] = NULL;
if (--hisidx[HIST_SEARCH] < 0)
hisidx[HIST_SEARCH] = hislen - 1;
}
last_maptick = -1;
}
if (!in_history(histype, new_entry, TRUE))
{
if (++hisidx[histype] == hislen)
hisidx[histype] = 0;
vim_free(history[histype][hisidx[histype]]);
history[histype][hisidx[histype]] = vim_strsave(new_entry);
if (histype == HIST_SEARCH)
last_maptick = maptick;
}
}
#ifdef VIMINFO
static char_u **viminfo_history[HIST_COUNT] = {NULL, NULL, NULL, NULL};
static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0};
static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0};
static int viminfo_add_at_front = FALSE;
static int hist_type2char __ARGS((int type, int use_question));
/*
* Translate a history type number to the associated character.
*/
static int
hist_type2char(type, use_question)
int type;
int use_question; /* use '?' instead of '/' */
{
if (type == HIST_CMD)
return ':';
if (type == HIST_SEARCH)
{
if (use_question)
return '?';
else
return '/';
}
if (type == HIST_EXPR)
return '=';
return '@';
}
/*
* Prepare for reading the history from the viminfo file.
* This allocates history arrays to store the read history lines.
*/
void
prepare_viminfo_history(asklen)
int asklen;
{
int i;
int num;
int type;
int len;
init_history();
viminfo_add_at_front = (asklen != 0);
if (asklen > hislen)
asklen = hislen;
for (type = 0; type < HIST_COUNT; ++type)
{
/*
* Count the number of empty spaces in the history list. If there are
* more spaces available than we request, then fill them up.
*/
for (i = 0, num = 0; i < hislen; i++)
if (history[type][i] == NULL)
num++;
len = asklen;
if (num > len)
len = num;
if (len <= 0)
viminfo_history[type] = NULL;
else
viminfo_history[type] =
(char_u **)lalloc((long_u)(len * sizeof(char_u *)), FALSE);
if (viminfo_history[type] == NULL)
len = 0;
viminfo_hislen[type] = len;
viminfo_hisidx[type] = 0;
}
}
/*
* Accept a line from the viminfo, store it in the history array when it's
* new.
*/
int
read_viminfo_history(line, fp)
char_u *line;
FILE *fp;
{
int type;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -