Patch 8.2.4356
Problem:    Command line completion functions are very long.
Solution:   Refactor into multiple functions. (Yegappan Lakshmanan,
            closes #9753)
Files:      src/cmdexpand.c


*** ../vim-8.2.4355/src/cmdexpand.c     2022-02-10 21:09:26.387235267 +0000
--- src/cmdexpand.c     2022-02-12 12:00:45.831907799 +0000
***************
*** 351,356 ****
--- 351,539 ----
  #endif
  
  /*
+  * Get the next or prev cmdline completion match. The index of the match is 
set
+  * in 'p_findex'
+  */
+     static char_u *
+ get_next_or_prev_match(
+       int             mode,
+       expand_T        *xp,
+       int             *p_findex,
+       char_u          *orig_save)
+ {
+     int findex = *p_findex;
+ 
+     if (xp->xp_numfiles <= 0)
+       return NULL;
+ 
+     if (mode == WILD_PREV)
+     {
+       if (findex == -1)
+           findex = xp->xp_numfiles;
+       --findex;
+     }
+     else    // mode == WILD_NEXT
+       ++findex;
+ 
+     // When wrapping around, return the original string, set findex to
+     // -1.
+     if (findex < 0)
+     {
+       if (orig_save == NULL)
+           findex = xp->xp_numfiles - 1;
+       else
+           findex = -1;
+     }
+     if (findex >= xp->xp_numfiles)
+     {
+       if (orig_save == NULL)
+           findex = 0;
+       else
+           findex = -1;
+     }
+ #ifdef FEAT_WILDMENU
+     if (compl_match_array)
+     {
+       compl_selected = findex;
+       cmdline_pum_display();
+     }
+     else if (p_wmnu)
+       win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
+               findex, cmd_showtail);
+ #endif
+     *p_findex = findex;
+ 
+     if (findex == -1)
+       return vim_strsave(orig_save);
+ 
+     return vim_strsave(xp->xp_files[findex]);
+ }
+ 
+ /*
+  * Start the command-line expansion and get the matches.
+  */
+     static char_u *
+ ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
+ {
+     int               non_suf_match;          // number without matching 
suffix
+     int               i;
+     char_u    *ss = NULL;
+ 
+     // Do the expansion.
+     if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
+               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,
+       // causing the pattern to be added, which has illegal characters.
+       if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND))
+           semsg(_(e_no_match_str_2), str);
+ #endif
+     }
+     else if (xp->xp_numfiles == 0)
+     {
+       if (!(options & WILD_SILENT))
+           semsg(_(e_no_match_str_2), str);
+     }
+     else
+     {
+       // Escape the matches for use on the command line.
+       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;
+           else
+               non_suf_match = 1;
+           if ((xp->xp_context == EXPAND_FILES
+                       || xp->xp_context == EXPAND_DIRECTORIES)
+                   && xp->xp_numfiles > 1)
+           {
+               // More than one match; check suffix.
+               // The files will have been sorted on matching suffix in
+               // expand_wildcards, only need to check the first two.
+               non_suf_match = 0;
+               for (i = 0; i < 2; ++i)
+                   if (match_suffix(xp->xp_files[i]))
+                       ++non_suf_match;
+           }
+           if (non_suf_match != 1)
+           {
+               // Can we ever get here unless it's while expanding
+               // interactively?  If not, we can get rid of this all
+               // together. Don't really want to wait for this message
+               // (and possibly have to hit return to continue!).
+               if (!(options & WILD_SILENT))
+                   emsg(_(e_too_many_file_names));
+               else if (!(options & WILD_NO_BEEP))
+                   beep_flush();
+           }
+           if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
+               ss = vim_strsave(xp->xp_files[0]);
+       }
+     }
+ 
+     return ss;
+ }
+ 
+ /*
+  * Return the longest common part in the list of cmdline completion matches.
+  */
+     static char_u *
+ find_longest_match(expand_T *xp, int options)
+ {
+     long_u    len;
+     int               mb_len = 1;
+     int               c0, ci;
+     int               i;
+     char_u    *ss;
+ 
+     for (len = 0; xp->xp_files[0][len]; len += mb_len)
+     {
+       if (has_mbyte)
+       {
+           mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]);
+           c0 =(* mb_ptr2char)(&xp->xp_files[0][len]);
+       }
+       else
+           c0 = xp->xp_files[0][len];
+       for (i = 1; i < xp->xp_numfiles; ++i)
+       {
+           if (has_mbyte)
+               ci =(* mb_ptr2char)(&xp->xp_files[i][len]);
+           else
+               ci = xp->xp_files[i][len];
+           if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
+                       || xp->xp_context == EXPAND_FILES
+                       || xp->xp_context == EXPAND_SHELLCMD
+                       || xp->xp_context == EXPAND_BUFFERS))
+           {
+               if (MB_TOLOWER(c0) != MB_TOLOWER(ci))
+                   break;
+           }
+           else if (c0 != ci)
+               break;
+       }
+       if (i < xp->xp_numfiles)
+       {
+           if (!(options & WILD_NO_BEEP))
+               vim_beep(BO_WILD);
+           break;
+       }
+     }
+ 
+     ss = alloc(len + 1);
+     if (ss)
+       vim_strncpy(ss, xp->xp_files[0], (size_t)len);
+ 
+     return ss;
+ }
+ 
+ /*
   * Do wildcard expansion on the string 'str'.
   * Chars that should not be expanded must be preceded with a backslash.
   * Return a pointer to allocated memory containing the new string.
***************
*** 371,376 ****
--- 554,563 ----
   * mode = WILD_ALL:       return all matches concatenated
   * mode = WILD_LONGEST:           return longest matched part
   * mode = WILD_ALL_KEEP:    get all matches, keep matches
+  * mode = WILD_APPLY:     apply the item selected in the cmdline completion
+  *                        popup menu and close the menu.
+  * mode = WILD_CANCEL:            cancel and close the cmdline completion 
popup and
+  *                        use the original text.
   *
   * options = WILD_LIST_NOTFOUND:    list entries without a match
   * options = WILD_HOME_REPLACE:           do home_replace() for buffer names
***************
*** 399,453 ****
      int               orig_saved = FALSE;
      int               i;
      long_u    len;
-     int               non_suf_match;          // number without matching 
suffix
  
      // first handle the case of using an old match
      if (mode == WILD_NEXT || mode == WILD_PREV)
!     {
!       if (xp->xp_numfiles > 0)
!       {
!           if (mode == WILD_PREV)
!           {
!               if (findex == -1)
!                   findex = xp->xp_numfiles;
!               --findex;
!           }
!           else    // mode == WILD_NEXT
!               ++findex;
! 
!           // When wrapping around, return the original string, set findex to
!           // -1.
!           if (findex < 0)
!           {
!               if (orig_save == NULL)
!                   findex = xp->xp_numfiles - 1;
!               else
!                   findex = -1;
!           }
!           if (findex >= xp->xp_numfiles)
!           {
!               if (orig_save == NULL)
!                   findex = 0;
!               else
!                   findex = -1;
!           }
! #ifdef FEAT_WILDMENU
!           if (compl_match_array)
!           {
!               compl_selected = findex;
!               cmdline_pum_display();
!           }
!           else if (p_wmnu)
!               win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
!                                                       findex, cmd_showtail);
! #endif
!           if (findex == -1)
!               return vim_strsave(orig_save);
!           return vim_strsave(xp->xp_files[findex]);
!       }
!       else
!           return NULL;
!     }
  
      if (mode == WILD_CANCEL)
        ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
--- 586,595 ----
      int               orig_saved = FALSE;
      int               i;
      long_u    len;
  
      // first handle the case of using an old match
      if (mode == WILD_NEXT || mode == WILD_PREV)
!       return get_next_or_prev_match(mode, xp, &findex, orig_save);
  
      if (mode == WILD_CANCEL)
        ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
***************
*** 473,580 ****
        orig_save = orig;
        orig_saved = TRUE;
  
!       // Do the expansion.
!       if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files,
!                                                            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,
!           // causing the pattern to be added, which has illegal characters.
!           if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND))
!               semsg(_(e_no_match_str_2), str);
! #endif
!       }
!       else if (xp->xp_numfiles == 0)
!       {
!           if (!(options & WILD_SILENT))
!               semsg(_(e_no_match_str_2), str);
!       }
!       else
!       {
!           // Escape the matches for use on the command line.
!           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;
!               else
!                   non_suf_match = 1;
!               if ((xp->xp_context == EXPAND_FILES
!                           || xp->xp_context == EXPAND_DIRECTORIES)
!                       && xp->xp_numfiles > 1)
!               {
!                   // More than one match; check suffix.
!                   // The files will have been sorted on matching suffix in
!                   // expand_wildcards, only need to check the first two.
!                   non_suf_match = 0;
!                   for (i = 0; i < 2; ++i)
!                       if (match_suffix(xp->xp_files[i]))
!                           ++non_suf_match;
!               }
!               if (non_suf_match != 1)
!               {
!                   // Can we ever get here unless it's while expanding
!                   // interactively?  If not, we can get rid of this all
!                   // together. Don't really want to wait for this message
!                   // (and possibly have to hit return to continue!).
!                   if (!(options & WILD_SILENT))
!                       emsg(_(e_too_many_file_names));
!                   else if (!(options & WILD_NO_BEEP))
!                       beep_flush();
!               }
!               if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE))
!                   ss = vim_strsave(xp->xp_files[0]);
!           }
!       }
      }
  
      // Find longest common part
      if (mode == WILD_LONGEST && xp->xp_numfiles > 0)
      {
!       int mb_len = 1;
!       int c0, ci;
! 
!       for (len = 0; xp->xp_files[0][len]; len += mb_len)
!       {
!           if (has_mbyte)
!           {
!               mb_len = (*mb_ptr2len)(&xp->xp_files[0][len]);
!               c0 =(* mb_ptr2char)(&xp->xp_files[0][len]);
!           }
!           else
!               c0 = xp->xp_files[0][len];
!           for (i = 1; i < xp->xp_numfiles; ++i)
!           {
!               if (has_mbyte)
!                   ci =(* mb_ptr2char)(&xp->xp_files[i][len]);
!               else
!                   ci = xp->xp_files[i][len];
!               if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
!                       || xp->xp_context == EXPAND_FILES
!                       || xp->xp_context == EXPAND_SHELLCMD
!                       || xp->xp_context == EXPAND_BUFFERS))
!               {
!                   if (MB_TOLOWER(c0) != MB_TOLOWER(ci))
!                       break;
!               }
!               else if (c0 != ci)
!                   break;
!           }
!           if (i < xp->xp_numfiles)
!           {
!               if (!(options & WILD_NO_BEEP))
!                   vim_beep(BO_WILD);
!               break;
!           }
!       }
! 
!       ss = alloc(len + 1);
!       if (ss)
!           vim_strncpy(ss, xp->xp_files[0], (size_t)len);
        findex = -1;                        // next p_wc gets first one
      }
  
--- 615,627 ----
        orig_save = orig;
        orig_saved = TRUE;
  
!       ss = ExpandOne_start(mode, xp, str, options);
      }
  
      // Find longest common part
      if (mode == WILD_LONGEST && xp->xp_numfiles > 0)
      {
!       ss = find_longest_match(xp, options);
        findex = -1;                        // next p_wc gets first one
      }
  
***************
*** 1077,1134 ****
  }
  
  /*
!  * This is all pretty much copied from do_one_cmd(), with all the extra stuff
!  * we don't need/want deleted.        Maybe this could be done better if we 
didn't
!  * repeat all this stuff.  The only problem is that they may not stay
!  * perfectly compatible with each other, but then the command line syntax
!  * probably won't change that much -- webb.
   */
      static char_u *
! set_one_cmd_context(
!     expand_T  *xp,
!     char_u    *buff)      // buffer for command string
  {
!     char_u            *p;
!     char_u            *cmd, *arg;
!     int                       len = 0;
!     exarg_T           ea;
!     int                       compl = EXPAND_NOTHING;
!     int                       delim;
!     int                       forceit = FALSE;
!     int                       usefilter = FALSE;  // filter instead of file 
name
! 
!     ExpandInit(xp);
!     xp->xp_pattern = buff;
!     xp->xp_line = buff;
!     xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
!     ea.argt = 0;
! 
!     // 1. skip comment lines and leading space, colons or bars
!     for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
!       ;
!     xp->xp_pattern = cmd;
! 
!     if (*cmd == NUL)
!       return NULL;
!     if (*cmd == '"')      // ignore comment lines
!     {
!       xp->xp_context = EXPAND_NOTHING;
!       return NULL;
!     }
! 
!     // 3. Skip over the range to find the command.
!     cmd = skip_range(cmd, TRUE, &xp->xp_context);
!     xp->xp_pattern = cmd;
!     if (*cmd == NUL)
!       return NULL;
!     if (*cmd == '"')
!     {
!       xp->xp_context = EXPAND_NOTHING;
!       return NULL;
!     }
! 
!     if (*cmd == '|' || *cmd == '\n')
!       return cmd + 1;                 // There's another command
  
      // Isolate the command and search for it in the command table.
      // Exceptions:
--- 1124,1140 ----
  }
  
  /*
!  * Sets the index of a built-in or user defined command 'cmd' in eap->cmdidx.
!  * For user defined commands, the completion context is set in 'xp' and the
!  * completion flags in 'complp'.
!  *
!  * Returns a pointer to the text after the command or NULL for failure.
   */
      static char_u *
! set_cmd_index(char_u *cmd, exarg_T *eap, expand_T *xp, int *complp)
  {
!     char_u    *p = NULL;
!     int               len = 0;
  
      // Isolate the command and search for it in the command table.
      // Exceptions:
***************
*** 1137,1143 ****
      // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
      if (*cmd == 'k' && cmd[1] != 'e')
      {
!       ea.cmdidx = CMD_k;
        p = cmd + 1;
      }
      else
--- 1143,1149 ----
      // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
      if (*cmd == 'k' && cmd[1] != 'e')
      {
!       eap->cmdidx = CMD_k;
        p = cmd + 1;
      }
      else
***************
*** 1168,1174 ****
            return NULL;
        }
  
!       ea.cmdidx = excmd_get_cmdidx(cmd, len);
  
        if (cmd[0] >= 'A' && cmd[0] <= 'Z')
            while (ASCII_ISALNUM(*p) || *p == '*')      // Allow * wild card
--- 1174,1180 ----
            return NULL;
        }
  
!       eap->cmdidx = excmd_get_cmdidx(cmd, len);
  
        if (cmd[0] >= 'A' && cmd[0] <= 'Z')
            while (ASCII_ISALNUM(*p) || *p == '*')      // Allow * wild card
***************
*** 1180,1441 ****
      if (*p == NUL && ASCII_ISALNUM(p[-1]))
        return NULL;
  
!     if (ea.cmdidx == CMD_SIZE)
      {
        if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL)
        {
!           ea.cmdidx = CMD_substitute;
            p = cmd + 1;
        }
        else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
        {
!           ea.cmd = cmd;
!           p = find_ucmd(&ea, p, NULL, xp, &compl);
            if (p == NULL)
!               ea.cmdidx = CMD_SIZE;   // ambiguous user command
        }
      }
!     if (ea.cmdidx == CMD_SIZE)
      {
        // Not still touching the command and it was an illegal one
        xp->xp_context = EXPAND_UNSUCCESSFUL;
        return NULL;
      }
  
!     xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
! 
!     if (*p == '!')                // forced commands
!     {
!       forceit = TRUE;
!       ++p;
!     }
! 
!     // 6. parse arguments
!     if (!IS_USER_CMDIDX(ea.cmdidx))
!       ea.argt = excmd_get_argt(ea.cmdidx);
! 
!     arg = skipwhite(p);
! 
!     // Skip over ++argopt argument
!     if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
!     {
!       p = arg;
!       while (*p && !vim_isspace(*p))
!           MB_PTR_ADV(p);
!       arg = skipwhite(p);
!     }
! 
!     if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
!     {
!       if (*arg == '>')                        // append
!       {
!           if (*++arg == '>')
!               ++arg;
!           arg = skipwhite(arg);
!       }
!       else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter
!       {
!           ++arg;
!           usefilter = TRUE;
!       }
!     }
! 
!     if (ea.cmdidx == CMD_read)
!     {
!       usefilter = forceit;                    // :r! filter if forced
!       if (*arg == '!')                        // :r !filter
!       {
!           ++arg;
!           usefilter = TRUE;
!       }
!     }
! 
!     if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
!     {
!       while (*arg == *cmd)        // allow any number of '>' or '<'
!           ++arg;
!       arg = skipwhite(arg);
!     }
! 
!     // Does command allow "+command"?
!     if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+')
!     {
!       // Check if we're in the +command
!       p = arg + 1;
!       arg = skip_cmd_arg(arg, FALSE);
! 
!       // Still touching the command after '+'?
!       if (*arg == NUL)
!           return p;
! 
!       // Skip space(s) after +command to get to the real argument
!       arg = skipwhite(arg);
!     }
! 
! 
!     // Check for '|' to separate commands and '"' to start comments.
!     // Don't do this for ":read !cmd" and ":write !cmd".
!     if ((ea.argt & EX_TRLBAR) && !usefilter)
!     {
!       p = arg;
!       // ":redir @" is not the start of a comment
!       if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"')
!           p += 2;
!       while (*p)
!       {
!           if (*p == Ctrl_V)
!           {
!               if (p[1] != NUL)
!                   ++p;
!           }
!           else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM))
!                   || *p == '|' || *p == '\n')
!           {
!               if (*(p - 1) != '\\')
!               {
!                   if (*p == '|' || *p == '\n')
!                       return p + 1;
!                   return NULL;    // It's a comment
!               }
!           }
!           MB_PTR_ADV(p);
!       }
!     }
! 
!     if (!(ea.argt & EX_EXTRA) && *arg != NUL
!                                 && vim_strchr((char_u *)"|\"", *arg) == NULL)
!       // no arguments allowed but there is something
!       return NULL;
  
!     // Find start of last argument (argument just before cursor):
!     p = buff;
!     xp->xp_pattern = p;
!     len = (int)STRLEN(buff);
!     while (*p && p < buff + len)
      {
!       if (*p == ' ' || *p == TAB)
!       {
!           // argument starts after a space
!           xp->xp_pattern = ++p;
!       }
        else
        {
!           if (*p == '\\' && *(p + 1) != NUL)
!               ++p; // skip over escaped character
!           MB_PTR_ADV(p);
!       }
!     }
! 
!     if (ea.argt & EX_XFILE)
!     {
!       int     c;
!       int     in_quote = FALSE;
!       char_u  *bow = NULL;    // Beginning of word
! 
!       // Allow spaces within back-quotes to count as part of the argument
!       // being expanded.
!       xp->xp_pattern = skipwhite(arg);
!       p = xp->xp_pattern;
!       while (*p != NUL)
!       {
!           if (has_mbyte)
!               c = mb_ptr2char(p);
!           else
!               c = *p;
!           if (c == '\\' && p[1] != NUL)
!               ++p;
!           else if (c == '`')
            {
!               if (!in_quote)
!               {
!                   xp->xp_pattern = p;
!                   bow = p + 1;
!               }
!               in_quote = !in_quote;
            }
!           // An argument can contain just about everything, except
!           // characters that end the command and white space.
!           else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c)
  #ifdef SPACE_IN_FILENAME
!                                       && (!(ea.argt & EX_NOSPC) || usefilter)
  #endif
                    ))
            {
!               len = 0;  // avoid getting stuck when space is in 'isfname'
!               while (*p != NUL)
!               {
!                   if (has_mbyte)
!                       c = mb_ptr2char(p);
!                   else
!                       c = *p;
!                   if (c == '`' || vim_isfilec_or_wc(c))
!                       break;
!                   if (has_mbyte)
!                       len = (*mb_ptr2len)(p);
!                   else
!                       len = 1;
!                   MB_PTR_ADV(p);
!               }
!               if (in_quote)
!                   bow = p;
                else
!                   xp->xp_pattern = p;
!               p -= len;
            }
!           MB_PTR_ADV(p);
        }
  
!       // If we are still inside the quotes, and we passed a space, just
!       // expand from there.
!       if (bow != NULL && in_quote)
!           xp->xp_pattern = bow;
!       xp->xp_context = EXPAND_FILES;
  
!       // For a shell command more chars need to be escaped.
!       if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal)
!       {
  #ifndef BACKSLASH_IN_FILENAME
!           xp->xp_shell = TRUE;
  #endif
!           // When still after the command name expand executables.
!           if (xp->xp_pattern == skipwhite(arg))
!               xp->xp_context = EXPAND_SHELLCMD;
!       }
  
!       // Check for environment variable.
!       if (*xp->xp_pattern == '$')
        {
!           for (p = xp->xp_pattern + 1; *p != NUL; ++p)
!               if (!vim_isIDc(*p))
!                   break;
!           if (*p == NUL)
!           {
!               xp->xp_context = EXPAND_ENV_VARS;
!               ++xp->xp_pattern;
!               // Avoid that the assignment uses EXPAND_FILES again.
!               if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST)
!                   compl = EXPAND_ENV_VARS;
!           }
!       }
!       // Check for user names.
!       if (*xp->xp_pattern == '~')
!       {
!           for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p)
!               ;
!           // Complete ~user only if it partially matches a user name.
!           // A full match ~user<Tab> will be replaced by user's home
!           // directory i.e. something like ~user<Tab> -> /home/user/
!           if (*p == NUL && p > xp->xp_pattern + 1
!                                      && match_user(xp->xp_pattern + 1) >= 1)
!           {
!               xp->xp_context = EXPAND_USER;
!               ++xp->xp_pattern;
!           }
        }
      }
  
!     // 6. Switch on command name.
!     switch (ea.cmdidx)
      {
        case CMD_find:
        case CMD_sfind:
--- 1186,1357 ----
      if (*p == NUL && ASCII_ISALNUM(p[-1]))
        return NULL;
  
!     if (eap->cmdidx == CMD_SIZE)
      {
        if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL)
        {
!           eap->cmdidx = CMD_substitute;
            p = cmd + 1;
        }
        else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
        {
!           eap->cmd = cmd;
!           p = find_ucmd(eap, p, NULL, xp, complp);
            if (p == NULL)
!               eap->cmdidx = CMD_SIZE; // ambiguous user command
        }
      }
!     if (eap->cmdidx == CMD_SIZE)
      {
        // Not still touching the command and it was an illegal one
        xp->xp_context = EXPAND_UNSUCCESSFUL;
        return NULL;
      }
  
!     return p;
! }
  
! /*
!  * Set the completion context for a command argument with wild card 
characters.
!  */
!     static void
! set_context_for_wildcard_arg(
!       exarg_T         *eap,
!       char_u          *arg,
!       int             usefilter,
!       expand_T        *xp,
!       int             *complp)
! {
!     char_u    *p;
!     int               c;
!     int               in_quote = FALSE;
!     char_u    *bow = NULL;    // Beginning of word
!     int               len = 0;
! 
!     // Allow spaces within back-quotes to count as part of the argument
!     // being expanded.
!     xp->xp_pattern = skipwhite(arg);
!     p = xp->xp_pattern;
!     while (*p != NUL)
      {
!       if (has_mbyte)
!           c = mb_ptr2char(p);
        else
+           c = *p;
+       if (c == '\\' && p[1] != NUL)
+           ++p;
+       else if (c == '`')
        {
!           if (!in_quote)
            {
!               xp->xp_pattern = p;
!               bow = p + 1;
            }
!           in_quote = !in_quote;
!       }
!       // An argument can contain just about everything, except
!       // characters that end the command and white space.
!       else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c)
  #ifdef SPACE_IN_FILENAME
!                   && (!(eap->argt & EX_NOSPC) || usefilter)
  #endif
                    ))
+       {
+           len = 0;  // avoid getting stuck when space is in 'isfname'
+           while (*p != NUL)
            {
!               if (has_mbyte)
!                   c = mb_ptr2char(p);
!               else
!                   c = *p;
!               if (c == '`' || vim_isfilec_or_wc(c))
!                   break;
!               if (has_mbyte)
!                   len = (*mb_ptr2len)(p);
                else
!                   len = 1;
!               MB_PTR_ADV(p);
            }
!           if (in_quote)
!               bow = p;
!           else
!               xp->xp_pattern = p;
!           p -= len;
        }
+       MB_PTR_ADV(p);
+     }
  
!     // If we are still inside the quotes, and we passed a space, just
!     // expand from there.
!     if (bow != NULL && in_quote)
!       xp->xp_pattern = bow;
!     xp->xp_context = EXPAND_FILES;
  
!     // For a shell command more chars need to be escaped.
!     if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal)
!     {
  #ifndef BACKSLASH_IN_FILENAME
!       xp->xp_shell = TRUE;
  #endif
!       // When still after the command name expand executables.
!       if (xp->xp_pattern == skipwhite(arg))
!           xp->xp_context = EXPAND_SHELLCMD;
!     }
  
!     // Check for environment variable.
!     if (*xp->xp_pattern == '$')
!     {
!       for (p = xp->xp_pattern + 1; *p != NUL; ++p)
!           if (!vim_isIDc(*p))
!               break;
!       if (*p == NUL)
        {
!           xp->xp_context = EXPAND_ENV_VARS;
!           ++xp->xp_pattern;
!           // Avoid that the assignment uses EXPAND_FILES again.
!           if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST)
!               *complp = EXPAND_ENV_VARS;
!       }
!     }
!     // Check for user names.
!     if (*xp->xp_pattern == '~')
!     {
!       for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p)
!           ;
!       // Complete ~user only if it partially matches a user name.
!       // A full match ~user<Tab> will be replaced by user's home
!       // directory i.e. something like ~user<Tab> -> /home/user/
!       if (*p == NUL && p > xp->xp_pattern + 1
!               && match_user(xp->xp_pattern + 1) >= 1)
!       {
!           xp->xp_context = EXPAND_USER;
!           ++xp->xp_pattern;
        }
      }
+ }
  
! /*
!  * 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, 'compl' has the
!  * completion type.
!  * Returns a pointer to the next command. Returns NULL if there is no next
!  * command.
!  */
!     static char_u *
! 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:
        case CMD_sfind:
***************
*** 1658,1664 ****
        case CMD_lexpr:
        case CMD_laddexpr:
        case CMD_lgetexpr:
!           set_context_for_expression(xp, arg, ea.cmdidx);
            break;
  
        case CMD_unlet:
--- 1574,1580 ----
        case CMD_lexpr:
        case CMD_laddexpr:
        case CMD_lgetexpr:
!           set_context_for_expression(xp, arg, cmdidx);
            break;
  
        case CMD_unlet:
***************
*** 1696,1702 ****
        case CMD_cscope:
        case CMD_lcscope:
        case CMD_scscope:
!           set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
            break;
  #endif
  #ifdef FEAT_SIGNS
--- 1612,1618 ----
        case CMD_cscope:
        case CMD_lcscope:
        case CMD_scscope:
!           set_context_in_cscope_cmd(xp, arg, cmdidx);
            break;
  #endif
  #ifdef FEAT_SIGNS
***************
*** 1730,1736 ****
            if (compl != EXPAND_NOTHING)
            {
                // EX_XFILE: file names are handled above
!               if (!(ea.argt & EX_XFILE))
                {
  #ifdef FEAT_MENU
                    if (compl == EXPAND_MENUS)
--- 1646,1652 ----
            if (compl != EXPAND_NOTHING)
            {
                // EX_XFILE: file names are handled above
!               if (!(argt & EX_XFILE))
                {
  #ifdef FEAT_MENU
                    if (compl == EXPAND_MENUS)
***************
*** 1769,1775 ****
        case CMD_tmap:      case CMD_tnoremap:
        case CMD_xmap:      case CMD_xnoremap:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                    FALSE, FALSE, ea.cmdidx);
        case CMD_unmap:
        case CMD_nunmap:
        case CMD_vunmap:
--- 1685,1691 ----
        case CMD_tmap:      case CMD_tnoremap:
        case CMD_xmap:      case CMD_xnoremap:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                    FALSE, FALSE, cmdidx);
        case CMD_unmap:
        case CMD_nunmap:
        case CMD_vunmap:
***************
*** 1781,1787 ****
        case CMD_tunmap:
        case CMD_xunmap:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                     FALSE, TRUE, ea.cmdidx);
        case CMD_mapclear:
        case CMD_nmapclear:
        case CMD_vmapclear:
--- 1697,1703 ----
        case CMD_tunmap:
        case CMD_xunmap:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                     FALSE, TRUE, cmdidx);
        case CMD_mapclear:
        case CMD_nmapclear:
        case CMD_vmapclear:
***************
*** 1800,1811 ****
        case CMD_cabbrev:       case CMD_cnoreabbrev:
        case CMD_iabbrev:       case CMD_inoreabbrev:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                     TRUE, FALSE, ea.cmdidx);
        case CMD_unabbreviate:
        case CMD_cunabbrev:
        case CMD_iunabbrev:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                      TRUE, TRUE, ea.cmdidx);
  #ifdef FEAT_MENU
        case CMD_menu:      case CMD_noremenu:      case CMD_unmenu:
        case CMD_amenu:     case CMD_anoremenu:     case CMD_aunmenu:
--- 1716,1727 ----
        case CMD_cabbrev:       case CMD_cnoreabbrev:
        case CMD_iabbrev:       case CMD_inoreabbrev:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                     TRUE, FALSE, cmdidx);
        case CMD_unabbreviate:
        case CMD_cunabbrev:
        case CMD_iunabbrev:
            return set_context_in_map_cmd(xp, cmd, arg, forceit,
!                                                      TRUE, TRUE, cmdidx);
  #ifdef FEAT_MENU
        case CMD_menu:      case CMD_noremenu:      case CMD_unmenu:
        case CMD_amenu:     case CMD_anoremenu:     case CMD_aunmenu:
***************
*** 1907,1912 ****
--- 1823,2018 ----
      return NULL;
  }
  
+ /*
+  * This is all pretty much copied from do_one_cmd(), with all the extra stuff
+  * we don't need/want deleted.        Maybe this could be done better if we 
didn't
+  * repeat all this stuff.  The only problem is that they may not stay
+  * perfectly compatible with each other, but then the command line syntax
+  * probably won't change that much -- webb.
+  */
+     static char_u *
+ set_one_cmd_context(
+     expand_T  *xp,
+     char_u    *buff)      // buffer for command string
+ {
+     char_u            *p;
+     char_u            *cmd, *arg;
+     int                       len = 0;
+     exarg_T           ea;
+     int                       compl = EXPAND_NOTHING;
+     int                       forceit = FALSE;
+     int                       usefilter = FALSE;  // filter instead of file 
name
+ 
+     ExpandInit(xp);
+     xp->xp_pattern = buff;
+     xp->xp_line = buff;
+     xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
+     ea.argt = 0;
+ 
+     // 1. skip comment lines and leading space, colons or bars
+     for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++)
+       ;
+     xp->xp_pattern = cmd;
+ 
+     if (*cmd == NUL)
+       return NULL;
+     if (*cmd == '"')      // ignore comment lines
+     {
+       xp->xp_context = EXPAND_NOTHING;
+       return NULL;
+     }
+ 
+     // 3. Skip over the range to find the command.
+     cmd = skip_range(cmd, TRUE, &xp->xp_context);
+     xp->xp_pattern = cmd;
+     if (*cmd == NUL)
+       return NULL;
+     if (*cmd == '"')
+     {
+       xp->xp_context = EXPAND_NOTHING;
+       return NULL;
+     }
+ 
+     if (*cmd == '|' || *cmd == '\n')
+       return cmd + 1;                 // There's another command
+ 
+     // Get the command index.
+     p = set_cmd_index(cmd, &ea, xp, &compl);
+     if (p == NULL)
+       return NULL;
+ 
+     xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
+ 
+     if (*p == '!')                // forced commands
+     {
+       forceit = TRUE;
+       ++p;
+     }
+ 
+     // 6. parse arguments
+     if (!IS_USER_CMDIDX(ea.cmdidx))
+       ea.argt = excmd_get_argt(ea.cmdidx);
+ 
+     arg = skipwhite(p);
+ 
+     // Skip over ++argopt argument
+     if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
+     {
+       p = arg;
+       while (*p && !vim_isspace(*p))
+           MB_PTR_ADV(p);
+       arg = skipwhite(p);
+     }
+ 
+     if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
+     {
+       if (*arg == '>')                        // append
+       {
+           if (*++arg == '>')
+               ++arg;
+           arg = skipwhite(arg);
+       }
+       else if (*arg == '!' && ea.cmdidx == CMD_write) // :w !filter
+       {
+           ++arg;
+           usefilter = TRUE;
+       }
+     }
+ 
+     if (ea.cmdidx == CMD_read)
+     {
+       usefilter = forceit;                    // :r! filter if forced
+       if (*arg == '!')                        // :r !filter
+       {
+           ++arg;
+           usefilter = TRUE;
+       }
+     }
+ 
+     if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
+     {
+       while (*arg == *cmd)        // allow any number of '>' or '<'
+           ++arg;
+       arg = skipwhite(arg);
+     }
+ 
+     // Does command allow "+command"?
+     if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+')
+     {
+       // Check if we're in the +command
+       p = arg + 1;
+       arg = skip_cmd_arg(arg, FALSE);
+ 
+       // Still touching the command after '+'?
+       if (*arg == NUL)
+           return p;
+ 
+       // Skip space(s) after +command to get to the real argument
+       arg = skipwhite(arg);
+     }
+ 
+ 
+     // Check for '|' to separate commands and '"' to start comments.
+     // Don't do this for ":read !cmd" and ":write !cmd".
+     if ((ea.argt & EX_TRLBAR) && !usefilter)
+     {
+       p = arg;
+       // ":redir @" is not the start of a comment
+       if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"')
+           p += 2;
+       while (*p)
+       {
+           if (*p == Ctrl_V)
+           {
+               if (p[1] != NUL)
+                   ++p;
+           }
+           else if ( (*p == '"' && !(ea.argt & EX_NOTRLCOM))
+                   || *p == '|' || *p == '\n')
+           {
+               if (*(p - 1) != '\\')
+               {
+                   if (*p == '|' || *p == '\n')
+                       return p + 1;
+                   return NULL;    // It's a comment
+               }
+           }
+           MB_PTR_ADV(p);
+       }
+     }
+ 
+     if (!(ea.argt & EX_EXTRA) && *arg != NUL
+                                 && vim_strchr((char_u *)"|\"", *arg) == NULL)
+       // no arguments allowed but there is something
+       return NULL;
+ 
+     // Find start of last argument (argument just before cursor):
+     p = buff;
+     xp->xp_pattern = p;
+     len = (int)STRLEN(buff);
+     while (*p && p < buff + len)
+     {
+       if (*p == ' ' || *p == TAB)
+       {
+           // argument starts after a space
+           xp->xp_pattern = ++p;
+       }
+       else
+       {
+           if (*p == '\\' && *(p + 1) != NUL)
+               ++p; // skip over escaped character
+           MB_PTR_ADV(p);
+       }
+     }
+ 
+     if (ea.argt & EX_XFILE)
+       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,
***************
*** 2007,2012 ****
--- 2113,2190 ----
  }
  
  /*
+  * Expand file or directory names.
+  */
+     static int
+ expand_files_and_dirs(
+       expand_T        *xp,
+       char_u          *pat,
+       char_u          ***file,
+       int             *num_file,
+       int             flags,
+       int             options)
+ {
+     int               free_pat = FALSE;
+     int               i;
+     int               ret;
+ 
+     // for ":set path=" and ":set tags=" halve backslashes for escaped
+     // space
+     if (xp->xp_backslash != XP_BS_NONE)
+     {
+       free_pat = TRUE;
+       pat = vim_strsave(pat);
+       for (i = 0; pat[i]; ++i)
+           if (pat[i] == '\\')
+           {
+               if (xp->xp_backslash == XP_BS_THREE
+                       && pat[i + 1] == '\\'
+                       && pat[i + 2] == '\\'
+                       && pat[i + 3] == ' ')
+                   STRMOVE(pat + i, pat + i + 3);
+               if (xp->xp_backslash == XP_BS_ONE
+                       && pat[i + 1] == ' ')
+                   STRMOVE(pat + i, pat + i + 1);
+           }
+     }
+ 
+     if (xp->xp_context == EXPAND_FILES)
+       flags |= EW_FILE;
+     else if (xp->xp_context == EXPAND_FILES_IN_PATH)
+       flags |= (EW_FILE | EW_PATH);
+     else
+       flags = (flags | EW_DIR) & ~EW_FILE;
+     if (options & WILD_ICASE)
+       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
+     if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0)
+     {
+       int         j;
+ 
+       for (j = 0; j < *num_file; ++j)
+       {
+           char_u      *ptr = (*file)[j];
+ 
+           while (*ptr != NUL)
+           {
+               if (p_csl[0] == 's' && *ptr == '\\')
+                   *ptr = '/';
+               else if (p_csl[0] == 'b' && *ptr == '/')
+                   *ptr = '\\';
+               ptr += (*mb_ptr2len)(ptr);
+           }
+       }
+     }
+ #endif
+     return ret;
+ }
+ 
+ /*
   * Function given to ExpandGeneric() to obtain the possible arguments of the
   * ":behave {mswin,xterm}" command.
   */
***************
*** 2041,2046 ****
--- 2219,2309 ----
  }
  
  /*
+  * Do the expansion based on xp->xp_context and 'rmp'.
+  */
+     static int
+ ExpandOther(
+       expand_T        *xp, 
+       regmatch_T      *rmp,
+       int             *num_file,
+       char_u          ***file)
+ {
+     static struct expgen
+     {
+       int             context;
+       char_u  *((*func)(expand_T *, int));
+       int             ic;
+       int             escaped;
+     } tab[] =
+     {
+       {EXPAND_COMMANDS, get_command_name, FALSE, TRUE},
+       {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE},
+       {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE},
+       {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE},
+       {EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
+       {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE},
+       {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE},
+       {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE},
+       {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE},
+       {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE},
+ # ifdef FEAT_EVAL
+       {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE},
+       {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE},
+       {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE},
+       {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE},
+       {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE},
+ # endif
+ # ifdef FEAT_MENU
+       {EXPAND_MENUS, get_menu_name, FALSE, TRUE},
+       {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE},
+ # endif
+ # ifdef FEAT_SYN_HL
+       {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE},
+ # endif
+ # ifdef FEAT_PROFILE
+       {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE},
+ # endif
+       {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE},
+       {EXPAND_EVENTS, get_event_name, TRUE, FALSE},
+       {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE},
+ # ifdef FEAT_CSCOPE
+       {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE},
+ # endif
+ # ifdef FEAT_SIGNS
+       {EXPAND_SIGN, get_sign_name, TRUE, TRUE},
+ # endif
+ # ifdef FEAT_PROFILE
+       {EXPAND_PROFILE, get_profile_name, TRUE, TRUE},
+ # endif
+ # if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
+       {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE},
+       {EXPAND_LOCALES, get_locales, TRUE, FALSE},
+ # endif
+       {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE},
+       {EXPAND_USER, get_users, TRUE, FALSE},
+       {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE},
+     };
+     int       i;
+     int ret = FAIL;
+ 
+     // Find a context in the table and call the ExpandGeneric() with the
+     // right function to do the expansion.
+     for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i)
+     {
+       if (xp->xp_context == tab[i].context)
+       {
+           if (tab[i].ic)
+               rmp->rm_ic = TRUE;
+           ret = ExpandGeneric(xp, rmp, num_file, file,
+                                               tab[i].func, tab[i].escaped);
+           break;
+       }
+     }
+ 
+     return ret;
+ }
+ 
+ /*
   * Do the expansion based on xp->xp_context and "pat".
   */
      static int
***************
*** 2073,2138 ****
      if (xp->xp_context == EXPAND_FILES
            || xp->xp_context == EXPAND_DIRECTORIES
            || xp->xp_context == EXPAND_FILES_IN_PATH)
!     {
!       // Expand file or directory names.
!       int     free_pat = FALSE;
!       int     i;
! 
!       // for ":set path=" and ":set tags=" halve backslashes for escaped
!       // space
!       if (xp->xp_backslash != XP_BS_NONE)
!       {
!           free_pat = TRUE;
!           pat = vim_strsave(pat);
!           for (i = 0; pat[i]; ++i)
!               if (pat[i] == '\\')
!               {
!                   if (xp->xp_backslash == XP_BS_THREE
!                           && pat[i + 1] == '\\'
!                           && pat[i + 2] == '\\'
!                           && pat[i + 3] == ' ')
!                       STRMOVE(pat + i, pat + i + 3);
!                   if (xp->xp_backslash == XP_BS_ONE
!                           && pat[i + 1] == ' ')
!                       STRMOVE(pat + i, pat + i + 1);
!               }
!       }
! 
!       if (xp->xp_context == EXPAND_FILES)
!           flags |= EW_FILE;
!       else if (xp->xp_context == EXPAND_FILES_IN_PATH)
!           flags |= (EW_FILE | EW_PATH);
!       else
!           flags = (flags | EW_DIR) & ~EW_FILE;
!       if (options & WILD_ICASE)
!           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
!       if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0)
!       {
!           int     j;
! 
!           for (j = 0; j < *num_file; ++j)
!           {
!               char_u  *ptr = (*file)[j];
! 
!               while (*ptr != NUL)
!               {
!                   if (p_csl[0] == 's' && *ptr == '\\')
!                       *ptr = '/';
!                   else if (p_csl[0] == 'b' && *ptr == '/')
!                       *ptr = '\\';
!                   ptr += (*mb_ptr2len)(ptr);
!               }
!           }
!       }
! #endif
!       return ret;
!     }
  
      *file = (char_u **)"";
      *num_file = 0;
--- 2336,2342 ----
      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;
***************
*** 2222,2298 ****
        ret = ExpandUserDefined(xp, &regmatch, num_file, file);
  # endif
      else
!     {
!       static struct expgen
!       {
!           int         context;
!           char_u      *((*func)(expand_T *, int));
!           int         ic;
!           int         escaped;
!       } tab[] =
!       {
!           {EXPAND_COMMANDS, get_command_name, FALSE, TRUE},
!           {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE},
!           {EXPAND_MAPCLEAR, get_mapclear_arg, TRUE, TRUE},
!           {EXPAND_MESSAGES, get_messages_arg, TRUE, TRUE},
!           {EXPAND_HISTORY, get_history_arg, TRUE, TRUE},
!           {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE},
!           {EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, FALSE, TRUE},
!           {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE},
!           {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE},
!           {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE},
! # ifdef FEAT_EVAL
!           {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE},
!           {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE},
!           {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE},
!           {EXPAND_DISASSEMBLE, get_disassemble_argument, FALSE, TRUE},
!           {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE},
! # endif
! # ifdef FEAT_MENU
!           {EXPAND_MENUS, get_menu_name, FALSE, TRUE},
!           {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE},
! # endif
! # ifdef FEAT_SYN_HL
!           {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE},
! # endif
! # ifdef FEAT_PROFILE
!           {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE},
! # endif
!           {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE},
!           {EXPAND_EVENTS, get_event_name, TRUE, FALSE},
!           {EXPAND_AUGROUP, get_augroup_name, TRUE, FALSE},
! # ifdef FEAT_CSCOPE
!           {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE},
! # endif
! # ifdef FEAT_SIGNS
!           {EXPAND_SIGN, get_sign_name, TRUE, TRUE},
! # endif
! # ifdef FEAT_PROFILE
!           {EXPAND_PROFILE, get_profile_name, TRUE, TRUE},
! # endif
! # if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
!           {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE},
!           {EXPAND_LOCALES, get_locales, TRUE, FALSE},
! # endif
!           {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE},
!           {EXPAND_USER, get_users, TRUE, FALSE},
!           {EXPAND_ARGLIST, get_arglist_name, TRUE, FALSE},
!       };
!       int     i;
! 
!       // Find a context in the table and call the ExpandGeneric() with the
!       // right function to do the expansion.
!       ret = FAIL;
!       for (i = 0; i < (int)ARRAY_LENGTH(tab); ++i)
!           if (xp->xp_context == tab[i].context)
!           {
!               if (tab[i].ic)
!                   regmatch.rm_ic = TRUE;
!               ret = ExpandGeneric(xp, &regmatch, num_file, file,
!                                               tab[i].func, tab[i].escaped);
!               break;
!           }
!     }
  
      vim_regfree(regmatch.regprog);
      vim_free(tofree);
--- 2426,2432 ----
        ret = ExpandUserDefined(xp, &regmatch, num_file, file);
  # endif
      else
!       ret = ExpandOther(xp, &regmatch, num_file, file);
  
      vim_regfree(regmatch.regprog);
      vim_free(tofree);
*** ../vim-8.2.4355/src/version.c       2022-02-12 11:51:20.048953988 +0000
--- src/version.c       2022-02-12 12:02:00.343789971 +0000
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     4356,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
29. Your phone bill comes to your doorstep in a box.

 /// Bram Moolenaar -- [email protected] -- 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220212120431.8651A1C0D2C%40moolenaar.net.

Raspunde prin e-mail lui