diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-01-14 23:44:38 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-14 23:44:38 +0800 |
commit | 8b86cb8f5b26699ead075cb6660c9236dfb5c495 (patch) | |
tree | e1f07d59b0ba794ddeaff4fccb173535a6a0e913 | |
parent | c1fffc42fbf32f5faac07de8fb7aab7943d1cc53 (diff) | |
download | rneovim-8b86cb8f5b26699ead075cb6660c9236dfb5c495.tar.gz rneovim-8b86cb8f5b26699ead075cb6660c9236dfb5c495.tar.bz2 rneovim-8b86cb8f5b26699ead075cb6660c9236dfb5c495.zip |
vim-patch:8.2.4406: expand functions use confusing argument names (#21800)
Problem: Expand functions use confusing argument names.
Solution: Rename "file" to "match". Refactor some completion code. Add a
few more tests. (Yegappan Lakshmanan, closes vim/vim#9790)
https://github.com/vim/vim/commit/24384308635f40b15d399fbd4e60c25d5e21ed8d
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
-rw-r--r-- | src/nvim/cmdexpand.c | 384 | ||||
-rw-r--r-- | src/nvim/testdir/test_usercommands.vim | 52 |
2 files changed, 260 insertions, 176 deletions
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 92a3a0717c..b5ec3e7032 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -90,9 +90,11 @@ static int compl_startcol; static int compl_selected; #define SHOW_FILE_TEXT(m) (showtail \ - ? showmatches_gettail(files_found[m], false) \ - : files_found[m]) + ? showmatches_gettail(matches[m], false) \ + : matches[m]) +/// Sort function for the completion matches. +/// <SNR> functions should be sorted to the end. static int sort_func_compare(const void *s1, const void *s2) { char *p1 = *(char **)s1; @@ -284,15 +286,15 @@ int nextwild(expand_T *xp, int type, int options, bool escape) } /// Create and display a cmdline completion popup menu with items from -/// "files_found". -static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **files_found, int num_files, +/// "matches". +static int cmdline_pum_create(CmdlineInfo *ccline, expand_T *xp, char **matches, int numMatches, int showtail) { - assert(num_files >= 0); + assert(numMatches >= 0); // Add all the completion matches - compl_match_arraysize = num_files; + compl_match_arraysize = numMatches; compl_match_array = xmalloc(sizeof(pumitem_T) * (size_t)compl_match_arraysize); - for (int i = 0; i < num_files; i++) { + for (int i = 0; i < numMatches; i++) { compl_match_array[i] = (pumitem_T){ .pum_text = SHOW_FILE_TEXT(i), .pum_info = NULL, @@ -628,7 +630,7 @@ static char *ExpandOne_start(int mode, expand_T *xp, char *str, int options) char *ss = NULL; // Do the expansion. - if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) { + if (ExpandFromContext(xp, str, &xp->xp_files, &xp->xp_numfiles, options) == FAIL) { #ifdef FNAME_ILLEGAL // Illegal file name has been silently skipped. But when there // are wildcards, the real problem is that there was no match, @@ -646,8 +648,7 @@ static char *ExpandOne_start(int mode, expand_T *xp, char *str, int options) ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options); // Check for matching suffixes in file names. - if (mode != WILD_ALL && mode != WILD_ALL_KEEP - && mode != WILD_LONGEST) { + if (mode != WILD_ALL && mode != WILD_ALL_KEEP && mode != WILD_LONGEST) { if (xp->xp_numfiles) { non_suf_match = xp->xp_numfiles; } else { @@ -862,22 +863,22 @@ void ExpandCleanup(expand_T *xp) /// Display one line of completion matches. Multiple matches are displayed in /// each line (used by wildmode=list and CTRL-D) /// -/// @param files_found list of completion match names -/// @param num_files number of completion matches in "files_found" +/// @param matches list of completion match names +/// @param numMatches number of completion matches in "matches" /// @param lines number of output lines /// @param linenr line number of matches to display /// @param maxlen maximum number of characters in each line /// @param showtail display only the tail of the full path of a file name /// @param dir_attr highlight attribute to use for directory names -static void showmatches_oneline(expand_T *xp, char **files_found, int num_files, int lines, - int linenr, int maxlen, int showtail, int dir_attr) +static void showmatches_oneline(expand_T *xp, char **matches, int numMatches, int lines, int linenr, + int maxlen, int showtail, int dir_attr) { char *p; int lastlen = 999; - for (int j = linenr; j < num_files; j += lines) { + for (int j = linenr; j < numMatches; j += lines) { if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - msg_outtrans_attr(files_found[j], HL_ATTR(HLF_D)); - p = files_found[j] + strlen(files_found[j]) + 1; + msg_outtrans_attr(matches[j], HL_ATTR(HLF_D)); + p = matches[j] + strlen(matches[j]) + 1; msg_advance(maxlen + 1); msg_puts((const char *)p); msg_advance(maxlen + 3); @@ -896,8 +897,8 @@ static void showmatches_oneline(expand_T *xp, char **files_found, int num_files, // Expansion was done before and special characters // were escaped, need to halve backslashes. Also // $HOME has been replaced with ~/. - char *exp_path = (char *)expand_env_save_opt(files_found[j], true); - char *path = exp_path != NULL ? exp_path : files_found[j]; + char *exp_path = (char *)expand_env_save_opt(matches[j], true); + char *path = exp_path != NULL ? exp_path : matches[j]; char *halved_slash = backslash_halve_save(path); isdir = os_isdir(halved_slash); xfree(exp_path); @@ -906,12 +907,12 @@ static void showmatches_oneline(expand_T *xp, char **files_found, int num_files, } } else { // Expansion was done here, file names are literal. - isdir = os_isdir(files_found[j]); + isdir = os_isdir(matches[j]); } if (showtail) { p = SHOW_FILE_TEXT(j); } else { - home_replace(NULL, files_found[j], NameBuff, MAXPATHL, true); + home_replace(NULL, matches[j], NameBuff, MAXPATHL, true); p = NameBuff; } } else { @@ -932,8 +933,8 @@ static void showmatches_oneline(expand_T *xp, char **files_found, int num_files, int showmatches(expand_T *xp, int wildmenu) { CmdlineInfo *const ccline = get_cmdline_info(); - int num_files; - char **files_found; + int numMatches; + char **matches; int i, j; int maxlen; int lines; @@ -944,14 +945,14 @@ int showmatches(expand_T *xp, int wildmenu) if (xp->xp_numfiles == -1) { set_expand_context(xp); i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos, - &num_files, &files_found); + &numMatches, &matches); showtail = expand_showtail(xp); if (i != EXPAND_OK) { return i; } } else { - num_files = xp->xp_numfiles; - files_found = xp->xp_files; + numMatches = xp->xp_numfiles; + matches = xp->xp_files; showtail = cmd_showtail; } @@ -962,7 +963,7 @@ int showmatches(expand_T *xp, int wildmenu) if (compl_use_pum) { // cmdline completion popup menu (with wildoptions=pum) - return cmdline_pum_create(ccline, xp, files_found, num_files, showtail); + return cmdline_pum_create(ccline, xp, matches, numMatches, showtail); } if (!wildmenu) { @@ -978,15 +979,15 @@ int showmatches(expand_T *xp, int wildmenu) if (got_int) { got_int = false; // only int. the completion, not the cmd line } else if (wildmenu) { - redraw_wildmenu(xp, num_files, files_found, -1, showtail); + redraw_wildmenu(xp, numMatches, matches, -1, showtail); } else { // find the length of the longest file name maxlen = 0; - for (i = 0; i < num_files; i++) { + for (i = 0; i < numMatches; i++) { if (!showtail && (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_SHELLCMD || xp->xp_context == EXPAND_BUFFERS)) { - home_replace(NULL, files_found[i], NameBuff, MAXPATHL, true); + home_replace(NULL, matches[i], NameBuff, MAXPATHL, true); j = vim_strsize(NameBuff); } else { j = vim_strsize(SHOW_FILE_TEXT(i)); @@ -997,7 +998,7 @@ int showmatches(expand_T *xp, int wildmenu) } if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - lines = num_files; + lines = numMatches; } else { // compute the number of columns and lines for the listing maxlen += 2; // two spaces between file names @@ -1005,7 +1006,7 @@ int showmatches(expand_T *xp, int wildmenu) if (columns < 1) { columns = 1; } - lines = (num_files + columns - 1) / columns; + lines = (numMatches + columns - 1) / columns; } attr = HL_ATTR(HLF_D); // find out highlighting for directories @@ -1019,7 +1020,7 @@ int showmatches(expand_T *xp, int wildmenu) // list the files line by line for (i = 0; i < lines; i++) { - showmatches_oneline(xp, files_found, num_files, lines, i, maxlen, showtail, attr); + showmatches_oneline(xp, matches, numMatches, lines, i, maxlen, showtail, attr); if (got_int) { got_int = false; break; @@ -1032,7 +1033,7 @@ int showmatches(expand_T *xp, int wildmenu) } if (xp->xp_numfiles == -1) { - FreeWild(num_files, files_found); + FreeWild(numMatches, matches); } return EXPAND_OK; @@ -1457,6 +1458,58 @@ static void set_context_for_wildcard_arg(exarg_T *eap, const char *arg, bool use } } +/// Set the completion context for the :filter command. Returns a pointer to the +/// next command after the :filter command. +static const char *set_context_in_filter_cmd(expand_T *xp, const char *arg) +{ + 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); +} + +/// Set the completion context for the :match command. Returns a pointer to the +/// next command after the :match command. +static const char *set_context_in_match_cmd(expand_T *xp, const char *arg) +{ + if (*arg == NUL || !ends_excmd(*arg)) { + // also complete "None" + set_context_in_echohl_cmd(xp, arg); + arg = (const char *)skipwhite(skiptowhite(arg)); + if (*arg != NUL) { + xp->xp_context = EXPAND_NOTHING; + arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), magic_isset()); + } + } + return (const char *)find_nextcmd(arg); +} + +/// Returns a pointer to the next command after a :global or a :v command. +/// Returns NULL if there is no next command. +static const char *find_cmd_after_global_cmd(const char *arg) +{ + 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; + } + + return NULL; +} + /// Returns a pointer to the next command after a :substitute or a :& command. /// Returns NULL if there is no next command. static const char *find_cmd_after_substitute_cmd(const char *arg) @@ -1494,7 +1547,7 @@ static const char *find_cmd_after_substitute_cmd(const char *arg) /// Returns a pointer to the next command after a :isearch/:dsearch/:ilist /// :dlist/:ijump/:psearch/:djump/:isplit/:dsplit command. /// Returns NULL if there is no next command. -static const char *find_cmd_after_isearch_cmd(const char *arg, expand_T *xp) +static const char *find_cmd_after_isearch_cmd(expand_T *xp, const char *arg) { // Skip count. arg = (const char *)skipwhite(skipdigits(arg)); @@ -1522,17 +1575,55 @@ static const char *find_cmd_after_isearch_cmd(const char *arg, expand_T *xp) return NULL; } +/// Set the completion context for the :unlet command. Always returns NULL. +static const char *set_context_in_unlet_cmd(expand_T *xp, const char *arg) +{ + 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++; + } + + return NULL; +} + +/// Set the completion context for the :language command. Always returns NULL. +static const char *set_context_in_lang_cmd(expand_T *xp, const char *arg) +{ + const char *p = (const char *)skiptowhite(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; + } + } + + return NULL; +} + /// Set the completion context in "xp" for command "cmd" with index "cmdidx". /// The argument to the command is "arg" and the argument flags is "argt". /// For user-defined commands and for environment variables, "context" has the /// completion type. /// /// @return a pointer to the next command, or NULL if there is no next command. -static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, const char *arg, - uint32_t argt, int context, expand_T *xp, bool forceit) +static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expand_T *xp, + const char *arg, uint32_t argt, int context, bool forceit) { - const char *p; - switch (cmdidx) { case CMD_find: case CMD_sfind: @@ -1594,26 +1685,10 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons 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); + return set_context_in_filter_cmd(xp, arg); case CMD_match: - if (*arg == NUL || !ends_excmd(*arg)) { - // also complete "None" - set_context_in_echohl_cmd(xp, arg); - arg = (const char *)skipwhite(skiptowhite(arg)); - if (*arg != NUL) { - xp->xp_context = EXPAND_NOTHING; - arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), magic_isset()); - } - } - return (const char *)find_nextcmd(arg); + return set_context_in_match_cmd(xp, arg); // All completion for the +cmdline_compl feature goes here. @@ -1626,23 +1701,8 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons 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_vglobal: + return find_cmd_after_global_cmd(arg); case CMD_and: case CMD_substitute: return find_cmd_after_substitute_cmd(arg); @@ -1655,7 +1715,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons case CMD_djump: case CMD_isplit: case CMD_dsplit: - return find_cmd_after_isearch_cmd(arg, xp); + return find_cmd_after_isearch_cmd(xp, arg); case CMD_autocmd: return (const char *)set_context_in_autocmd(xp, (char *)arg, false); @@ -1718,20 +1778,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons 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; - + return set_context_in_unlet_cmd(xp, arg); case CMD_function: case CMD_delfunction: xp->xp_context = EXPAND_USER_FUNC; @@ -1886,22 +1933,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons #ifdef HAVE_WORKING_LIBINTL case CMD_language: - p = (const char *)skiptowhite(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; + return set_context_in_lang_cmd(xp, arg); #endif case CMD_profile: set_context_in_profile_cmd(xp, arg); @@ -2120,9 +2152,11 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff) } // Switch on command name. - return set_context_by_cmdname(cmd, ea.cmdidx, arg, ea.argt, context, xp, forceit); + return set_context_by_cmdname(cmd, ea.cmdidx, xp, arg, ea.argt, context, forceit); } +/// Set the completion context in "xp" for command "str" +/// /// @param str start of command line /// @param len length of command line (excl. NUL) /// @param col position of cursor @@ -2199,7 +2233,7 @@ int expand_cmdline(expand_T *xp, const char *str, int col, int *matchcount, char } // find all files that match the description - if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) { + if (ExpandFromContext(xp, file_str, matches, matchcount, options) == FAIL) { *matchcount = 0; *matches = NULL; } @@ -2209,8 +2243,8 @@ int expand_cmdline(expand_T *xp, const char *str, int col, int *matchcount, char } /// Expand file or directory names. -static int expand_files_and_dirs(expand_T *xp, char *pat, char ***file, int *num_file, int flags, - int options) +static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *numMatches, + int flags, int options) { bool free_pat = false; @@ -2246,14 +2280,14 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***file, int *num } // Expand wildcards, supporting %:h and the like. - int ret = expand_wildcards_eval(&pat, num_file, file, flags); + int ret = expand_wildcards_eval(&pat, numMatches, matches, flags); if (free_pat) { xfree(pat); } #ifdef BACKSLASH_IN_FILENAME if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) { - for (int j = 0; j < *num_file; j++) { - char *ptr = (*file)[j]; + for (int j = 0; j < *numMatches; j++) { + char *ptr = (*matches)[j]; while (*ptr != NUL) { if (p_csl[0] == 's' && *ptr == '\\') { *ptr = '/'; @@ -2326,7 +2360,7 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx) } /// Do the expansion based on xp->xp_context and "rmp". -static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***file) +static int ExpandOther(expand_T *xp, regmatch_T *rmp, char ***matches, int *numMatches) { typedef CompleteListItemGetter ExpandFunc; static struct expgen { @@ -2376,7 +2410,7 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil if (tab[i].ic) { rmp->rm_ic = true; } - ExpandGeneric(xp, rmp, num_file, file, tab[i].func, tab[i].escaped); + ExpandGeneric(xp, rmp, matches, numMatches, tab[i].func, tab[i].escaped); ret = OK; break; } @@ -2414,7 +2448,7 @@ static int map_wildopts_to_ewflags(int options) /// Do the expansion based on xp->xp_context and "pat". /// /// @param options WILD_ flags -static int ExpandFromContext(expand_T *xp, char *pat, int *num_file, char ***file, int options) +static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numMatches, int options) { regmatch_T regmatch; int ret; @@ -2423,65 +2457,64 @@ static int ExpandFromContext(expand_T *xp, char *pat, int *num_file, char ***fil if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES || xp->xp_context == EXPAND_FILES_IN_PATH) { - return expand_files_and_dirs(xp, pat, file, num_file, flags, options); + return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options); } - *file = NULL; - *num_file = 0; + *matches = NULL; + *numMatches = 0; if (xp->xp_context == EXPAND_HELP) { // With an empty argument we would get all the help tags, which is // very slow. Get matches for "help" instead. if (find_help_tags(*pat == NUL ? "help" : pat, - num_file, file, false) == OK) { - cleanup_help_tags(*num_file, *file); + numMatches, matches, false) == OK) { + cleanup_help_tags(*numMatches, *matches); return OK; } return FAIL; } if (xp->xp_context == EXPAND_SHELLCMD) { - *file = NULL; - expand_shellcmd(pat, num_file, file, flags); + expand_shellcmd(pat, matches, numMatches, flags); return OK; } if (xp->xp_context == EXPAND_OLD_SETTING) { - ExpandOldSetting(num_file, file); + ExpandOldSetting(numMatches, matches); return OK; } if (xp->xp_context == EXPAND_BUFFERS) { - return ExpandBufnames(pat, num_file, file, options); + return ExpandBufnames(pat, numMatches, matches, options); } if (xp->xp_context == EXPAND_DIFF_BUFFERS) { - return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER); + return ExpandBufnames(pat, numMatches, matches, options | BUF_DIFF_FILTER); } if (xp->xp_context == EXPAND_TAGS || xp->xp_context == EXPAND_TAGS_LISTFILES) { - return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file); + return expand_tags(xp->xp_context == EXPAND_TAGS, pat, numMatches, matches); } if (xp->xp_context == EXPAND_COLORS) { char *directories[] = { "colors", NULL }; - return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories); + return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, numMatches, matches, directories); } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = { "compiler", NULL }; - return ExpandRTDir(pat, DIP_LUA, num_file, file, directories); + return ExpandRTDir(pat, DIP_LUA, numMatches, matches, directories); } if (xp->xp_context == EXPAND_OWNSYNTAX) { char *directories[] = { "syntax", NULL }; - return ExpandRTDir(pat, 0, num_file, file, directories); + return ExpandRTDir(pat, 0, numMatches, matches, directories); } if (xp->xp_context == EXPAND_FILETYPE) { char *directories[] = { "syntax", "indent", "ftplugin", NULL }; - return ExpandRTDir(pat, DIP_LUA, num_file, file, directories); + return ExpandRTDir(pat, DIP_LUA, numMatches, matches, directories); } if (xp->xp_context == EXPAND_USER_LIST) { - return ExpandUserList(xp, num_file, file); + return ExpandUserList(xp, matches, numMatches); } if (xp->xp_context == EXPAND_USER_LUA) { - return ExpandUserLua(xp, num_file, file); + return ExpandUserLua(xp, numMatches, matches); } if (xp->xp_context == EXPAND_PACKADD) { - return ExpandPackAddDir(pat, num_file, file); + return ExpandPackAddDir(pat, numMatches, matches); } // When expanding a function name starting with s:, match the <SNR>nr_ @@ -2497,7 +2530,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, int *num_file, char ***fil if (xp->xp_context == EXPAND_LUA) { ILOG("PAT %s", pat); - return nlua_expand_pat(xp, pat, num_file, file); + return nlua_expand_pat(xp, pat, numMatches, matches); } regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0); @@ -2510,13 +2543,13 @@ static int ExpandFromContext(expand_T *xp, char *pat, int *num_file, char ***fil if (xp->xp_context == EXPAND_SETTINGS || xp->xp_context == EXPAND_BOOL_SETTINGS) { - ret = ExpandSettings(xp, ®match, num_file, file); + ret = ExpandSettings(xp, ®match, numMatches, matches); } else if (xp->xp_context == EXPAND_MAPPINGS) { - ret = ExpandMappings(®match, num_file, file); + ret = ExpandMappings(®match, numMatches, matches); } else if (xp->xp_context == EXPAND_USER_DEFINED) { - ret = ExpandUserDefined(xp, ®match, num_file, file); + ret = ExpandUserDefined(xp, ®match, matches, numMatches); } else { - ret = ExpandOther(xp, ®match, num_file, file); + ret = ExpandOther(xp, ®match, matches, numMatches); } vim_regfree(regmatch.regprog); @@ -2532,7 +2565,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, int *num_file, char ***fil /// program. Matching strings are copied into an array, which is returned. /// /// @param func returns a string from the list -static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file, +static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, char ***matches, int *numMatches, CompleteListItemGetter func, int escaped) { int i; @@ -2556,8 +2589,8 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha return; } assert(count < INT_MAX); - *num_file = (int)count; - *file = xmalloc(count * sizeof(char *)); + *numMatches = (int)count; + *matches = xmalloc(count * sizeof(char *)); // copy the matching names into allocated memory count = 0; @@ -2575,7 +2608,7 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha } else { str = xstrdup(str); } - (*file)[count++] = str; + (*matches)[count++] = str; if (func == get_menu_names) { // Test for separator added by get_menu_names(). str += strlen(str) - 1; @@ -2592,9 +2625,9 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha || xp->xp_context == EXPAND_FUNCTIONS || xp->xp_context == EXPAND_USER_FUNC) { // <SNR> functions should be sorted to the end. - qsort((void *)(*file), (size_t)(*num_file), sizeof(char *), sort_func_compare); + qsort((void *)(*matches), (size_t)(*numMatches), sizeof(char *), sort_func_compare); } else { - sort_strings(*file, *num_file); + sort_strings(*matches, *numMatches); } } @@ -2604,8 +2637,8 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha } /// Expand shell command matches in one directory of $PATH. -static void expand_shellcmd_onedir(char *buf, char *s, size_t l, char *pat, char ***files, - int *num_files, int flags, hashtab_T *ht, garray_T *gap) +static void expand_shellcmd_onedir(char *buf, char *s, size_t l, char *pat, char ***matches, + int *numMatches, int flags, hashtab_T *ht, garray_T *gap) { xstrlcpy(buf, s, l + 1); add_pathsep(buf); @@ -2613,12 +2646,12 @@ static void expand_shellcmd_onedir(char *buf, char *s, size_t l, char *pat, char xstrlcpy(buf + l, pat, MAXPATHL - l); // Expand matches in one directory of $PATH. - int ret = expand_wildcards(1, &buf, num_files, files, flags); + int ret = expand_wildcards(1, &buf, numMatches, matches, flags); if (ret == OK) { - ga_grow(gap, *num_files); + ga_grow(gap, *numMatches); { - for (int i = 0; i < *num_files; i++) { - char *name = (*files)[i]; + for (int i = 0; i < *numMatches; i++) { + char *name = (*matches)[i]; if (strlen(name) > l) { // Check if this name was already found. @@ -2635,20 +2668,20 @@ static void expand_shellcmd_onedir(char *buf, char *s, size_t l, char *pat, char } xfree(name); } - xfree(*files); + xfree(*matches); } } } /// Complete a shell command. /// -/// @param filepat is a pattern to match with command names. -/// @param[out] num_file is pointer to number of matches. -/// @param[out] file is pointer to array of pointers to matches. -/// *file will either be set to NULL or point to -/// allocated memory. -/// @param flagsarg is a combination of EW_* flags. -static void expand_shellcmd(char *filepat, int *num_file, char ***file, int flagsarg) +/// @param filepat is a pattern to match with command names. +/// @param[out] matches is pointer to array of pointers to matches. +/// *matches will either be set to NULL or point to +/// allocated memory. +/// @param[out] numMatches is pointer to number of matches. +/// @param flagsarg is a combination of EW_* flags. +static void expand_shellcmd(char *filepat, char ***matches, int *numMatches, int flagsarg) FUNC_ATTR_NONNULL_ALL { char *pat; @@ -2718,13 +2751,13 @@ static void expand_shellcmd(char *filepat, int *num_file, char ***file, int flag if (l > MAXPATHL - 5) { break; } - expand_shellcmd_onedir(buf, s, l, pat, file, num_file, flags, &found_ht, &ga); + expand_shellcmd_onedir(buf, s, l, pat, matches, numMatches, flags, &found_ht, &ga); if (*e != NUL) { e++; } } - *file = ga.ga_data; - *num_file = ga.ga_len; + *matches = ga.ga_data; + *numMatches = ga.ga_len; xfree(buf); xfree(pat); @@ -2736,8 +2769,7 @@ static void expand_shellcmd(char *filepat, int *num_file, char ***file, int flag /// Call "user_expand_func()" to invoke a user defined Vim script function and /// return the result (either a string, a List or NULL). -static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file, - char ***file) +static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp) FUNC_ATTR_NONNULL_ALL { CmdlineInfo *const ccline = get_cmdline_info(); @@ -2749,8 +2781,6 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) { return NULL; } - *num_file = 0; - *file = NULL; if (ccline->cmdbuff != NULL) { keep = ccline->cmdbuff[ccline->cmdlen]; @@ -2780,13 +2810,14 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T } /// Expand names with a function defined by the user. -static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file) +static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, char ***matches, int *numMatches) { char *e; garray_T ga; - char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, num_file, - file); + *matches = NULL; + *numMatches = 0; + char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp); if (retstr == NULL) { return FAIL; @@ -2812,16 +2843,17 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, } } xfree(retstr); - *file = ga.ga_data; - *num_file = ga.ga_len; + *matches = ga.ga_data; + *numMatches = ga.ga_len; return OK; } /// Expand names with a list returned by a function defined by the user. -static int ExpandUserList(expand_T *xp, int *num_file, char ***file) +static int ExpandUserList(expand_T *xp, char ***matches, int *numMatches) { - list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, num_file, - file); + *matches = NULL; + *numMatches = 0; + list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp); if (retlist == NULL) { return FAIL; } @@ -2839,8 +2871,8 @@ static int ExpandUserList(expand_T *xp, int *num_file, char ***file) }); tv_list_unref(retlist); - *file = ga.ga_data; - *num_file = ga.ga_len; + *matches = ga.ga_data; + *numMatches = ga.ga_len; return OK; } @@ -2893,7 +2925,7 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options) char **p; int num_p = 0; - (void)ExpandFromContext(&xpc, buf, &num_p, &p, + (void)ExpandFromContext(&xpc, buf, &p, &num_p, WILD_SILENT | expand_options); if (num_p > 0) { ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options); diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 3ae990e022..6910361345 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -354,6 +354,14 @@ func Test_CmdCompletion() call feedkeys(":com -complete=co\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"com -complete=color command compiler', @:) + " try completion for unsupported argument values + call feedkeys(":com -newarg=\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"com -newarg=\t", @:) + + " command completion after the name in a user defined command + call feedkeys(":com MyCmd chist\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"com MyCmd chistory", @:) + command! DoCmd1 : command! DoCmd2 : call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx') @@ -365,6 +373,10 @@ func Test_CmdCompletion() call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"delcom DoCmd1 DoCmd2', @:) + " try argument completion for a command without completion + call feedkeys(":DoCmd1 \<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal("\"DoCmd1 \t", @:) + delcom DoCmd1 call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"delcom DoCmd2', @:) @@ -383,6 +395,21 @@ func Test_CmdCompletion() call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"DoCmd mswin xterm', @:) + " Test for file name completion + com! -nargs=1 -complete=file DoCmd : + call feedkeys(":DoCmd READM\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd README.txt', @:) + + " Test for buffer name completion + com! -nargs=1 -complete=buffer DoCmd : + let bnum = bufadd('BufForUserCmd') + call setbufvar(bnum, '&buflisted', 1) + call feedkeys(":DoCmd BufFor\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd BufForUserCmd', @:) + bwipe BufForUserCmd + call feedkeys(":DoCmd BufFor\<Tab>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd BufFor', @:) + com! -nargs=* -complete=custom,CustomComplete DoCmd : call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"DoCmd January February Mars', @:) @@ -694,5 +721,30 @@ func Test_recursive_define() endwhile endfunc +" Test for using buffer-local ambiguous user-defined commands +func Test_buflocal_ambiguous_usercmd() + new + command -buffer -nargs=1 -complete=sign TestCmd1 echo "Hello" + command -buffer -nargs=1 -complete=sign TestCmd2 echo "World" + + call assert_fails("call feedkeys(':TestCmd\<CR>', 'xt')", 'E464:') + call feedkeys(":TestCmd \<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"TestCmd ', @:) + + delcommand TestCmd1 + delcommand TestCmd2 + bw! +endfunc + +" Test for using a multibyte character in a user command +func Test_multibyte_in_usercmd() + command SubJapanesePeriodToDot exe "%s/\u3002/./g" + new + call setline(1, "Hello\u3002") + SubJapanesePeriodToDot + call assert_equal('Hello.', getline(1)) + bw! + delcommand SubJapanesePeriodToDot +endfunc " vim: shiftwidth=2 sts=2 expandtab |