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.
+  * <SNR> 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 <SNR>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 <SNR>nr_
      // prefix.
***************
*** 2465,2479 ****
  
      if (xp->xp_context == EXPAND_SETTINGS
            || xp->xp_context == EXPAND_BOOL_SETTINGS)
!       ret = ExpandSettings(xp, &regmatch, num_file, file);
      else if (xp->xp_context == EXPAND_MAPPINGS)
!       ret = ExpandMappings(&regmatch, num_file, file);
  # if defined(FEAT_EVAL)
      else if (xp->xp_context == EXPAND_USER_DEFINED)
!       ret = ExpandUserDefined(xp, &regmatch, num_file, file);
  # endif
      else
!       ret = ExpandOther(xp, &regmatch, 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, &regmatch, numMatches, matches);
      else if (xp->xp_context == EXPAND_MAPPINGS)
!       ret = ExpandMappings(&regmatch, numMatches, matches);
  # if defined(FEAT_EVAL)
      else if (xp->xp_context == EXPAND_USER_DEFINED)
!       ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
  # endif
      else
!       ret = ExpandOther(xp, &regmatch, 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)
            // <SNR> 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)
            // <SNR> 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\<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')
***************
*** 344,349 ****
--- 352,361 ----
    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', @:)
***************
*** 362,367 ****
--- 374,394 ----
    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', @:)
***************
*** 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\<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
*** ../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 -- b...@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    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220217112734.688B71C0FE0%40moolenaar.net.

Raspunde prin e-mail lui