📄 ex_getln.c
字号:
int options;
int mode;
{
char_u *ss = NULL;
static char_u **cmd_files = NULL; /* list of input files */
static int findex;
static char_u *orig_save = NULL; /* kept value of orig */
int i;
int non_suf_match; /* number without matching suffix */
long_u len;
char_u *p;
/*
* first handle the case of using an old match
*/
if (mode == WILD_NEXT || mode == WILD_PREV)
{
if (cmd_numfiles > 0)
{
if (mode == WILD_PREV)
{
if (findex == -1)
findex = cmd_numfiles;
--findex;
}
else /* mode == WILD_NEXT */
++findex;
/*
* When wrapping around, return the original string, set findex to
* -1.
*/
if (findex < 0)
{
if (orig_save == NULL)
findex = cmd_numfiles - 1;
else
findex = -1;
}
if (findex >= cmd_numfiles)
{
if (orig_save == NULL)
findex = 0;
else
findex = -1;
}
if (findex == -1)
return vim_strsave(orig_save);
return vim_strsave(cmd_files[findex]);
}
else
return NULL;
}
/* free old names */
if (cmd_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST)
{
FreeWild(cmd_numfiles, cmd_files);
cmd_numfiles = -1;
vim_free(orig_save);
orig_save = NULL;
}
findex = 0;
if (mode == WILD_FREE) /* only release file name */
return NULL;
if (cmd_numfiles == -1)
{
vim_free(orig_save);
orig_save = orig;
/*
* Do the expansion.
*/
if (ExpandFromContext(str, &cmd_numfiles, &cmd_files, FALSE,
options) == FAIL)
/* error: do nothing */;
else if (cmd_numfiles == 0)
{
if (!expand_interactively)
emsg2(e_nomatch2, str);
}
else
{
/*
* May change home directory back to "~"
*/
if (options & WILD_HOME_REPLACE)
tilde_replace(str, cmd_numfiles, cmd_files);
/*
* Insert backslashes into a file name before a space, \, %, # and
* wildmatch characters, except '~'.
*/
if (expand_interactively &&
(expand_context == EXPAND_FILES ||
expand_context == EXPAND_BUFFERS ||
expand_context == EXPAND_DIRECTORIES))
{
for (i = 0; i < cmd_numfiles; ++i)
{
/* for ":set path=" we need to escape spaces twice */
if (expand_set_path)
{
p = vim_strsave_escaped(cmd_files[i], (char_u *)" ");
if (p != NULL)
{
vim_free(cmd_files[i]);
cmd_files[i] = p;
#if defined(BACKSLASH_IN_FILENAME) || defined(COLON_AS_PATHSEP)
p = vim_strsave_escaped(cmd_files[i],
(char_u *)" ");
if (p != NULL)
{
vim_free(cmd_files[i]);
cmd_files[i] = p;
}
#endif
}
}
p = vim_strsave_escaped(cmd_files[i],
#ifdef BACKSLASH_IN_FILENAME
(char_u *)" *?[{`$%#"
#else
# ifdef COLON_AS_PATHSEP
(char_u *)" *?[{`$%#/"
# else
(char_u *)" *?[{`$\\%#"
# endif
#endif
);
if (p != NULL)
{
vim_free(cmd_files[i]);
cmd_files[i] = p;
}
}
expand_set_path = FALSE;
}
/*
* Check for matching suffixes in file names.
*/
if (mode != WILD_ALL && mode != WILD_LONGEST)
{
if (cmd_numfiles)
non_suf_match = cmd_numfiles;
else
non_suf_match = 1;
if ((!expand_interactively
|| expand_context == EXPAND_FILES
|| expand_context == EXPAND_DIRECTORIES)
&& cmd_numfiles > 1) /* more than one match; check suffix */
{
/*
* The files will have been sorted on matching suffix in
* expand_wildcards, only need to check the first two.
*/
non_suf_match = 0;
for (i = 0; i < 2; ++i)
if (match_suffix(cmd_files[i]))
++non_suf_match;
}
if (non_suf_match != 1)
{
/* Can we ever get here unless it's while expanding
* interactively? If not, we can get rid of this all
* together. Don't really want to wait for this message
* (and possibly have to hit return to continue!).
*/
if (!expand_interactively)
emsg(e_toomany);
else if (!(options & WILD_NO_BEEP))
beep_flush();
}
if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
ss = vim_strsave(cmd_files[0]);
}
}
}
/* Find longest common part */
if (mode == WILD_LONGEST && cmd_numfiles > 0)
{
for (len = 0; cmd_files[0][len]; ++len)
{
for (i = 0; i < cmd_numfiles; ++i)
{
#ifdef CASE_INSENSITIVE_FILENAME
if (expand_context == EXPAND_DIRECTORIES
|| expand_context == EXPAND_FILES
|| expand_context == EXPAND_BUFFERS)
{
if (TO_LOWER(cmd_files[i][len]) !=
TO_LOWER(cmd_files[0][len]))
break;
}
else
#endif
if (cmd_files[i][len] != cmd_files[0][len])
break;
}
if (i < cmd_numfiles)
{
if (!(options & WILD_NO_BEEP))
vim_beep();
break;
}
}
ss = alloc((unsigned)len + 1);
if (ss)
{
STRNCPY(ss, cmd_files[0], len);
ss[len] = NUL;
}
findex = -1; /* next p_wc gets first one */
}
/* Concatenate all matching names */
if (mode == WILD_ALL && cmd_numfiles > 0)
{
len = 0;
for (i = 0; i < cmd_numfiles; ++i)
len += STRLEN(cmd_files[i]) + 1;
ss = lalloc(len, TRUE);
if (ss != NULL)
{
*ss = NUL;
for (i = 0; i < cmd_numfiles; ++i)
{
STRCAT(ss, cmd_files[i]);
if (i != cmd_numfiles - 1)
STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
}
}
}
if (mode == WILD_EXPAND_FREE || mode == WILD_ALL)
{
FreeWild(cmd_numfiles, cmd_files);
cmd_numfiles = -1;
}
return ss;
}
/*
* For each file name in files[num_files]:
* If 'orig_pat' starts with "~/", replace the home directory with "~".
*/
void
tilde_replace(orig_pat, num_files, files)
char_u *orig_pat;
int num_files;
char_u **files;
{
int i;
char_u *p;
if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1]))
{
for (i = 0; i < num_files; ++i)
{
p = home_replace_save(NULL, files[i]);
if (p != NULL)
{
vim_free(files[i]);
files[i] = p;
}
}
}
}
/*
* show all matches for completion on the command line
*/
static int
showmatches()
{
char_u *file_str;
int num_files;
char_u **files_found;
int i, j, k;
int maxlen;
int lines;
int columns;
char_u *p;
int lastlen;
int attr;
set_expand_context();
if (expand_context == EXPAND_UNSUCCESSFUL)
{
beep_flush();
return OK; /* Something illegal on command line */
}
if (expand_context == EXPAND_NOTHING)
{
/* Caller can use the character as a normal char instead */
return FAIL;
}
expand_interactively = TRUE;
/* add star to file name, or convert to regexp if not expanding files! */
file_str = addstar(expand_pattern,
(int)(ccline.cmdbuff + ccline.cmdpos - expand_pattern));
if (file_str == NULL)
{
expand_interactively = FALSE;
return OK;
}
msg_didany = FALSE; /* lines_left will be set */
msg_start(); /* prepare for paging */
msg_putchar('\n');
out_flush();
cmdline_row = msg_row;
msg_didany = FALSE; /* lines_left will be set again */
msg_start(); /* prepare for paging */
/* find all files that match the description */
if (ExpandFromContext(file_str, &num_files, &files_found, FALSE, 0) == FAIL)
{
num_files = 0;
files_found = (char_u **)"";
}
/* find the length of the longest file name */
maxlen = 0;
for (i = 0; i < num_files; ++i)
{
if (expand_context == EXPAND_FILES || expand_context == EXPAND_BUFFERS)
{
home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE);
j = vim_strsize(NameBuff);
}
else
j = vim_strsize(files_found[i]);
if (j > maxlen)
maxlen = j;
}
/* compute the number of columns and lines for the listing */
maxlen += 2; /* two spaces between file names */
columns = ((int)Columns + 2) / maxlen;
if (columns < 1)
columns = 1;
lines = (num_files + columns - 1) / columns;
attr = hl_attr(HLF_D); /* find out highlighting for directories */
/* list the files line by line */
for (i = 0; i < lines; ++i)
{
lastlen = 999;
for (k = i; k < num_files; k += lines)
{
for (j = maxlen - lastlen; --j >= 0; )
msg_putchar(' ');
if (expand_context == EXPAND_FILES ||
expand_context == EXPAND_BUFFERS)
{
/* highlight directories */
j = (mch_isdir(files_found[k]));
home_replace(NULL, files_found[k], NameBuff, MAXPATHL, TRUE);
p = NameBuff;
}
else
{
j = FALSE;
p = files_found[k];
}
lastlen = msg_outtrans_attr(p, j ? attr : 0);
}
msg_clr_eos();
msg_putchar('\n');
out_flush(); /* show one line at a time */
if (got_int)
{
got_int = FALSE;
break;
}
}
vim_free(file_str);
FreeWild(num_files, files_found);
/*
* we redraw the command below the lines that we have just listed
* This is a bit tricky, but it saves a lot of screen updating.
*/
cmdline_row = msg_row; /* will put it back later */
expand_interactively = FALSE;
return OK;
}
/*
* Prepare a string for expansion.
* When expanding file names: The string will be used with expand_wildcards().
* Copy the file name into allocated memory and add a '*' at the end.
* When expanding other names: The string will be used with regcomp(). Copy
* the name into allocated memory and add ".*" at the end.
*/
char_u *
addstar(fname, len)
char_u *fname;
int len;
{
char_u *retval;
int i, j;
int new_len;
char_u *tail;
if (expand_interactively && expand_context != EXPAND_FILES &&
expand_context != EXPAND_DIRECTORIES)
{
/*
* Matching will be done internally (on something other than files).
* So we convert the file-matching-type wildcards into our kind for
* use with vim_regcomp(). First work out how long it will be:
*/
/* for help tags the translation is done in find_help_tags() */
if (expand_context == EXPAND_HELP)
retval = vim_strnsave(fname, len);
else
{
new_len = len + 2; /* +2 for '^' at start, NUL at end */
for (i = 0; i < len; i++)
{
if (fname[i] == '*' || fname[i] == '~')
new_len++; /* '*' needs to be replaced by ".*"
'~' needs to be replaced by "\~" */
/* Buffer names are like file names. "." should be literal */
if (expand_context == EXPAND_BUFFERS && fname[i] == '.')
new_len++; /* "." becomes "\." */
}
retval = alloc(new_len);
if (retval != NULL)
{
retval[0] = '^';
j = 1;
for (i = 0; i < len; i++, j++)
{
if (fname[i] == '\\' && ++i == len) /* skip backslash */
break;
switch (fname[i])
{
case '*': retval[j++] = '.';
break;
case '~': retval[j++] = '\\';
break;
case '?': retval[j] = '.';
continue;
case '.': if (expand_context == EXPAND_BUFFERS)
retval[j++] = '\\';
break;
}
retval[j] = fname[i];
}
retval[j] = NUL;
}
}
}
else
{
retval = alloc(len + 4);
if (retval != NULL)
{
STRNCPY(retval, fname, len);
retval[len] = NUL;
#ifndef macintosh
if (!expand_set_path)
backslash_halve(retval, TRUE); /* remove some backslashes */
#endif
len = STRLEN(retval);
/*
* Don't add a star to ~, ~user, $var or `cmd`.
* ~ would be at the start of the tail.
* $ could be anywhere in the tail.
* ` could be anywhere in the file name.
*/
tail = gettail(retval);
if (*tail != '~' && vim_strchr(tail, '$') == NULL
&& vim_strchr(retval, '`') == NULL)
{
#ifdef MSDOS
/*
* if there is no dot in the file name, add "*.*" instead of
* "*".
*/
for (i = len - 1; i >= 0; --i)
if (vim_strchr((char_u *)".\\/:", retval[i]) != NULL)
break;
if (i < 0 || retval[i] != '.')
{
retval[len++] = '*';
retval[len++] = '.';
}
#endif
retval[len++] = '*';
}
retval[len] = NUL;
}
}
return retval;
}
/*
* Must parse the command line so far to work out what context we are in.
* Completion can then be done based on that context.
* This routine sets two global variables:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -