To: vim_dev@googlegroups.com Subject: Patch 8.2.4406 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4406 Problem: Expand functions use confusing argument names. Solution: Rename "file" to "match". Refactor some completion code. Add a few more tests. (Yegappan Lakshmanan, closes #9790) Files: src/cmdexpand.c, src/testdir/test_usercommands.vim *** ../vim-8.2.4405/src/cmdexpand.c 2022-02-16 12:44:26.129908861 +0000 --- src/cmdexpand.c 2022-02-17 11:22:52.229287488 +0000 *************** *** 17,30 **** static void set_expand_context(expand_T *xp); static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, ! int *num_file, char_u ***file, char_u *((*func)(expand_T *, int)), int escaped); ! static int ExpandFromContext(expand_T *xp, char_u *, int *, char_u ***, int); static int expand_showtail(expand_T *xp); ! static int expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int flagsarg); #if defined(FEAT_EVAL) ! static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file); ! static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file); #endif #ifdef FEAT_WILDMENU --- 17,30 ---- static void set_expand_context(expand_T *xp); static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, ! char_u ***matches, int *numMatches, char_u *((*func)(expand_T *, int)), int escaped); ! static int ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int); static int expand_showtail(expand_T *xp); ! static int expand_shellcmd(char_u *filepat, char_u ***matches, int *numMatches, int flagsarg); #if defined(FEAT_EVAL) ! static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches); ! static int ExpandUserList(expand_T *xp, char_u ***matches, int *numMatches); #endif #ifdef FEAT_WILDMENU *************** *** 37,44 **** static int compl_selected; #endif ! #define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(files_found[m]) : files_found[m]) static int sort_func_compare(const void *s1, const void *s2) { --- 37,48 ---- static int compl_selected; #endif ! #define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(matches[m]) : matches[m]) + /* + * sort function for the completion matches. + * functions should be sorted to the end. + */ static int sort_func_compare(const void *s1, const void *s2) { *************** *** 264,286 **** /* * Create and display a cmdline completion popup menu with items from ! * 'files_found'. */ static int cmdline_pum_create( cmdline_info_T *ccline, expand_T *xp, ! char_u **files_found, ! int num_files, int showtail) { int i; int columns; // Add all the completion matches ! compl_match_arraysize = num_files; compl_match_array = ALLOC_MULT(pumitem_T, compl_match_arraysize); ! for (i = 0; i < num_files; i++) { compl_match_array[i].pum_text = SHOW_FILE_TEXT(i); compl_match_array[i].pum_info = NULL; --- 268,290 ---- /* * Create and display a cmdline completion popup menu with items from ! * 'matches'. */ static int cmdline_pum_create( cmdline_info_T *ccline, expand_T *xp, ! char_u **matches, ! int numMatches, int showtail) { int i; int columns; // Add all the completion matches ! compl_match_arraysize = numMatches; compl_match_array = ALLOC_MULT(pumitem_T, compl_match_arraysize); ! for (i = 0; i < numMatches; i++) { compl_match_array[i].pum_text = SHOW_FILE_TEXT(i); compl_match_array[i].pum_info = NULL; *************** *** 293,300 **** columns = vim_strsize(xp->xp_pattern); if (showtail) { ! columns += vim_strsize(sm_gettail(files_found[0])); ! columns -= vim_strsize(files_found[0]); } if (columns >= compl_startcol) compl_startcol = 0; --- 297,304 ---- columns = vim_strsize(xp->xp_pattern); if (showtail) { ! columns += vim_strsize(sm_gettail(matches[0])); ! columns -= vim_strsize(matches[0]); } if (columns >= compl_startcol) compl_startcol = 0; *************** *** 423,429 **** char_u *ss = NULL; // Do the expansion. ! if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) { #ifdef FNAME_ILLEGAL --- 427,433 ---- char_u *ss = NULL; // Do the expansion. ! if (ExpandFromContext(xp, str, &xp->xp_files, &xp->xp_numfiles, options) == FAIL) { #ifdef FNAME_ILLEGAL *************** *** 445,452 **** 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 (xp->xp_numfiles) non_suf_match = xp->xp_numfiles; --- 449,455 ---- 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 (xp->xp_numfiles) non_suf_match = xp->xp_numfiles; *************** *** 684,691 **** /* * Display one line of completion matches. Multiple matches are displayed in * each line (used by wildmode=list and CTRL-D) ! * files_found - list of completion match names ! * num_files - number of completion matches in "files_found" * lines - number of output lines * linenr - line number of matches to display * maxlen - maximum number of characters in each line --- 687,694 ---- /* * Display one line of completion matches. Multiple matches are displayed in * each line (used by wildmode=list and CTRL-D) ! * matches - list of completion match names ! * numMatches - number of completion matches in "matches" * lines - number of output lines * linenr - line number of matches to display * maxlen - maximum number of characters in each line *************** *** 695,702 **** static void showmatches_oneline( expand_T *xp, ! char_u **files_found, ! int num_files, int lines, int linenr, int maxlen, --- 698,705 ---- static void showmatches_oneline( expand_T *xp, ! char_u **matches, ! int numMatches, int lines, int linenr, int maxlen, *************** *** 709,720 **** char_u *p; lastlen = 999; ! for (j = linenr; j < num_files; 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_advance(maxlen + 1); msg_puts((char *)p); msg_advance(maxlen + 3); --- 712,723 ---- char_u *p; lastlen = 999; ! for (j = linenr; j < numMatches; j += lines) { if (xp->xp_context == EXPAND_TAGS_LISTFILES) { ! msg_outtrans_attr(matches[j], HL_ATTR(HLF_D)); ! p = matches[j] + STRLEN(matches[j]) + 1; msg_advance(maxlen + 1); msg_puts((char *)p); msg_advance(maxlen + 3); *************** *** 737,759 **** // Expansion was done before and special characters // were escaped, need to halve backslashes. Also // $HOME has been replaced with ~/. ! exp_path = expand_env_save_opt(files_found[j], TRUE); ! path = exp_path != NULL ? exp_path : files_found[j]; halved_slash = backslash_halve_save(path); isdir = mch_isdir(halved_slash != NULL ? halved_slash ! : files_found[j]); vim_free(exp_path); if (halved_slash != path) vim_free(halved_slash); } else // Expansion was done here, file names are literal. ! isdir = mch_isdir(files_found[j]); if (showtail) p = SHOW_FILE_TEXT(j); else { ! home_replace(NULL, files_found[j], NameBuff, MAXPATHL, TRUE); p = NameBuff; } --- 740,762 ---- // Expansion was done before and special characters // were escaped, need to halve backslashes. Also // $HOME has been replaced with ~/. ! exp_path = expand_env_save_opt(matches[j], TRUE); ! path = exp_path != NULL ? exp_path : matches[j]; halved_slash = backslash_halve_save(path); isdir = mch_isdir(halved_slash != NULL ? halved_slash ! : matches[j]); vim_free(exp_path); if (halved_slash != path) vim_free(halved_slash); } else // Expansion was done here, file names are literal. ! isdir = mch_isdir(matches[j]); if (showtail) p = SHOW_FILE_TEXT(j); else { ! home_replace(NULL, matches[j], NameBuff, MAXPATHL, TRUE); p = NameBuff; } *************** *** 782,789 **** showmatches(expand_T *xp, int wildmenu UNUSED) { cmdline_info_T *ccline = get_cmdline_info(); ! int num_files; ! char_u **files_found; int i, j; int maxlen; int lines; --- 785,792 ---- showmatches(expand_T *xp, int wildmenu UNUSED) { cmdline_info_T *ccline = get_cmdline_info(); ! int numMatches; ! char_u **matches; int i, j; int maxlen; int lines; *************** *** 795,816 **** { set_expand_context(xp); i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos, ! &num_files, &files_found); showtail = expand_showtail(xp); if (i != EXPAND_OK) return i; } else { ! num_files = xp->xp_numfiles; ! files_found = xp->xp_files; showtail = cmd_showtail; } #ifdef FEAT_WILDMENU if (wildmenu && vim_strchr(p_wop, WOP_PUM) != NULL) // cmdline completion popup menu (with wildoptions=pum) ! return cmdline_pum_create(ccline, xp, files_found, num_files, showtail); #endif #ifdef FEAT_WILDMENU --- 798,819 ---- { set_expand_context(xp); i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos, ! &numMatches, &matches); showtail = expand_showtail(xp); if (i != EXPAND_OK) return i; } else { ! numMatches = xp->xp_numfiles; ! matches = xp->xp_files; showtail = cmd_showtail; } #ifdef FEAT_WILDMENU if (wildmenu && vim_strchr(p_wop, WOP_PUM) != NULL) // cmdline completion popup menu (with wildoptions=pum) ! return cmdline_pum_create(ccline, xp, matches, numMatches, showtail); #endif #ifdef FEAT_WILDMENU *************** *** 832,850 **** got_int = FALSE; // only int. the completion, not the cmd line #ifdef FEAT_WILDMENU else if (wildmenu) ! win_redr_status_matches(xp, num_files, files_found, -1, showtail); #endif else { // find the length of the longest file name maxlen = 0; ! for (i = 0; i < num_files; ++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); j = vim_strsize(NameBuff); } else --- 835,853 ---- got_int = FALSE; // only int. the completion, not the cmd line #ifdef FEAT_WILDMENU else if (wildmenu) ! win_redr_status_matches(xp, numMatches, matches, -1, showtail); #endif else { // find the length of the longest file name maxlen = 0; ! 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, matches[i], NameBuff, MAXPATHL, TRUE); j = vim_strsize(NameBuff); } else *************** *** 854,860 **** } if (xp->xp_context == EXPAND_TAGS_LISTFILES) ! lines = num_files; else { // compute the number of columns and lines for the listing --- 857,863 ---- } if (xp->xp_context == EXPAND_TAGS_LISTFILES) ! lines = numMatches; else { // compute the number of columns and lines for the listing *************** *** 862,868 **** columns = ((int)Columns + 2) / maxlen; if (columns < 1) columns = 1; ! lines = (num_files + columns - 1) / columns; } attr = HL_ATTR(HLF_D); // find out highlighting for directories --- 865,871 ---- columns = ((int)Columns + 2) / maxlen; if (columns < 1) columns = 1; ! lines = (numMatches + columns - 1) / columns; } attr = HL_ATTR(HLF_D); // find out highlighting for directories *************** *** 878,884 **** // list the files line by line for (i = 0; i < lines; ++i) { ! showmatches_oneline(xp, files_found, num_files, lines, i, maxlen, showtail, attr); if (got_int) { --- 881,887 ---- // list the files line by line for (i = 0; i < lines; ++i) { ! showmatches_oneline(xp, matches, numMatches, lines, i, maxlen, showtail, attr); if (got_int) { *************** *** 893,899 **** } if (xp->xp_numfiles == -1) ! FreeWild(num_files, files_found); return EXPAND_OK; } --- 896,902 ---- } if (xp->xp_numfiles == -1) ! FreeWild(numMatches, matches); return EXPAND_OK; } *************** *** 1362,1367 **** --- 1365,1435 ---- } /* + * Set the completion context for the :filter command. Returns a pointer to the + * next command after the :filter command. + */ + static char_u * + set_context_in_filter_cmd(expand_T *xp, char_u *arg) + { + if (*arg != NUL) + arg = skip_vimgrep_pat(arg, NULL, NULL); + if (arg == NULL || *arg == NUL) + { + xp->xp_context = EXPAND_NOTHING; + return NULL; + } + return skipwhite(arg); + } + + #ifdef FEAT_SEARCH_EXTRA + /* + * Set the completion context for the :match command. Returns a pointer to the + * next command after the :match command. + */ + static char_u * + set_context_in_match_cmd(expand_T *xp, char_u *arg) + { + if (*arg == NUL || !ends_excmd(*arg)) + { + // also complete "None" + set_context_in_echohl_cmd(xp, arg); + arg = skipwhite(skiptowhite(arg)); + if (*arg != NUL) + { + xp->xp_context = EXPAND_NOTHING; + arg = skip_regexp(arg + 1, *arg, magic_isset()); + } + } + return find_nextcmd(arg); + } + #endif + + /* + * Returns a pointer to the next command after a :global or a :v command. + * Returns NULL if there is no next command. + */ + static char_u * + find_cmd_after_global_cmd(char_u *arg) + { + int delim; + + delim = *arg; // get the delimiter + if (delim) + ++arg; // skip delimiter if there is one + + while (arg[0] != NUL && 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. */ *************** *** 1405,1411 **** * Returns NULL if there is no next command. */ static char_u * ! find_cmd_after_isearch_cmd(char_u *arg, expand_T *xp) { arg = skipwhite(skipdigits(arg)); // skip count if (*arg == '/') // Match regexp, not just whole words --- 1473,1479 ---- * Returns NULL if there is no next command. */ static char_u * ! find_cmd_after_isearch_cmd(expand_T *xp, char_u *arg) { arg = skipwhite(skipdigits(arg)); // skip count if (*arg == '/') // Match regexp, not just whole words *************** *** 1428,1433 **** --- 1496,1557 ---- return NULL; } + #ifdef FEAT_EVAL + /* + * Set the completion context for the :unlet command. Always returns NULL. + */ + static char_u * + set_context_in_unlet_cmd(expand_T *xp, char_u *arg) + { + while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) + arg = xp->xp_pattern + 1; + + xp->xp_context = EXPAND_USER_VARS; + xp->xp_pattern = arg; + + if (*xp->xp_pattern == '$') + { + xp->xp_context = EXPAND_ENV_VARS; + ++xp->xp_pattern; + } + + return NULL; + } + #endif + + #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + /* + * Set the completion context for the :language command. Always returns NULL. + */ + static char_u * + set_context_in_lang_cmd(expand_T *xp, char_u *arg) + { + char_u *p; + + p = skiptowhite(arg); + if (*p == NUL) + { + xp->xp_context = EXPAND_LANGUAGE; + xp->xp_pattern = arg; + } + else + { + if ( STRNCMP(arg, "messages", p - arg) == 0 + || STRNCMP(arg, "ctype", p - arg) == 0 + || STRNCMP(arg, "time", p - arg) == 0 + || STRNCMP(arg, "collate", p - arg) == 0) + { + xp->xp_context = EXPAND_LOCALES; + xp->xp_pattern = skipwhite(p); + } + else + xp->xp_context = EXPAND_NOTHING; + } + + return NULL; + } + #endif + /* * 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'. *************** *** 1440,1454 **** set_context_by_cmdname( char_u *cmd, cmdidx_T cmdidx, char_u *arg, long argt, int compl, - expand_T *xp, int forceit) { - char_u *p; - int delim; - switch (cmdidx) { case CMD_find: --- 1564,1575 ---- set_context_by_cmdname( char_u *cmd, cmdidx_T cmdidx, + expand_T *xp, char_u *arg, long argt, int compl, int forceit) { switch (cmdidx) { case CMD_find: *************** *** 1510,1538 **** return arg; case CMD_filter: ! if (*arg != NUL) ! arg = skip_vimgrep_pat(arg, NULL, NULL); ! if (arg == NULL || *arg == NUL) ! { ! xp->xp_context = EXPAND_NOTHING; ! return NULL; ! } ! return skipwhite(arg); #ifdef FEAT_SEARCH_EXTRA case CMD_match: ! if (*arg == NUL || !ends_excmd(*arg)) ! { ! // also complete "None" ! set_context_in_echohl_cmd(xp, arg); ! arg = skipwhite(skiptowhite(arg)); ! if (*arg != NUL) ! { ! xp->xp_context = EXPAND_NOTHING; ! arg = skip_regexp(arg + 1, *arg, magic_isset()); ! } ! } ! return find_nextcmd(arg); #endif // All completion for the +cmdline_compl feature goes here. --- 1631,1641 ---- return arg; case CMD_filter: ! return set_context_in_filter_cmd(xp, arg); #ifdef FEAT_SEARCH_EXTRA case CMD_match: ! return set_context_in_match_cmd(xp, arg); #endif // All completion for the +cmdline_compl feature goes here. *************** *** 1547,1565 **** case CMD_global: case CMD_vglobal: ! delim = *arg; // get the delimiter ! if (delim) ! ++arg; // skip delimiter if there is one ! ! while (arg[0] != NUL && 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: return find_cmd_after_substitute_cmd(arg); --- 1650,1656 ---- case CMD_global: case CMD_vglobal: ! return find_cmd_after_global_cmd(arg); case CMD_and: case CMD_substitute: return find_cmd_after_substitute_cmd(arg); *************** *** 1572,1578 **** case CMD_djump: case CMD_isplit: case CMD_dsplit: ! return find_cmd_after_isearch_cmd(arg, xp); case CMD_autocmd: return set_context_in_autocmd(xp, arg, FALSE); case CMD_doautocmd: --- 1663,1669 ---- case CMD_djump: case CMD_isplit: case CMD_dsplit: ! return find_cmd_after_isearch_cmd(xp, arg); case CMD_autocmd: return set_context_in_autocmd(xp, arg, FALSE); case CMD_doautocmd: *************** *** 1638,1657 **** break; case CMD_unlet: ! while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) ! arg = xp->xp_pattern + 1; ! ! xp->xp_context = EXPAND_USER_VARS; ! xp->xp_pattern = 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; --- 1729,1735 ---- break; case CMD_unlet: ! return set_context_in_unlet_cmd(xp, arg); case CMD_function: case CMD_delfunction: xp->xp_context = EXPAND_USER_FUNC; *************** *** 1795,1820 **** #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) case CMD_language: ! p = skiptowhite(arg); ! if (*p == NUL) ! { ! xp->xp_context = EXPAND_LANGUAGE; ! xp->xp_pattern = arg; ! } ! else ! { ! if ( STRNCMP(arg, "messages", p - arg) == 0 ! || STRNCMP(arg, "ctype", p - arg) == 0 ! || STRNCMP(arg, "time", p - arg) == 0 ! || STRNCMP(arg, "collate", p - arg) == 0) ! { ! xp->xp_context = EXPAND_LOCALES; ! xp->xp_pattern = skipwhite(p); ! } ! else ! xp->xp_context = EXPAND_NOTHING; ! } ! break; #endif #if defined(FEAT_PROFILE) case CMD_profile: --- 1873,1879 ---- #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) case CMD_language: ! return set_context_in_lang_cmd(xp, arg); #endif #if defined(FEAT_PROFILE) case CMD_profile: *************** *** 2041,2050 **** set_context_for_wildcard_arg(&ea, arg, usefilter, xp, &compl); // 6. Switch on command name. ! return set_context_by_cmdname(cmd, ea.cmdidx, arg, ea.argt, compl, xp, forceit); } void set_cmd_context( expand_T *xp, --- 2100,2112 ---- set_context_for_wildcard_arg(&ea, arg, usefilter, xp, &compl); // 6. Switch on command name. ! return set_context_by_cmdname(cmd, ea.cmdidx, xp, arg, ea.argt, compl, forceit); } + /* + * Set the completion context in 'xp' for command 'str' + */ void set_cmd_context( expand_T *xp, *************** *** 2134,2140 **** options += WILD_ICASE; // find all files that match the description ! if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) { *matchcount = 0; *matches = NULL; --- 2196,2202 ---- options += WILD_ICASE; // find all files that match the description ! if (ExpandFromContext(xp, file_str, matches, matchcount, options) == FAIL) { *matchcount = 0; *matches = NULL; *************** *** 2151,2158 **** expand_files_and_dirs( expand_T *xp, char_u *pat, ! char_u ***file, ! int *num_file, int flags, int options) { --- 2213,2220 ---- expand_files_and_dirs( expand_T *xp, char_u *pat, ! char_u ***matches, ! int *numMatches, int flags, int options) { *************** *** 2190,2196 **** flags |= EW_ICASE; // Expand wildcards, supporting %:h and the like. ! ret = expand_wildcards_eval(&pat, num_file, file, flags); if (free_pat) vim_free(pat); #ifdef BACKSLASH_IN_FILENAME --- 2252,2258 ---- flags |= EW_ICASE; // Expand wildcards, supporting %:h and the like. ! ret = expand_wildcards_eval(&pat, numMatches, matches, flags); if (free_pat) vim_free(pat); #ifdef BACKSLASH_IN_FILENAME *************** *** 2198,2206 **** { int j; ! for (j = 0; j < *num_file; ++j) { ! char_u *ptr = (*file)[j]; while (*ptr != NUL) { --- 2260,2268 ---- { int j; ! for (j = 0; j < *numMatches; ++j) { ! char_u *ptr = (*matches)[j]; while (*ptr != NUL) { *************** *** 2257,2264 **** ExpandOther( expand_T *xp, regmatch_T *rmp, ! int *num_file, ! char_u ***file) { static struct expgen { --- 2319,2326 ---- ExpandOther( expand_T *xp, regmatch_T *rmp, ! char_u ***matches, ! int *numMatches) { static struct expgen { *************** *** 2326,2332 **** { if (tab[i].ic) rmp->rm_ic = TRUE; ! ret = ExpandGeneric(xp, rmp, num_file, file, tab[i].func, tab[i].escaped); break; } --- 2388,2394 ---- { if (tab[i].ic) rmp->rm_ic = TRUE; ! ret = ExpandGeneric(xp, rmp, matches, numMatches, tab[i].func, tab[i].escaped); break; } *************** *** 2367,2374 **** ExpandFromContext( expand_T *xp, char_u *pat, ! int *num_file, ! char_u ***file, int options) // WILD_ flags { regmatch_T regmatch; --- 2429,2436 ---- ExpandFromContext( expand_T *xp, char_u *pat, ! char_u ***matches, ! int *numMatches, int options) // WILD_ flags { regmatch_T regmatch; *************** *** 2381,2399 **** 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); ! *file = (char_u **)""; ! *num_file = 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 ? (char_u *)"help" : pat, ! num_file, file, FALSE) == OK) { #ifdef FEAT_MULTI_LANG ! cleanup_help_tags(*num_file, *file); #endif return OK; } --- 2443,2462 ---- 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, matches, numMatches, flags, ! options); ! *matches = (char_u **)""; ! *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 ? (char_u *)"help" : pat, ! numMatches, matches, FALSE) == OK) { #ifdef FEAT_MULTI_LANG ! cleanup_help_tags(*numMatches, *matches); #endif return OK; } *************** *** 2401,2445 **** } if (xp->xp_context == EXPAND_SHELLCMD) ! return expand_shellcmd(pat, num_file, file, flags); if (xp->xp_context == EXPAND_OLD_SETTING) ! return ExpandOldSetting(num_file, file); if (xp->xp_context == EXPAND_BUFFERS) ! return ExpandBufnames(pat, num_file, file, options); #ifdef FEAT_DIFF if (xp->xp_context == EXPAND_DIFF_BUFFERS) ! return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER); #endif if (xp->xp_context == EXPAND_TAGS || xp->xp_context == EXPAND_TAGS_LISTFILES) ! return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file); if (xp->xp_context == EXPAND_COLORS) { char *directories[] = {"colors", NULL}; ! return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file, directories); } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = {"compiler", NULL}; ! return ExpandRTDir(pat, 0, num_file, file, directories); } if (xp->xp_context == EXPAND_OWNSYNTAX) { char *directories[] = {"syntax", NULL}; ! return ExpandRTDir(pat, 0, num_file, file, directories); } if (xp->xp_context == EXPAND_FILETYPE) { char *directories[] = {"syntax", "indent", "ftplugin", NULL}; ! return ExpandRTDir(pat, 0, num_file, file, directories); } # if defined(FEAT_EVAL) if (xp->xp_context == EXPAND_USER_LIST) ! return ExpandUserList(xp, num_file, file); # endif if (xp->xp_context == EXPAND_PACKADD) ! return ExpandPackAddDir(pat, num_file, file); // When expanding a function name starting with s:, match the nr_ // prefix. --- 2464,2510 ---- } if (xp->xp_context == EXPAND_SHELLCMD) ! return expand_shellcmd(pat, matches, numMatches, flags); if (xp->xp_context == EXPAND_OLD_SETTING) ! return ExpandOldSetting(numMatches, matches); if (xp->xp_context == EXPAND_BUFFERS) ! return ExpandBufnames(pat, numMatches, matches, options); #ifdef FEAT_DIFF if (xp->xp_context == EXPAND_DIFF_BUFFERS) ! return ExpandBufnames(pat, numMatches, matches, ! options | BUF_DIFF_FILTER); #endif if (xp->xp_context == EXPAND_TAGS || xp->xp_context == EXPAND_TAGS_LISTFILES) ! 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, numMatches, matches, directories); } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = {"compiler", NULL}; ! return ExpandRTDir(pat, 0, numMatches, matches, directories); } if (xp->xp_context == EXPAND_OWNSYNTAX) { char *directories[] = {"syntax", NULL}; ! return ExpandRTDir(pat, 0, numMatches, matches, directories); } if (xp->xp_context == EXPAND_FILETYPE) { char *directories[] = {"syntax", "indent", "ftplugin", NULL}; ! return ExpandRTDir(pat, 0, numMatches, matches, directories); } # if defined(FEAT_EVAL) if (xp->xp_context == EXPAND_USER_LIST) ! return ExpandUserList(xp, matches, numMatches); # endif if (xp->xp_context == EXPAND_PACKADD) ! return ExpandPackAddDir(pat, numMatches, matches); // When expanding a function name starting with s:, match the nr_ // prefix. *************** *** 2465,2479 **** if (xp->xp_context == EXPAND_SETTINGS || xp->xp_context == EXPAND_BOOL_SETTINGS) ! ret = ExpandSettings(xp, ®match, num_file, file); else if (xp->xp_context == EXPAND_MAPPINGS) ! ret = ExpandMappings(®match, num_file, file); # if defined(FEAT_EVAL) else if (xp->xp_context == EXPAND_USER_DEFINED) ! ret = ExpandUserDefined(xp, ®match, num_file, file); # endif else ! ret = ExpandOther(xp, ®match, num_file, file); vim_regfree(regmatch.regprog); vim_free(tofree); --- 2530,2544 ---- if (xp->xp_context == EXPAND_SETTINGS || xp->xp_context == EXPAND_BOOL_SETTINGS) ! ret = ExpandSettings(xp, ®match, numMatches, matches); else if (xp->xp_context == EXPAND_MAPPINGS) ! ret = ExpandMappings(®match, numMatches, matches); # if defined(FEAT_EVAL) else if (xp->xp_context == EXPAND_USER_DEFINED) ! ret = ExpandUserDefined(xp, ®match, matches, numMatches); # endif else ! ret = ExpandOther(xp, ®match, matches, numMatches); vim_regfree(regmatch.regprog); vim_free(tofree); *************** *** 2494,2501 **** ExpandGeneric( expand_T *xp, regmatch_T *regmatch, ! int *num_file, ! char_u ***file, char_u *((*func)(expand_T *, int)), // returns a string from the list int escaped) --- 2559,2566 ---- ExpandGeneric( expand_T *xp, regmatch_T *regmatch, ! char_u ***matches, ! int *numMatches, char_u *((*func)(expand_T *, int)), // returns a string from the list int escaped) *************** *** 2528,2539 **** str = vim_strsave(str); if (str == NULL) { ! FreeWild(count, *file); ! *num_file = 0; ! *file = NULL; return FAIL; } ! (*file)[count] = str; # ifdef FEAT_MENU if (func == get_menu_names && str != NULL) { --- 2593,2604 ---- str = vim_strsave(str); if (str == NULL) { ! FreeWild(count, *matches); ! *numMatches = 0; ! *matches = NULL; return FAIL; } ! (*matches)[count] = str; # ifdef FEAT_MENU if (func == get_menu_names && str != NULL) { *************** *** 2551,2564 **** { if (count == 0) return OK; ! *file = ALLOC_MULT(char_u *, count); ! if (*file == NULL) { ! *num_file = 0; ! *file = NULL; return FAIL; } ! *num_file = count; count = 0; } } --- 2616,2629 ---- { if (count == 0) return OK; ! *matches = ALLOC_MULT(char_u *, count); ! if (*matches == NULL) { ! *numMatches = 0; ! *matches = NULL; return FAIL; } ! *numMatches = count; count = 0; } } *************** *** 2571,2580 **** || xp->xp_context == EXPAND_USER_FUNC || xp->xp_context == EXPAND_DISASSEMBLE) // functions should be sorted to the end. ! qsort((void *)*file, (size_t)*num_file, sizeof(char_u *), sort_func_compare); else ! sort_strings(*file, *num_file); } #if defined(FEAT_SYN_HL) --- 2636,2645 ---- || xp->xp_context == EXPAND_USER_FUNC || xp->xp_context == EXPAND_DISASSEMBLE) // functions should be sorted to the end. ! qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *), sort_func_compare); else ! sort_strings(*matches, *numMatches); } #if defined(FEAT_SYN_HL) *************** *** 2594,2601 **** char_u *s, size_t l, char_u *pat, ! char_u ***files, ! int *num_files, int flags, hashtab_T *ht, garray_T *gap) --- 2659,2666 ---- char_u *s, size_t l, char_u *pat, ! char_u ***matches, ! int *numMatches, int flags, hashtab_T *ht, garray_T *gap) *************** *** 2611,2626 **** vim_strncpy(buf + l, pat, MAXPATHL - 1 - l); // Expand matches in one directory of $PATH. ! ret = expand_wildcards(1, &buf, num_files, files, flags); if (ret == OK) { ! if (ga_grow(gap, *num_files) == FAIL) ! FreeWild(*num_files, *files); else { ! for (i = 0; i < *num_files; ++i) { ! char_u *name = (*files)[i]; if (STRLEN(name) > l) { --- 2676,2691 ---- vim_strncpy(buf + l, pat, MAXPATHL - 1 - l); // Expand matches in one directory of $PATH. ! ret = expand_wildcards(1, &buf, numMatches, matches, flags); if (ret == OK) { ! if (ga_grow(gap, *numMatches) == FAIL) ! FreeWild(*numMatches, *matches); else { ! for (i = 0; i < *numMatches; ++i) { ! char_u *name = (*matches)[i]; if (STRLEN(name) > l) { *************** *** 2638,2644 **** } vim_free(name); } ! vim_free(*files); } } } --- 2703,2709 ---- } vim_free(name); } ! vim_free(*matches); } } } *************** *** 2650,2657 **** static int expand_shellcmd( char_u *filepat, // pattern to match with command names ! int *num_file, // return: number of matches ! char_u ***file, // return: array with matches int flagsarg) // EW_ flags { char_u *pat; --- 2715,2722 ---- static int expand_shellcmd( char_u *filepat, // pattern to match with command names ! char_u ***matches, // return: array with matches ! int *numMatches, // return: number of matches int flagsarg) // EW_ flags { char_u *pat; *************** *** 2732,2745 **** if (l > MAXPATHL - 5) break; ! expand_shellcmd_onedir(buf, s, l, pat, file, num_file, flags, &found_ht, &ga); if (*e != NUL) ++e; } ! *file = ga.ga_data; ! *num_file = ga.ga_len; vim_free(buf); vim_free(pat); --- 2797,2810 ---- if (l > MAXPATHL - 5) break; ! expand_shellcmd_onedir(buf, s, l, pat, matches, numMatches, flags, &found_ht, &ga); if (*e != NUL) ++e; } ! *matches = ga.ga_data; ! *numMatches = ga.ga_len; vim_free(buf); vim_free(pat); *************** *** 2757,2765 **** static void * call_user_expand_func( void *(*user_expand_func)(char_u *, int, typval_T *), ! expand_T *xp, ! int *num_file, ! char_u ***file) { cmdline_info_T *ccline = get_cmdline_info(); int keep = 0; --- 2822,2828 ---- static void * call_user_expand_func( void *(*user_expand_func)(char_u *, int, typval_T *), ! expand_T *xp) { cmdline_info_T *ccline = get_cmdline_info(); int keep = 0; *************** *** 2770,2777 **** 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) { --- 2833,2838 ---- *************** *** 2808,2815 **** ExpandUserDefined( expand_T *xp, regmatch_T *regmatch, ! int *num_file, ! char_u ***file) { char_u *retstr; char_u *s; --- 2869,2876 ---- ExpandUserDefined( expand_T *xp, regmatch_T *regmatch, ! char_u ***matches, ! int *numMatches) { char_u *retstr; char_u *s; *************** *** 2818,2824 **** garray_T ga; int skip; ! retstr = call_user_expand_func(call_func_retstr, xp, num_file, file); if (retstr == NULL) return FAIL; --- 2879,2887 ---- garray_T ga; int skip; ! *matches = NULL; ! *numMatches = 0; ! retstr = call_user_expand_func(call_func_retstr, xp); if (retstr == NULL) return FAIL; *************** *** 2846,2853 **** ++e; } vim_free(retstr); ! *file = ga.ga_data; ! *num_file = ga.ga_len; return OK; } --- 2909,2916 ---- ++e; } vim_free(retstr); ! *matches = ga.ga_data; ! *numMatches = ga.ga_len; return OK; } *************** *** 2857,2870 **** static int ExpandUserList( expand_T *xp, ! int *num_file, ! char_u ***file) { list_T *retlist; listitem_T *li; garray_T ga; ! retlist = call_user_expand_func(call_func_retlist, xp, num_file, file); if (retlist == NULL) return FAIL; --- 2920,2935 ---- static int ExpandUserList( expand_T *xp, ! char_u ***matches, ! int *numMatches) { list_T *retlist; listitem_T *li; garray_T ga; ! *matches = NULL; ! *numMatches = 0; ! retlist = call_user_expand_func(call_func_retlist, xp); if (retlist == NULL) return FAIL; *************** *** 2884,2891 **** } list_unref(retlist); ! *file = ga.ga_data; ! *num_file = ga.ga_len; return OK; } # endif --- 2949,2956 ---- } list_unref(retlist); ! *matches = ga.ga_data; ! *numMatches = ga.ga_len; return OK; } # endif *************** *** 2930,2936 **** add_pathsep(buf); # endif STRCAT(buf, file); ! if (ExpandFromContext(&xpc, buf, &num_p, &p, WILD_SILENT|expand_options) != FAIL && num_p > 0) { ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT|expand_options); --- 2995,3001 ---- add_pathsep(buf); # endif STRCAT(buf, file); ! if (ExpandFromContext(&xpc, buf, &p, &num_p, WILD_SILENT|expand_options) != FAIL && num_p > 0) { ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT|expand_options); *** ../vim-8.2.4405/src/testdir/test_usercommands.vim 2022-02-14 11:10:54.365486323 +0000 --- src/testdir/test_usercommands.vim 2022-02-17 11:22:52.229287488 +0000 *************** *** 333,338 **** --- 333,346 ---- call feedkeys(":com -complete=co\\\"\", 'tx') call assert_equal('"com -complete=color command compiler', @:) + " try completion for unsupported argument values + call feedkeys(":com -newarg=\\\"\", 'tx') + call assert_equal("\"com -newarg=\t", @:) + + " command completion after the name in a user defined command + call feedkeys(":com MyCmd chist\\\"\", 'tx') + call assert_equal("\"com MyCmd chistory", @:) + command! DoCmd1 : command! DoCmd2 : call feedkeys(":com \\\"\", 'tx') *************** *** 344,349 **** --- 352,361 ---- call feedkeys(":delcom DoC\\\"\", 'tx') call assert_equal('"delcom DoCmd1 DoCmd2', @:) + " try argument completion for a command without completion + call feedkeys(":DoCmd1 \\\"\", 'tx') + call assert_equal("\"DoCmd1 \t", @:) + delcom DoCmd1 call feedkeys(":delcom DoC\\\"\", 'tx') call assert_equal('"delcom DoCmd2', @:) *************** *** 362,367 **** --- 374,394 ---- call feedkeys(":DoCmd \\\"\", 'tx') call assert_equal('"DoCmd mswin xterm', @:) + " Test for file name completion + com! -nargs=1 -complete=file DoCmd : + call feedkeys(":DoCmd READM\\\"\", '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\\\"\", 'tx') + call assert_equal('"DoCmd BufForUserCmd', @:) + bwipe BufForUserCmd + call feedkeys(":DoCmd BufFor\\\"\", 'tx') + call assert_equal('"DoCmd BufFor', @:) + com! -nargs=* -complete=custom,CustomComplete DoCmd : call feedkeys(":DoCmd \\\"\", 'tx') call assert_equal('"DoCmd January February Mars', @:) *************** *** 745,749 **** --- 772,801 ---- 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\', 'xt')", 'E464:') + call feedkeys(":TestCmd \\\"\", '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 *** ../vim-8.2.4405/src/version.c 2022-02-16 21:50:53.966082550 +0000 --- src/version.c 2022-02-17 11:25:39.885127520 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4406, /**/ -- Be thankful to be in a traffic jam, because it means you own a car. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///