vim-patch:8.1.1914: command line expansion code is spread out (#19867)

Problem:    Command line expansion code is spread out.
Solution:   Move set_one_cmd_context(). (Yegappan Lakshmanan, closes vim/vim#4855)
d019039ccd
This commit is contained in:
zeertzjq 2022-08-21 08:05:39 +08:00 committed by GitHub
parent 76d35a5a36
commit 741ef87876
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 856 additions and 843 deletions

View File

@ -928,6 +928,818 @@ void set_expand_context(expand_T *xp)
set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, true);
}
/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
/// we don't need/want deleted. Maybe this could be done better if we didn't
/// repeat all this stuff. The only problem is that they may not stay
/// perfectly compatible with each other, but then the command line syntax
/// probably won't change that much -- webb.
///
/// @param buff buffer for command string
static const char *set_one_cmd_context(expand_T *xp, const char *buff)
{
size_t len = 0;
exarg_T ea;
int context = EXPAND_NOTHING;
bool forceit = false;
bool usefilter = false; // Filter instead of file name.
ExpandInit(xp);
xp->xp_pattern = (char *)buff;
xp->xp_line = (char *)buff;
xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
ea.argt = 0;
// 2. skip comment lines and leading space, colons or bars
const char *cmd;
for (cmd = buff; vim_strchr(" \t:|", *cmd) != NULL; cmd++) {}
xp->xp_pattern = (char *)cmd;
if (*cmd == NUL) {
return NULL;
}
if (*cmd == '"') { // ignore comment lines
xp->xp_context = EXPAND_NOTHING;
return NULL;
}
// 3. parse a range specifier of the form: addr [,addr] [;addr] ..
cmd = (const char *)skip_range(cmd, &xp->xp_context);
// 4. parse command
xp->xp_pattern = (char *)cmd;
if (*cmd == NUL) {
return NULL;
}
if (*cmd == '"') {
xp->xp_context = EXPAND_NOTHING;
return NULL;
}
if (*cmd == '|' || *cmd == '\n') {
return cmd + 1; // There's another command
}
// Isolate the command and search for it in the command table.
// Exceptions:
// - the 'k' command can directly be followed by any character, but
// do accept "keepmarks", "keepalt" and "keepjumps".
// - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
const char *p;
if (*cmd == 'k' && cmd[1] != 'e') {
ea.cmdidx = CMD_k;
p = cmd + 1;
} else {
p = cmd;
while (ASCII_ISALPHA(*p) || *p == '*') { // Allow * wild card
p++;
}
// a user command may contain digits
if (ASCII_ISUPPER(cmd[0])) {
while (ASCII_ISALNUM(*p) || *p == '*') {
p++;
}
}
// for python 3.x: ":py3*" commands completion
if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') {
p++;
while (ASCII_ISALPHA(*p) || *p == '*') {
p++;
}
}
// check for non-alpha command
if (p == cmd && vim_strchr("@*!=><&~#", *p) != NULL) {
p++;
}
len = (size_t)(p - cmd);
if (len == 0) {
xp->xp_context = EXPAND_UNSUCCESSFUL;
return NULL;
}
ea.cmdidx = excmd_get_cmdidx(cmd, len);
if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card
p++;
}
}
}
// If the cursor is touching the command, and it ends in an alphanumeric
// character, complete the command name.
if (*p == NUL && ASCII_ISALNUM(p[-1])) {
return NULL;
}
if (ea.cmdidx == CMD_SIZE) {
if (*cmd == 's' && vim_strchr("cgriI", cmd[1]) != NULL) {
ea.cmdidx = CMD_substitute;
p = cmd + 1;
} else if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
ea.cmd = (char *)cmd;
p = (const char *)find_ucmd(&ea, (char *)p, NULL, xp, &context);
if (p == NULL) {
ea.cmdidx = CMD_SIZE; // Ambiguous user command.
}
}
}
if (ea.cmdidx == CMD_SIZE) {
// Not still touching the command and it was an illegal one
xp->xp_context = EXPAND_UNSUCCESSFUL;
return NULL;
}
xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
if (*p == '!') { // forced commands
forceit = true;
p++;
}
// 5. parse arguments
if (!IS_USER_CMDIDX(ea.cmdidx)) {
ea.argt = excmd_get_argt(ea.cmdidx);
}
const char *arg = (const char *)skipwhite(p);
// Skip over ++argopt argument
if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
p = arg;
while (*p && !ascii_isspace(*p)) {
MB_PTR_ADV(p);
}
arg = (const char *)skipwhite(p);
}
if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
if (*arg == '>') { // Append.
if (*++arg == '>') {
arg++;
}
arg = (const char *)skipwhite(arg);
} else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
arg++;
usefilter = true;
}
}
if (ea.cmdidx == CMD_read) {
usefilter = forceit; // :r! filter if forced
if (*arg == '!') { // :r !filter
arg++;
usefilter = true;
}
}
if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
while (*arg == *cmd) { // allow any number of '>' or '<'
arg++;
}
arg = (const char *)skipwhite(arg);
}
// Does command allow "+command"?
if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') {
// Check if we're in the +command
p = arg + 1;
arg = (const char *)skip_cmd_arg((char *)arg, false);
// Still touching the command after '+'?
if (*arg == NUL) {
return p;
}
// Skip space(s) after +command to get to the real argument.
arg = (const char *)skipwhite(arg);
}
// Check for '|' to separate commands and '"' to start comments.
// Don't do this for ":read !cmd" and ":write !cmd".
if ((ea.argt & EX_TRLBAR) && !usefilter) {
p = arg;
// ":redir @" is not the start of a comment
if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') {
p += 2;
}
while (*p) {
if (*p == Ctrl_V) {
if (p[1] != NUL) {
p++;
}
} else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM))
|| *p == '|'
|| *p == '\n') {
if (*(p - 1) != '\\') {
if (*p == '|' || *p == '\n') {
return p + 1;
}
return NULL; // It's a comment
}
}
MB_PTR_ADV(p);
}
}
if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) {
// no arguments allowed but there is something
return NULL;
}
// Find start of last argument (argument just before cursor):
p = buff;
xp->xp_pattern = (char *)p;
len = strlen(buff);
while (*p && p < buff + len) {
if (*p == ' ' || *p == TAB) {
// Argument starts after a space.
xp->xp_pattern = (char *)++p;
} else {
if (*p == '\\' && *(p + 1) != NUL) {
p++; // skip over escaped character
}
MB_PTR_ADV(p);
}
}
if (ea.argt & EX_XFILE) {
int in_quote = false;
const char *bow = NULL; // Beginning of word.
// Allow spaces within back-quotes to count as part of the argument
// being expanded.
xp->xp_pattern = skipwhite(arg);
p = (const char *)xp->xp_pattern;
while (*p != NUL) {
int c = utf_ptr2char(p);
if (c == '\\' && p[1] != NUL) {
p++;
} else if (c == '`') {
if (!in_quote) {
xp->xp_pattern = (char *)p;
bow = p + 1;
}
in_quote = !in_quote;
// An argument can contain just about everything, except
// characters that end the command and white space.
} else if (c == '|' || c == '\n' || c == '"' || ascii_iswhite(c)) {
len = 0; // avoid getting stuck when space is in 'isfname'
while (*p != NUL) {
c = utf_ptr2char(p);
if (c == '`' || vim_isfilec_or_wc(c)) {
break;
}
len = (size_t)utfc_ptr2len(p);
MB_PTR_ADV(p);
}
if (in_quote) {
bow = p;
} else {
xp->xp_pattern = (char *)p;
}
p -= len;
}
MB_PTR_ADV(p);
}
// If we are still inside the quotes, and we passed a space, just
// expand from there.
if (bow != NULL && in_quote) {
xp->xp_pattern = (char *)bow;
}
xp->xp_context = EXPAND_FILES;
// For a shell command more chars need to be escaped.
if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal) {
#ifndef BACKSLASH_IN_FILENAME
xp->xp_shell = true;
#endif
// When still after the command name expand executables.
if (xp->xp_pattern == skipwhite(arg)) {
xp->xp_context = EXPAND_SHELLCMD;
}
}
// Check for environment variable.
if (*xp->xp_pattern == '$') {
for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) {
if (!vim_isIDc((uint8_t)(*p))) {
break;
}
}
if (*p == NUL) {
xp->xp_context = EXPAND_ENV_VARS;
xp->xp_pattern++;
// Avoid that the assignment uses EXPAND_FILES again.
if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) {
context = EXPAND_ENV_VARS;
}
}
}
// Check for user names.
if (*xp->xp_pattern == '~') {
for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) {}
// Complete ~user only if it partially matches a user name.
// A full match ~user<Tab> will be replaced by user's home
// directory i.e. something like ~user<Tab> -> /home/user/
if (*p == NUL && p > (const char *)xp->xp_pattern + 1
&& match_user((char_u *)xp->xp_pattern + 1) >= 1) {
xp->xp_context = EXPAND_USER;
xp->xp_pattern++;
}
}
}
// 6. switch on command name
switch (ea.cmdidx) {
case CMD_find:
case CMD_sfind:
case CMD_tabfind:
if (xp->xp_context == EXPAND_FILES) {
xp->xp_context = EXPAND_FILES_IN_PATH;
}
break;
case CMD_cd:
case CMD_chdir:
case CMD_lcd:
case CMD_lchdir:
case CMD_tcd:
case CMD_tchdir:
if (xp->xp_context == EXPAND_FILES) {
xp->xp_context = EXPAND_DIRECTORIES;
}
break;
case CMD_help:
xp->xp_context = EXPAND_HELP;
xp->xp_pattern = (char *)arg;
break;
// Command modifiers: return the argument.
// Also for commands with an argument that is a command.
case CMD_aboveleft:
case CMD_argdo:
case CMD_belowright:
case CMD_botright:
case CMD_browse:
case CMD_bufdo:
case CMD_cdo:
case CMD_cfdo:
case CMD_confirm:
case CMD_debug:
case CMD_folddoclosed:
case CMD_folddoopen:
case CMD_hide:
case CMD_keepalt:
case CMD_keepjumps:
case CMD_keepmarks:
case CMD_keeppatterns:
case CMD_ldo:
case CMD_leftabove:
case CMD_lfdo:
case CMD_lockmarks:
case CMD_noautocmd:
case CMD_noswapfile:
case CMD_rightbelow:
case CMD_sandbox:
case CMD_silent:
case CMD_tab:
case CMD_tabdo:
case CMD_topleft:
case CMD_verbose:
case CMD_vertical:
case CMD_windo:
return arg;
case CMD_filter:
if (*arg != NUL) {
arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL);
}
if (arg == NULL || *arg == NUL) {
xp->xp_context = EXPAND_NOTHING;
return NULL;
}
return (const char *)skipwhite(arg);
case CMD_match:
if (*arg == NUL || !ends_excmd(*arg)) {
// also complete "None"
set_context_in_echohl_cmd(xp, arg);
arg = (const char *)skipwhite((char *)skiptowhite((const char_u *)arg));
if (*arg != NUL) {
xp->xp_context = EXPAND_NOTHING;
arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg),
p_magic, NULL);
}
}
return (const char *)find_nextcmd((char_u *)arg);
// All completion for the +cmdline_compl feature goes here.
case CMD_command:
return set_context_in_user_cmd(xp, arg);
case CMD_delcommand:
xp->xp_context = EXPAND_USER_COMMANDS;
xp->xp_pattern = (char *)arg;
break;
case CMD_global:
case CMD_vglobal: {
const int delim = (uint8_t)(*arg); // Get the delimiter.
if (delim) {
arg++; // Skip delimiter if there is one.
}
while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
if (arg[0] == '\\' && arg[1] != NUL) {
arg++;
}
arg++;
}
if (arg[0] != NUL) {
return arg + 1;
}
break;
}
case CMD_and:
case CMD_substitute: {
const int delim = (uint8_t)(*arg);
if (delim) {
// Skip "from" part.
arg++;
arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL);
}
// Skip "to" part.
while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
if (arg[0] == '\\' && arg[1] != NUL) {
arg++;
}
arg++;
}
if (arg[0] != NUL) { // Skip delimiter.
arg++;
}
while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
arg++;
}
if (arg[0] != NUL) {
return arg;
}
break;
}
case CMD_isearch:
case CMD_dsearch:
case CMD_ilist:
case CMD_dlist:
case CMD_ijump:
case CMD_psearch:
case CMD_djump:
case CMD_isplit:
case CMD_dsplit:
// Skip count.
arg = (const char *)skipwhite(skipdigits(arg));
if (*arg == '/') { // Match regexp, not just whole words.
for (++arg; *arg && *arg != '/'; arg++) {
if (*arg == '\\' && arg[1] != NUL) {
arg++;
}
}
if (*arg) {
arg = (const char *)skipwhite(arg + 1);
// Check for trailing illegal characters.
if (*arg && strchr("|\"\n", *arg) == NULL) {
xp->xp_context = EXPAND_NOTHING;
} else {
return arg;
}
}
}
break;
case CMD_autocmd:
return (const char *)set_context_in_autocmd(xp, (char *)arg, false);
case CMD_doautocmd:
case CMD_doautoall:
return (const char *)set_context_in_autocmd(xp, (char *)arg, true);
case CMD_set:
set_context_in_set_cmd(xp, (char_u *)arg, 0);
break;
case CMD_setglobal:
set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL);
break;
case CMD_setlocal:
set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL);
break;
case CMD_tag:
case CMD_stag:
case CMD_ptag:
case CMD_ltag:
case CMD_tselect:
case CMD_stselect:
case CMD_ptselect:
case CMD_tjump:
case CMD_stjump:
case CMD_ptjump:
if (wop_flags & WOP_TAGFILE) {
xp->xp_context = EXPAND_TAGS_LISTFILES;
} else {
xp->xp_context = EXPAND_TAGS;
}
xp->xp_pattern = (char *)arg;
break;
case CMD_augroup:
xp->xp_context = EXPAND_AUGROUP;
xp->xp_pattern = (char *)arg;
break;
case CMD_syntax:
set_context_in_syntax_cmd(xp, arg);
break;
case CMD_const:
case CMD_let:
case CMD_if:
case CMD_elseif:
case CMD_while:
case CMD_for:
case CMD_echo:
case CMD_echon:
case CMD_execute:
case CMD_echomsg:
case CMD_echoerr:
case CMD_call:
case CMD_return:
case CMD_cexpr:
case CMD_caddexpr:
case CMD_cgetexpr:
case CMD_lexpr:
case CMD_laddexpr:
case CMD_lgetexpr:
set_context_for_expression(xp, (char *)arg, ea.cmdidx);
break;
case CMD_unlet:
while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
arg = (const char *)xp->xp_pattern + 1;
}
xp->xp_context = EXPAND_USER_VARS;
xp->xp_pattern = (char *)arg;
if (*xp->xp_pattern == '$') {
xp->xp_context = EXPAND_ENV_VARS;
xp->xp_pattern++;
}
break;
case CMD_function:
case CMD_delfunction:
xp->xp_context = EXPAND_USER_FUNC;
xp->xp_pattern = (char *)arg;
break;
case CMD_echohl:
set_context_in_echohl_cmd(xp, arg);
break;
case CMD_highlight:
set_context_in_highlight_cmd(xp, arg);
break;
case CMD_cscope:
case CMD_lcscope:
case CMD_scscope:
set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
break;
case CMD_sign:
set_context_in_sign_cmd(xp, (char_u *)arg);
break;
case CMD_bdelete:
case CMD_bwipeout:
case CMD_bunload:
while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
arg = (const char *)xp->xp_pattern + 1;
}
FALLTHROUGH;
case CMD_buffer:
case CMD_sbuffer:
case CMD_checktime:
xp->xp_context = EXPAND_BUFFERS;
xp->xp_pattern = (char *)arg;
break;
case CMD_diffget:
case CMD_diffput:
// If current buffer is in diff mode, complete buffer names
// which are in diff mode, and different than current buffer.
xp->xp_context = EXPAND_DIFF_BUFFERS;
xp->xp_pattern = (char *)arg;
break;
case CMD_USER:
case CMD_USER_BUF:
if (context != EXPAND_NOTHING) {
// EX_XFILE: file names are handled above.
if (!(ea.argt & EX_XFILE)) {
if (context == EXPAND_MENUS) {
return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
} else if (context == EXPAND_COMMANDS) {
return arg;
} else if (context == EXPAND_MAPPINGS) {
return (const char *)set_context_in_map_cmd(xp, "map", (char_u *)arg, forceit,
false, false,
CMD_map);
}
// Find start of last argument.
p = arg;
while (*p) {
if (*p == ' ') {
// argument starts after a space
arg = p + 1;
} else if (*p == '\\' && *(p + 1) != NUL) {
p++; // skip over escaped character
}
MB_PTR_ADV(p);
}
xp->xp_pattern = (char *)arg;
}
xp->xp_context = context;
}
break;
case CMD_map:
case CMD_noremap:
case CMD_nmap:
case CMD_nnoremap:
case CMD_vmap:
case CMD_vnoremap:
case CMD_omap:
case CMD_onoremap:
case CMD_imap:
case CMD_inoremap:
case CMD_cmap:
case CMD_cnoremap:
case CMD_lmap:
case CMD_lnoremap:
case CMD_smap:
case CMD_snoremap:
case CMD_xmap:
case CMD_xnoremap:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
false, ea.cmdidx);
case CMD_unmap:
case CMD_nunmap:
case CMD_vunmap:
case CMD_ounmap:
case CMD_iunmap:
case CMD_cunmap:
case CMD_lunmap:
case CMD_sunmap:
case CMD_xunmap:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
true, ea.cmdidx);
case CMD_mapclear:
case CMD_nmapclear:
case CMD_vmapclear:
case CMD_omapclear:
case CMD_imapclear:
case CMD_cmapclear:
case CMD_lmapclear:
case CMD_smapclear:
case CMD_xmapclear:
xp->xp_context = EXPAND_MAPCLEAR;
xp->xp_pattern = (char *)arg;
break;
case CMD_abbreviate:
case CMD_noreabbrev:
case CMD_cabbrev:
case CMD_cnoreabbrev:
case CMD_iabbrev:
case CMD_inoreabbrev:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
false, ea.cmdidx);
case CMD_unabbreviate:
case CMD_cunabbrev:
case CMD_iunabbrev:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
true, ea.cmdidx);
case CMD_menu:
case CMD_noremenu:
case CMD_unmenu:
case CMD_amenu:
case CMD_anoremenu:
case CMD_aunmenu:
case CMD_nmenu:
case CMD_nnoremenu:
case CMD_nunmenu:
case CMD_vmenu:
case CMD_vnoremenu:
case CMD_vunmenu:
case CMD_omenu:
case CMD_onoremenu:
case CMD_ounmenu:
case CMD_imenu:
case CMD_inoremenu:
case CMD_iunmenu:
case CMD_cmenu:
case CMD_cnoremenu:
case CMD_cunmenu:
case CMD_tlmenu:
case CMD_tlnoremenu:
case CMD_tlunmenu:
case CMD_tmenu:
case CMD_tunmenu:
case CMD_popup:
case CMD_emenu:
return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
case CMD_colorscheme:
xp->xp_context = EXPAND_COLORS;
xp->xp_pattern = (char *)arg;
break;
case CMD_compiler:
xp->xp_context = EXPAND_COMPILER;
xp->xp_pattern = (char *)arg;
break;
case CMD_ownsyntax:
xp->xp_context = EXPAND_OWNSYNTAX;
xp->xp_pattern = (char *)arg;
break;
case CMD_setfiletype:
xp->xp_context = EXPAND_FILETYPE;
xp->xp_pattern = (char *)arg;
break;
case CMD_packadd:
xp->xp_context = EXPAND_PACKADD;
xp->xp_pattern = (char *)arg;
break;
#ifdef HAVE_WORKING_LIBINTL
case CMD_language:
p = (const char *)skiptowhite((const char_u *)arg);
if (*p == NUL) {
xp->xp_context = EXPAND_LANGUAGE;
xp->xp_pattern = (char *)arg;
} else {
if (strncmp(arg, "messages", (size_t)(p - arg)) == 0
|| strncmp(arg, "ctype", (size_t)(p - arg)) == 0
|| strncmp(arg, "time", (size_t)(p - arg)) == 0
|| strncmp(arg, "collate", (size_t)(p - arg)) == 0) {
xp->xp_context = EXPAND_LOCALES;
xp->xp_pattern = skipwhite(p);
} else {
xp->xp_context = EXPAND_NOTHING;
}
}
break;
#endif
case CMD_profile:
set_context_in_profile_cmd(xp, arg);
break;
case CMD_checkhealth:
xp->xp_context = EXPAND_CHECKHEALTH;
xp->xp_pattern = (char *)arg;
break;
case CMD_behave:
xp->xp_context = EXPAND_BEHAVE;
xp->xp_pattern = (char *)arg;
break;
case CMD_messages:
xp->xp_context = EXPAND_MESSAGES;
xp->xp_pattern = (char *)arg;
break;
case CMD_history:
xp->xp_context = EXPAND_HISTORY;
xp->xp_pattern = (char *)arg;
break;
case CMD_syntime:
xp->xp_context = EXPAND_SYNTIME;
xp->xp_pattern = (char *)arg;
break;
case CMD_argdelete:
while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) {
arg = (const char *)(xp->xp_pattern + 1);
}
xp->xp_context = EXPAND_ARGLIST;
xp->xp_pattern = (char *)arg;
break;
case CMD_lua:
xp->xp_context = EXPAND_LUA;
break;
default:
break;
}
return NULL;
}
/// @param str start of command line
/// @param len length of command line (excl. NUL)
/// @param col position of cursor
@ -1013,12 +1825,43 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***
return EXPAND_OK;
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":behave {mswin,xterm}" command.
static char *get_behave_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
return "mswin";
}
if (idx == 1) {
return "xterm";
}
return NULL;
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":messages {clear}" command.
static char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
return "clear";
}
return NULL;
}
static char *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
return "<buffer>";
}
return NULL;
}
/// Completion for |:checkhealth| command.
///
/// Given to ExpandGeneric() to obtain all available heathcheck names.
/// @param[in] idx Index of the healthcheck item.
/// @param[in] xp Not used.
static char *get_healthcheck_names(expand_T *xp, int idx)
static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
static Object names = OBJECT_INIT;
static unsigned last_gen = 0;
@ -1039,8 +1882,6 @@ static char *get_healthcheck_names(expand_T *xp, int idx)
return NULL;
}
typedef char *(*ExpandFunc)(expand_T *, int);
/// Do the expansion based on xp->xp_context and "pat".
///
/// @param options WILD_ flags
@ -1221,6 +2062,7 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
} else if (xp->xp_context == EXPAND_USER_DEFINED) {
ret = ExpandUserDefined(xp, &regmatch, num_file, file);
} else {
typedef CompleteListItemGetter ExpandFunc;
static struct expgen {
int context;
ExpandFunc func;

View File

@ -3068,820 +3068,22 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
: (char_u *)cmdnames[ea.cmdidx].cmd_name);
}
/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
/// we don't need/want deleted. Maybe this could be done better if we didn't
/// repeat all this stuff. The only problem is that they may not stay
/// perfectly compatible with each other, but then the command line syntax
/// probably won't change that much -- webb.
///
/// @param buff buffer for command string
const char *set_one_cmd_context(expand_T *xp, const char *buff)
cmdidx_T excmd_get_cmdidx(const char *cmd, size_t len)
{
size_t len = 0;
exarg_T ea;
int context = EXPAND_NOTHING;
bool forceit = false;
bool usefilter = false; // Filter instead of file name.
cmdidx_T idx;
ExpandInit(xp);
xp->xp_pattern = (char *)buff;
xp->xp_line = (char *)buff;
xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
ea.argt = 0;
// 2. skip comment lines and leading space, colons or bars
const char *cmd;
for (cmd = buff; vim_strchr(" \t:|", *cmd) != NULL; cmd++) {}
xp->xp_pattern = (char *)cmd;
if (*cmd == NUL) {
return NULL;
}
if (*cmd == '"') { // ignore comment lines
xp->xp_context = EXPAND_NOTHING;
return NULL;
}
// 3. parse a range specifier of the form: addr [,addr] [;addr] ..
cmd = (const char *)skip_range(cmd, &xp->xp_context);
// 4. parse command
xp->xp_pattern = (char *)cmd;
if (*cmd == NUL) {
return NULL;
}
if (*cmd == '"') {
xp->xp_context = EXPAND_NOTHING;
return NULL;
}
if (*cmd == '|' || *cmd == '\n') {
return cmd + 1; // There's another command
}
// Isolate the command and search for it in the command table.
// Exceptions:
// - the 'k' command can directly be followed by any character, but
// do accept "keepmarks", "keepalt" and "keepjumps".
// - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
const char *p;
if (*cmd == 'k' && cmd[1] != 'e') {
ea.cmdidx = CMD_k;
p = cmd + 1;
} else {
p = cmd;
while (ASCII_ISALPHA(*p) || *p == '*') { // Allow * wild card
p++;
}
// a user command may contain digits
if (ASCII_ISUPPER(cmd[0])) {
while (ASCII_ISALNUM(*p) || *p == '*') {
p++;
}
}
// for python 3.x: ":py3*" commands completion
if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') {
p++;
while (ASCII_ISALPHA(*p) || *p == '*') {
p++;
}
}
// check for non-alpha command
if (p == cmd && vim_strchr("@*!=><&~#", *p) != NULL) {
p++;
}
len = (size_t)(p - cmd);
if (len == 0) {
xp->xp_context = EXPAND_UNSUCCESSFUL;
return NULL;
}
for (ea.cmdidx = (cmdidx_T)0; (int)ea.cmdidx < CMD_SIZE;
ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) {
if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, len) == 0) {
break;
}
}
if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card
p++;
}
for (idx = (cmdidx_T)0; (int)idx < (int)CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) {
if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) {
break;
}
}
//
// If the cursor is touching the command, and it ends in an alphanumeric
// character, complete the command name.
//
if (*p == NUL && ASCII_ISALNUM(p[-1])) {
return NULL;
}
return idx;
}
if (ea.cmdidx == CMD_SIZE) {
if (*cmd == 's' && vim_strchr("cgriI", cmd[1]) != NULL) {
ea.cmdidx = CMD_substitute;
p = cmd + 1;
} else if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
ea.cmd = (char *)cmd;
p = (const char *)find_ucmd(&ea, (char *)p, NULL, xp, &context);
if (p == NULL) {
ea.cmdidx = CMD_SIZE; // Ambiguous user command.
}
}
}
if (ea.cmdidx == CMD_SIZE) {
// Not still touching the command and it was an illegal one
xp->xp_context = EXPAND_UNSUCCESSFUL;
return NULL;
}
xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
if (*p == '!') { // forced commands
forceit = true;
p++;
}
// 5. parse arguments
if (!IS_USER_CMDIDX(ea.cmdidx)) {
ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt;
}
const char *arg = (const char *)skipwhite(p);
// Skip over ++argopt argument
if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
p = arg;
while (*p && !ascii_isspace(*p)) {
MB_PTR_ADV(p);
}
arg = (const char *)skipwhite(p);
}
if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
if (*arg == '>') { // Append.
if (*++arg == '>') {
arg++;
}
arg = (const char *)skipwhite(arg);
} else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
arg++;
usefilter = true;
}
}
if (ea.cmdidx == CMD_read) {
usefilter = forceit; // :r! filter if forced
if (*arg == '!') { // :r !filter
arg++;
usefilter = true;
}
}
if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
while (*arg == *cmd) { // allow any number of '>' or '<'
arg++;
}
arg = (const char *)skipwhite(arg);
}
// Does command allow "+command"?
if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') {
// Check if we're in the +command
p = arg + 1;
arg = (const char *)skip_cmd_arg((char *)arg, false);
// Still touching the command after '+'?
if (*arg == NUL) {
return p;
}
// Skip space(s) after +command to get to the real argument.
arg = (const char *)skipwhite(arg);
}
// Check for '|' to separate commands and '"' to start comments.
// Don't do this for ":read !cmd" and ":write !cmd".
if ((ea.argt & EX_TRLBAR) && !usefilter) {
p = arg;
// ":redir @" is not the start of a comment
if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') {
p += 2;
}
while (*p) {
if (*p == Ctrl_V) {
if (p[1] != NUL) {
p++;
}
} else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM))
|| *p == '|'
|| *p == '\n') {
if (*(p - 1) != '\\') {
if (*p == '|' || *p == '\n') {
return p + 1;
}
return NULL; // It's a comment
}
}
MB_PTR_ADV(p);
}
}
if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) {
// no arguments allowed but there is something
return NULL;
}
// Find start of last argument (argument just before cursor):
p = buff;
xp->xp_pattern = (char *)p;
len = strlen(buff);
while (*p && p < buff + len) {
if (*p == ' ' || *p == TAB) {
// Argument starts after a space.
xp->xp_pattern = (char *)++p;
} else {
if (*p == '\\' && *(p + 1) != NUL) {
p++; // skip over escaped character
}
MB_PTR_ADV(p);
}
}
if (ea.argt & EX_XFILE) {
int in_quote = false;
const char *bow = NULL; // Beginning of word.
// Allow spaces within back-quotes to count as part of the argument
// being expanded.
xp->xp_pattern = skipwhite(arg);
p = (const char *)xp->xp_pattern;
while (*p != NUL) {
int c = utf_ptr2char(p);
if (c == '\\' && p[1] != NUL) {
p++;
} else if (c == '`') {
if (!in_quote) {
xp->xp_pattern = (char *)p;
bow = p + 1;
}
in_quote = !in_quote;
// An argument can contain just about everything, except
// characters that end the command and white space.
} else if (c == '|' || c == '\n' || c == '"' || ascii_iswhite(c)) {
len = 0; // avoid getting stuck when space is in 'isfname'
while (*p != NUL) {
c = utf_ptr2char(p);
if (c == '`' || vim_isfilec_or_wc(c)) {
break;
}
len = (size_t)utfc_ptr2len(p);
MB_PTR_ADV(p);
}
if (in_quote) {
bow = p;
} else {
xp->xp_pattern = (char *)p;
}
p -= len;
}
MB_PTR_ADV(p);
}
// If we are still inside the quotes, and we passed a space, just
// expand from there.
if (bow != NULL && in_quote) {
xp->xp_pattern = (char *)bow;
}
xp->xp_context = EXPAND_FILES;
// For a shell command more chars need to be escaped.
if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal) {
#ifndef BACKSLASH_IN_FILENAME
xp->xp_shell = true;
#endif
// When still after the command name expand executables.
if (xp->xp_pattern == skipwhite(arg)) {
xp->xp_context = EXPAND_SHELLCMD;
}
}
// Check for environment variable.
if (*xp->xp_pattern == '$') {
for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) {
if (!vim_isIDc((uint8_t)(*p))) {
break;
}
}
if (*p == NUL) {
xp->xp_context = EXPAND_ENV_VARS;
xp->xp_pattern++;
// Avoid that the assignment uses EXPAND_FILES again.
if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) {
context = EXPAND_ENV_VARS;
}
}
}
// Check for user names.
if (*xp->xp_pattern == '~') {
for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) {}
// Complete ~user only if it partially matches a user name.
// A full match ~user<Tab> will be replaced by user's home
// directory i.e. something like ~user<Tab> -> /home/user/
if (*p == NUL && p > (const char *)xp->xp_pattern + 1
&& match_user((char_u *)xp->xp_pattern + 1) >= 1) {
xp->xp_context = EXPAND_USER;
xp->xp_pattern++;
}
}
}
// 6. switch on command name
switch (ea.cmdidx) {
case CMD_find:
case CMD_sfind:
case CMD_tabfind:
if (xp->xp_context == EXPAND_FILES) {
xp->xp_context = EXPAND_FILES_IN_PATH;
}
break;
case CMD_cd:
case CMD_chdir:
case CMD_lcd:
case CMD_lchdir:
case CMD_tcd:
case CMD_tchdir:
if (xp->xp_context == EXPAND_FILES) {
xp->xp_context = EXPAND_DIRECTORIES;
}
break;
case CMD_help:
xp->xp_context = EXPAND_HELP;
xp->xp_pattern = (char *)arg;
break;
// Command modifiers: return the argument.
// Also for commands with an argument that is a command.
case CMD_aboveleft:
case CMD_argdo:
case CMD_belowright:
case CMD_botright:
case CMD_browse:
case CMD_bufdo:
case CMD_cdo:
case CMD_cfdo:
case CMD_confirm:
case CMD_debug:
case CMD_folddoclosed:
case CMD_folddoopen:
case CMD_hide:
case CMD_keepalt:
case CMD_keepjumps:
case CMD_keepmarks:
case CMD_keeppatterns:
case CMD_ldo:
case CMD_leftabove:
case CMD_lfdo:
case CMD_lockmarks:
case CMD_noautocmd:
case CMD_noswapfile:
case CMD_rightbelow:
case CMD_sandbox:
case CMD_silent:
case CMD_tab:
case CMD_tabdo:
case CMD_topleft:
case CMD_verbose:
case CMD_vertical:
case CMD_windo:
return arg;
case CMD_filter:
if (*arg != NUL) {
arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL);
}
if (arg == NULL || *arg == NUL) {
xp->xp_context = EXPAND_NOTHING;
return NULL;
}
return (const char *)skipwhite(arg);
case CMD_match:
if (*arg == NUL || !ends_excmd(*arg)) {
// also complete "None"
set_context_in_echohl_cmd(xp, arg);
arg = (const char *)skipwhite((char *)skiptowhite((const char_u *)arg));
if (*arg != NUL) {
xp->xp_context = EXPAND_NOTHING;
arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg),
p_magic, NULL);
}
}
return (const char *)find_nextcmd((char_u *)arg);
// All completion for the +cmdline_compl feature goes here.
case CMD_command:
return set_context_in_user_cmd(xp, arg);
case CMD_delcommand:
xp->xp_context = EXPAND_USER_COMMANDS;
xp->xp_pattern = (char *)arg;
break;
case CMD_global:
case CMD_vglobal: {
const int delim = (uint8_t)(*arg); // Get the delimiter.
if (delim) {
arg++; // Skip delimiter if there is one.
}
while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
if (arg[0] == '\\' && arg[1] != NUL) {
arg++;
}
arg++;
}
if (arg[0] != NUL) {
return arg + 1;
}
break;
}
case CMD_and:
case CMD_substitute: {
const int delim = (uint8_t)(*arg);
if (delim) {
// Skip "from" part.
arg++;
arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL);
}
// Skip "to" part.
while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
if (arg[0] == '\\' && arg[1] != NUL) {
arg++;
}
arg++;
}
if (arg[0] != NUL) { // Skip delimiter.
arg++;
}
while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
arg++;
}
if (arg[0] != NUL) {
return arg;
}
break;
}
case CMD_isearch:
case CMD_dsearch:
case CMD_ilist:
case CMD_dlist:
case CMD_ijump:
case CMD_psearch:
case CMD_djump:
case CMD_isplit:
case CMD_dsplit:
// Skip count.
arg = (const char *)skipwhite(skipdigits(arg));
if (*arg == '/') { // Match regexp, not just whole words.
for (++arg; *arg && *arg != '/'; arg++) {
if (*arg == '\\' && arg[1] != NUL) {
arg++;
}
}
if (*arg) {
arg = (const char *)skipwhite(arg + 1);
// Check for trailing illegal characters.
if (*arg && strchr("|\"\n", *arg) == NULL) {
xp->xp_context = EXPAND_NOTHING;
} else {
return arg;
}
}
}
break;
case CMD_autocmd:
return (const char *)set_context_in_autocmd(xp, (char *)arg, false);
case CMD_doautocmd:
case CMD_doautoall:
return (const char *)set_context_in_autocmd(xp, (char *)arg, true);
case CMD_set:
set_context_in_set_cmd(xp, (char_u *)arg, 0);
break;
case CMD_setglobal:
set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL);
break;
case CMD_setlocal:
set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL);
break;
case CMD_tag:
case CMD_stag:
case CMD_ptag:
case CMD_ltag:
case CMD_tselect:
case CMD_stselect:
case CMD_ptselect:
case CMD_tjump:
case CMD_stjump:
case CMD_ptjump:
if (wop_flags & WOP_TAGFILE) {
xp->xp_context = EXPAND_TAGS_LISTFILES;
} else {
xp->xp_context = EXPAND_TAGS;
}
xp->xp_pattern = (char *)arg;
break;
case CMD_augroup:
xp->xp_context = EXPAND_AUGROUP;
xp->xp_pattern = (char *)arg;
break;
case CMD_syntax:
set_context_in_syntax_cmd(xp, arg);
break;
case CMD_const:
case CMD_let:
case CMD_if:
case CMD_elseif:
case CMD_while:
case CMD_for:
case CMD_echo:
case CMD_echon:
case CMD_execute:
case CMD_echomsg:
case CMD_echoerr:
case CMD_call:
case CMD_return:
case CMD_cexpr:
case CMD_caddexpr:
case CMD_cgetexpr:
case CMD_lexpr:
case CMD_laddexpr:
case CMD_lgetexpr:
set_context_for_expression(xp, (char *)arg, ea.cmdidx);
break;
case CMD_unlet:
while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
arg = (const char *)xp->xp_pattern + 1;
}
xp->xp_context = EXPAND_USER_VARS;
xp->xp_pattern = (char *)arg;
if (*xp->xp_pattern == '$') {
xp->xp_context = EXPAND_ENV_VARS;
xp->xp_pattern++;
}
break;
case CMD_function:
case CMD_delfunction:
xp->xp_context = EXPAND_USER_FUNC;
xp->xp_pattern = (char *)arg;
break;
case CMD_echohl:
set_context_in_echohl_cmd(xp, arg);
break;
case CMD_highlight:
set_context_in_highlight_cmd(xp, arg);
break;
case CMD_cscope:
case CMD_lcscope:
case CMD_scscope:
set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
break;
case CMD_sign:
set_context_in_sign_cmd(xp, (char_u *)arg);
break;
case CMD_bdelete:
case CMD_bwipeout:
case CMD_bunload:
while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
arg = (const char *)xp->xp_pattern + 1;
}
FALLTHROUGH;
case CMD_buffer:
case CMD_sbuffer:
case CMD_checktime:
xp->xp_context = EXPAND_BUFFERS;
xp->xp_pattern = (char *)arg;
break;
case CMD_diffget:
case CMD_diffput:
// If current buffer is in diff mode, complete buffer names
// which are in diff mode, and different than current buffer.
xp->xp_context = EXPAND_DIFF_BUFFERS;
xp->xp_pattern = (char *)arg;
break;
case CMD_USER:
case CMD_USER_BUF:
if (context != EXPAND_NOTHING) {
// EX_XFILE: file names are handled above.
if (!(ea.argt & EX_XFILE)) {
if (context == EXPAND_MENUS) {
return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
} else if (context == EXPAND_COMMANDS) {
return arg;
} else if (context == EXPAND_MAPPINGS) {
return (const char *)set_context_in_map_cmd(xp, "map", (char_u *)arg, forceit,
false, false,
CMD_map);
}
// Find start of last argument.
p = arg;
while (*p) {
if (*p == ' ') {
// argument starts after a space
arg = p + 1;
} else if (*p == '\\' && *(p + 1) != NUL) {
p++; // skip over escaped character
}
MB_PTR_ADV(p);
}
xp->xp_pattern = (char *)arg;
}
xp->xp_context = context;
}
break;
case CMD_map:
case CMD_noremap:
case CMD_nmap:
case CMD_nnoremap:
case CMD_vmap:
case CMD_vnoremap:
case CMD_omap:
case CMD_onoremap:
case CMD_imap:
case CMD_inoremap:
case CMD_cmap:
case CMD_cnoremap:
case CMD_lmap:
case CMD_lnoremap:
case CMD_smap:
case CMD_snoremap:
case CMD_xmap:
case CMD_xnoremap:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
false, ea.cmdidx);
case CMD_unmap:
case CMD_nunmap:
case CMD_vunmap:
case CMD_ounmap:
case CMD_iunmap:
case CMD_cunmap:
case CMD_lunmap:
case CMD_sunmap:
case CMD_xunmap:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
true, ea.cmdidx);
case CMD_mapclear:
case CMD_nmapclear:
case CMD_vmapclear:
case CMD_omapclear:
case CMD_imapclear:
case CMD_cmapclear:
case CMD_lmapclear:
case CMD_smapclear:
case CMD_xmapclear:
xp->xp_context = EXPAND_MAPCLEAR;
xp->xp_pattern = (char *)arg;
break;
case CMD_abbreviate:
case CMD_noreabbrev:
case CMD_cabbrev:
case CMD_cnoreabbrev:
case CMD_iabbrev:
case CMD_inoreabbrev:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
false, ea.cmdidx);
case CMD_unabbreviate:
case CMD_cunabbrev:
case CMD_iunabbrev:
return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
true, ea.cmdidx);
case CMD_menu:
case CMD_noremenu:
case CMD_unmenu:
case CMD_amenu:
case CMD_anoremenu:
case CMD_aunmenu:
case CMD_nmenu:
case CMD_nnoremenu:
case CMD_nunmenu:
case CMD_vmenu:
case CMD_vnoremenu:
case CMD_vunmenu:
case CMD_omenu:
case CMD_onoremenu:
case CMD_ounmenu:
case CMD_imenu:
case CMD_inoremenu:
case CMD_iunmenu:
case CMD_cmenu:
case CMD_cnoremenu:
case CMD_cunmenu:
case CMD_tlmenu:
case CMD_tlnoremenu:
case CMD_tlunmenu:
case CMD_tmenu:
case CMD_tunmenu:
case CMD_popup:
case CMD_emenu:
return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
case CMD_colorscheme:
xp->xp_context = EXPAND_COLORS;
xp->xp_pattern = (char *)arg;
break;
case CMD_compiler:
xp->xp_context = EXPAND_COMPILER;
xp->xp_pattern = (char *)arg;
break;
case CMD_ownsyntax:
xp->xp_context = EXPAND_OWNSYNTAX;
xp->xp_pattern = (char *)arg;
break;
case CMD_setfiletype:
xp->xp_context = EXPAND_FILETYPE;
xp->xp_pattern = (char *)arg;
break;
case CMD_packadd:
xp->xp_context = EXPAND_PACKADD;
xp->xp_pattern = (char *)arg;
break;
#ifdef HAVE_WORKING_LIBINTL
case CMD_language:
p = (const char *)skiptowhite((const char_u *)arg);
if (*p == NUL) {
xp->xp_context = EXPAND_LANGUAGE;
xp->xp_pattern = (char *)arg;
} else {
if (strncmp(arg, "messages", (size_t)(p - arg)) == 0
|| strncmp(arg, "ctype", (size_t)(p - arg)) == 0
|| strncmp(arg, "time", (size_t)(p - arg)) == 0
|| strncmp(arg, "collate", (size_t)(p - arg)) == 0) {
xp->xp_context = EXPAND_LOCALES;
xp->xp_pattern = skipwhite(p);
} else {
xp->xp_context = EXPAND_NOTHING;
}
}
break;
#endif
case CMD_profile:
set_context_in_profile_cmd(xp, arg);
break;
case CMD_checkhealth:
xp->xp_context = EXPAND_CHECKHEALTH;
xp->xp_pattern = (char *)arg;
break;
case CMD_behave:
xp->xp_context = EXPAND_BEHAVE;
xp->xp_pattern = (char *)arg;
break;
case CMD_messages:
xp->xp_context = EXPAND_MESSAGES;
xp->xp_pattern = (char *)arg;
break;
case CMD_history:
xp->xp_context = EXPAND_HISTORY;
xp->xp_pattern = (char *)arg;
break;
case CMD_syntime:
xp->xp_context = EXPAND_SYNTIME;
xp->xp_pattern = (char *)arg;
break;
case CMD_argdelete:
while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) {
arg = (const char *)(xp->xp_pattern + 1);
}
xp->xp_context = EXPAND_ARGLIST;
xp->xp_pattern = (char *)arg;
break;
case CMD_lua:
xp->xp_context = EXPAND_LUA;
break;
default:
break;
}
return NULL;
uint32_t excmd_get_argt(cmdidx_T idx)
{
return cmdnames[(int)idx].cmd_argt;
}
/// Skip a range specifier of the form: addr [,addr] [;addr] ..
@ -4750,7 +3952,7 @@ static char *getargcmd(char **argp)
/// Find end of "+command" argument. Skip over "\ " and "\\".
///
/// @param rembs true to halve the number of backslashes
static char *skip_cmd_arg(char *p, int rembs)
char *skip_cmd_arg(char *p, int rembs)
{
while (*p && !ascii_isspace(*p)) {
if (*p == '\\' && p[1] != NUL) {
@ -7757,37 +6959,6 @@ static void ex_behave(exarg_T *eap)
}
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":behave {mswin,xterm}" command.
char *get_behave_arg(expand_T *xp, int idx)
{
if (idx == 0) {
return "mswin";
}
if (idx == 1) {
return "xterm";
}
return NULL;
}
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// ":messages {clear}" command.
char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
return "clear";
}
return NULL;
}
char *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
if (idx == 0) {
return "<buffer>";
}
return NULL;
}
static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;