aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/cmdexpand.c848
-rw-r--r--src/nvim/ex_docmd.c851
2 files changed, 856 insertions, 843 deletions
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index fee86ba43b..28806094d6 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -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;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index bc6a05568b..7bf272fbba 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -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.
-
- 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++;
- }
- }
- }
-
- //
- // 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 = 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);
- }
+ cmdidx_T idx;
- 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;
+ 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;
}
- 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;
+ return idx;
+}
- 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;