Patch 8.2.4463
Problem:    Completion only uses strict matching.
Solution:   Add the "fuzzy" item for 'wildoptions'. (Yegappan Lakshmanan,
            closes #9803)
Files:      runtime/doc/options.txt, src/buffer.c, src/cmdexpand.c,
            src/option.c, src/option.h, src/optionstr.c,
            src/proto/cmdexpand.pro, src/proto/option.pro,
            src/proto/search.pro, src/search.c, src/structs.h,
            src/testdir/gen_opt_test.vim, src/testdir/test_cmdline.vim


*** ../vim-8.2.4462/runtime/doc/options.txt     2022-02-18 17:50:00.445462060 
+0000
--- runtime/doc/options.txt     2022-02-24 13:16:58.937971253 +0000
***************
*** 9058,9063 ****
--- 9087,9100 ----
                        feature}
        A list of words that change how |cmdline-completion| is done.
        The following values are supported:
+         fuzzy         Use fuzzy matching to find completion matches. When
+                       this value is specified, wildcard expansion will not
+                       be used for completion.  The matches will be sorted by
+                       the "best match" rather than alphabetically sorted.
+                       This will find more matches than the wildcard
+                       expansion. Currently fuzzy matching based completion
+                       is not supported for file and directory names and
+                       instead wildcard expansion is used.
          pum           Display the completion matches using the popupmenu
                        in the same style as the |ins-completion-menu|.
          tagfile       When using CTRL-D to list matching tags, the kind of
*** ../vim-8.2.4462/src/buffer.c        2022-02-19 11:44:57.147232377 +0000
--- src/buffer.c        2022-02-24 13:16:58.937971253 +0000
***************
*** 2728,2737 ****
      int               round;
      char_u    *p;
      int               attempt;
!     char_u    *patc;
  #ifdef FEAT_VIMINFO
      bufmatch_T        *matches = NULL;
  #endif
  
      *num_file = 0;                // return values in case of FAIL
      *file = NULL;
--- 2728,2739 ----
      int               round;
      char_u    *p;
      int               attempt;
!     char_u    *patc = NULL;
  #ifdef FEAT_VIMINFO
      bufmatch_T        *matches = NULL;
  #endif
+     int               fuzzy;
+     fuzmatch_str_T  *fuzmatch = NULL;
  
      *num_file = 0;                // return values in case of FAIL
      *file = NULL;
***************
*** 2741,2772 ****
        return FAIL;
  #endif
  
!     // Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
!     if (*pat == '^')
      {
!       patc = alloc(STRLEN(pat) + 11);
!       if (patc == NULL)
!           return FAIL;
!       STRCPY(patc, "\\(^\\|[\\/]\\)");
!       STRCPY(patc + 11, pat + 1);
      }
-     else
-       patc = pat;
  
      // attempt == 0: try match with    '\<', match at start of word
      // attempt == 1: try match without '\<', match anywhere
!     for (attempt = 0; attempt <= 1; ++attempt)
      {
        regmatch_T      regmatch;
  
!       if (attempt > 0 && patc == pat)
!           break;      // there was no anchor, no need to try again
!       regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
!       if (regmatch.regprog == NULL)
        {
!           if (patc != pat)
!               vim_free(patc);
!           return FAIL;
        }
  
        // round == 1: Count the matches.
--- 2743,2784 ----
        return FAIL;
  #endif
  
!     fuzzy = cmdline_fuzzy_complete(pat);
! 
!     // Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular
!     // expression matching)
!     if (!fuzzy)
      {
!       if (*pat == '^')
!       {
!           patc = alloc(STRLEN(pat) + 11);
!           if (patc == NULL)
!               return FAIL;
!           STRCPY(patc, "\\(^\\|[\\/]\\)");
!           STRCPY(patc + 11, pat + 1);
!       }
!       else
!           patc = pat;
      }
  
      // attempt == 0: try match with    '\<', match at start of word
      // attempt == 1: try match without '\<', match anywhere
!     for (attempt = 0; attempt <= (fuzzy ? 0 : 1); ++attempt)
      {
        regmatch_T      regmatch;
+       int             score = 0;
  
!       if (!fuzzy)
        {
!           if (attempt > 0 && patc == pat)
!               break;  // there was no anchor, no need to try again
!           regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
!           if (regmatch.regprog == NULL)
!           {
!               if (patc != pat)
!                   vim_free(patc);
!               return FAIL;
!           }
        }
  
        // round == 1: Count the matches.
***************
*** 2786,2792 ****
                        continue;
  #endif
  
!               p = buflist_match(&regmatch, buf, p_wic);
                if (p != NULL)
                {
                    if (round == 1)
--- 2798,2819 ----
                        continue;
  #endif
  
!               if (!fuzzy)
!                   p = buflist_match(&regmatch, buf, p_wic);
!               else
!               {
!                   p = NULL;
!                   // first try matching with the short file name
!                   if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0)
!                       p = buf->b_sfname;
!                   if (p == NULL)
!                   {
!                       // next try matching with the full path file name
!                       if ((score = fuzzy_match_str(buf->b_ffname, pat)) != 0)
!                           p = buf->b_ffname;
!                   }
!               }
! 
                if (p != NULL)
                {
                    if (round == 1)
***************
*** 2797,2812 ****
                            p = home_replace_save(buf, p);
                        else
                            p = vim_strsave(p);
  #ifdef FEAT_VIMINFO
!                       if (matches != NULL)
                        {
!                           matches[count].buf = buf;
!                           matches[count].match = p;
                            count++;
                        }
-                       else
- #endif
-                           (*file)[count++] = p;
                    }
                }
            }
--- 2824,2850 ----
                            p = home_replace_save(buf, p);
                        else
                            p = vim_strsave(p);
+ 
+                       if (!fuzzy)
+                       {
  #ifdef FEAT_VIMINFO
!                           if (matches != NULL)
!                           {
!                               matches[count].buf = buf;
!                               matches[count].match = p;
!                               count++;
!                           }
!                           else
! #endif
!                               (*file)[count++] = p;
!                       }
!                       else
                        {
!                           fuzmatch[count].idx = count;
!                           fuzmatch[count].str = p;
!                           fuzmatch[count].score = score;
                            count++;
                        }
                    }
                }
            }
***************
*** 2814,2860 ****
                break;
            if (round == 1)
            {
!               *file = ALLOC_MULT(char_u *, count);
!               if (*file == NULL)
                {
!                   vim_regfree(regmatch.regprog);
!                   if (patc != pat)
!                       vim_free(patc);
!                   return FAIL;
!               }
  #ifdef FEAT_VIMINFO
!               if (options & WILD_BUFLASTUSED)
!                   matches = ALLOC_MULT(bufmatch_T, count);
  #endif
            }
        }
!       vim_regfree(regmatch.regprog);
!       if (count)              // match(es) found, break here
!           break;
      }
  
!     if (patc != pat)
        vim_free(patc);
  
  #ifdef FEAT_VIMINFO
!     if (matches != NULL)
      {
!       int i;
!       if (count > 1)
!           qsort(matches, count, sizeof(bufmatch_T), buf_compare);
!       // if the current buffer is first in the list, place it at the end
!       if (matches[0].buf == curbuf)
        {
!           for (i = 1; i < count; i++)
!               (*file)[i-1] = matches[i].match;
!           (*file)[count-1] = matches[0].match;
!       }
!       else
!       {
!           for (i = 0; i < count; i++)
!               (*file)[i] = matches[i].match;
        }
!       vim_free(matches);
      }
  #endif
  
--- 2852,2923 ----
                break;
            if (round == 1)
            {
!               if (!fuzzy)
                {
!                   *file = ALLOC_MULT(char_u *, count);
!                   if (*file == NULL)
!                   {
!                       vim_regfree(regmatch.regprog);
!                       if (patc != pat)
!                           vim_free(patc);
!                       return FAIL;
!                   }
  #ifdef FEAT_VIMINFO
!                   if (options & WILD_BUFLASTUSED)
!                       matches = ALLOC_MULT(bufmatch_T, count);
  #endif
+               }
+               else
+               {
+                   fuzmatch = ALLOC_MULT(fuzmatch_str_T, count);
+                   if (fuzmatch == NULL)
+                   {
+                       *num_file = 0;
+                       *file = NULL;
+                       return FAIL;
+                   }
+               }
            }
        }
! 
!       if (!fuzzy)
!       {
!           vim_regfree(regmatch.regprog);
!           if (count)          // match(es) found, break here
!               break;
!       }
      }
  
!     if (!fuzzy && patc != pat)
        vim_free(patc);
  
  #ifdef FEAT_VIMINFO
!     if (!fuzzy)
      {
!       if (matches != NULL)
        {
!           int i;
!           if (count > 1)
!               qsort(matches, count, sizeof(bufmatch_T), buf_compare);
!           // if the current buffer is first in the list, place it at the end
!           if (matches[0].buf == curbuf)
!           {
!               for (i = 1; i < count; i++)
!                   (*file)[i-1] = matches[i].match;
!               (*file)[count-1] = matches[0].match;
!           }
!           else
!           {
!               for (i = 0; i < count; i++)
!                   (*file)[i] = matches[i].match;
!           }
!           vim_free(matches);
        }
!     }
!     else
!     {
!       if (fuzzymatches_to_strmatches(fuzmatch, file, count, FALSE) == FAIL)
!           return FAIL;
      }
  #endif
  
*** ../vim-8.2.4462/src/cmdexpand.c     2022-02-17 11:26:38.717059014 +0000
--- src/cmdexpand.c     2022-02-24 13:16:58.937971253 +0000
***************
*** 18,24 ****
  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);
--- 18,25 ----
  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,
!                             char_u *fuzzystr);
  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);
***************
*** 40,45 ****
--- 41,83 ----
  #define SHOW_FILE_TEXT(m) (showtail ? sm_gettail(matches[m]) : matches[m])
  
  /*
+  * Returns TRUE if fuzzy completion is supported for a given cmdline 
completion
+  * context.
+  */
+     static int
+ cmdline_fuzzy_completion_supported(expand_T *xp)
+ {
+     return (vim_strchr(p_wop, WOP_FUZZY) != NULL
+           && xp->xp_context != EXPAND_BOOL_SETTINGS
+           && xp->xp_context != EXPAND_COLORS
+           && xp->xp_context != EXPAND_COMPILER
+           && xp->xp_context != EXPAND_DIRECTORIES
+           && xp->xp_context != EXPAND_FILES
+           && xp->xp_context != EXPAND_FILES_IN_PATH
+           && xp->xp_context != EXPAND_FILETYPE
+           && xp->xp_context != EXPAND_HELP
+           && xp->xp_context != EXPAND_MAPPINGS
+           && xp->xp_context != EXPAND_OLD_SETTING
+           && xp->xp_context != EXPAND_OWNSYNTAX
+           && xp->xp_context != EXPAND_PACKADD
+           && xp->xp_context != EXPAND_SHELLCMD
+           && xp->xp_context != EXPAND_TAGS
+           && xp->xp_context != EXPAND_TAGS_LISTFILES
+           && xp->xp_context != EXPAND_USER_DEFINED
+           && xp->xp_context != EXPAND_USER_LIST);
+ }
+ 
+ /*
+  * Returns TRUE if fuzzy completion for cmdline completion is enabled and
+  * 'fuzzystr' is not empty.
+  */
+     int
+ cmdline_fuzzy_complete(char_u *fuzzystr)
+ {
+     return vim_strchr(p_wop, WOP_FUZZY) != NULL && *fuzzystr != NUL;
+ }
+ 
+ /*
   * sort function for the completion matches.
   * <SNR> functions should be sorted to the end.
   */
***************
*** 195,203 ****
      }
      else
      {
        // Translate string into pattern and expand it.
!       if ((p1 = addstar(xp->xp_pattern, xp->xp_pattern_len,
!                                                    xp->xp_context)) == NULL)
            p2 = NULL;
        else
        {
--- 233,246 ----
      }
      else
      {
+       if (cmdline_fuzzy_completion_supported(xp))
+           // If fuzzy matching, don't modify the search string
+           p1 = vim_strsave(xp->xp_pattern);
+       else
+           p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ 
        // Translate string into pattern and expand it.
!       if (p1 == NULL)
            p2 = NULL;
        else
        {
***************
*** 2188,2196 ****
  
      // add star to file name, or convert to regexp if not exp. files.
      xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
!     file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
!     if (file_str == NULL)
!       return EXPAND_UNSUCCESSFUL;
  
      if (p_wic)
        options += WILD_ICASE;
--- 2231,2245 ----
  
      // add star to file name, or convert to regexp if not exp. files.
      xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
!     if (cmdline_fuzzy_completion_supported(xp))
!       // If fuzzy matching, don't modify the search string
!       file_str = vim_strsave(xp->xp_pattern);
!     else
!     {
!       file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
!       if (file_str == NULL)
!           return EXPAND_UNSUCCESSFUL;
!     }
  
      if (p_wic)
        options += WILD_ICASE;
***************
*** 2317,2322 ****
--- 2366,2372 ----
   */
      static int
  ExpandOther(
+       char_u          *pat,
        expand_T        *xp, 
        regmatch_T      *rmp,
        char_u          ***matches,
***************
*** 2386,2395 ****
      {
        if (xp->xp_context == tab[i].context)
        {
            if (tab[i].ic)
                rmp->rm_ic = TRUE;
            ret = ExpandGeneric(xp, rmp, matches, numMatches,
!                                               tab[i].func, tab[i].escaped);
            break;
        }
      }
--- 2436,2451 ----
      {
        if (xp->xp_context == tab[i].context)
        {
+           // Use fuzzy matching if 'wildoptions' has 'fuzzy'.
+           // If no search pattern is supplied, then don't use fuzzy
+           // matching and return all the found items.
+           int fuzzy = cmdline_fuzzy_complete(pat);
+ 
            if (tab[i].ic)
                rmp->rm_ic = TRUE;
            ret = ExpandGeneric(xp, rmp, matches, numMatches,
!                                               tab[i].func, tab[i].escaped,
!                                               fuzzy ? pat : NULL);
            break;
        }
      }
***************
*** 2530,2536 ****
  
      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)
--- 2586,2592 ----
  
      if (xp->xp_context == EXPAND_SETTINGS
            || xp->xp_context == EXPAND_BOOL_SETTINGS)
!       ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches);
      else if (xp->xp_context == EXPAND_MAPPINGS)
        ret = ExpandMappings(&regmatch, numMatches, matches);
  # if defined(FEAT_EVAL)
***************
*** 2538,2544 ****
        ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
  # endif
      else
!       ret = ExpandOther(xp, &regmatch, matches, numMatches);
  
      vim_regfree(regmatch.regprog);
      vim_free(tofree);
--- 2594,2600 ----
        ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
  # endif
      else
!       ret = ExpandOther(pat, xp, &regmatch, matches, numMatches);
  
      vim_regfree(regmatch.regprog);
      vim_free(tofree);
***************
*** 2553,2558 ****
--- 2609,2617 ----
   * obtain strings, one by one.        The strings are matched against a regexp
   * program.  Matching strings are copied into an array, which is returned.
   *
+  * If 'fuzzy' is TRUE, then fuzzy matching is used. Otherwise, regex matching
+  * is used.
+  *
   * Returns OK when no problems encountered, FAIL for error (out of memory).
   */
      static int
***************
*** 2563,2574 ****
      int               *numMatches,
      char_u    *((*func)(expand_T *, int)),
                                          // returns a string from the list
!     int               escaped)
  {
      int               i;
      int               count = 0;
      int               round;
      char_u    *str;
  
      // do this loop twice:
      // round == 0: count the number of matching names
--- 2622,2638 ----
      int               *numMatches,
      char_u    *((*func)(expand_T *, int)),
                                          // returns a string from the list
!     int               escaped,
!     char_u    *fuzzystr)
  {
      int               i;
      int               count = 0;
      int               round;
      char_u    *str;
+     fuzmatch_str_T    *fuzmatch = NULL;
+     int                       score = 0;
+     int               fuzzy = (fuzzystr != NULL);
+     int               funcsort = FALSE;
  
      // do this loop twice:
      // round == 0: count the number of matching names
***************
*** 2583,2589 ****
            if (*str == NUL)        // skip empty strings
                continue;
  
!           if (vim_regexec(regmatch, str, (colnr_T)0))
            {
                if (round)
                {
--- 2647,2654 ----
            if (*str == NUL)        // skip empty strings
                continue;
  
!           if (vim_regexec(regmatch, str, (colnr_T)0) ||
!                   (fuzzy && ((score = fuzzy_match_str(str, fuzzystr)) != 0)))
            {
                if (round)
                {
***************
*** 2594,2604 ****
                    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)
                    {
--- 2659,2678 ----
                    if (str == NULL)
                    {
                        FreeWild(count, *matches);
+                       if (fuzzy)
+                           fuzmatch_str_free(fuzmatch, count);
                        *numMatches = 0;
                        *matches = NULL;
                        return FAIL;
                    }
!                   if (fuzzy)
!                   {
!                       fuzmatch[count].idx = count;
!                       fuzmatch[count].str = str;
!                       fuzmatch[count].score = score;
!                   }
!                   else
!                       (*matches)[count] = str;
  # ifdef FEAT_MENU
                    if (func == get_menu_names && str != NULL)
                    {
***************
*** 2616,2623 ****
        {
            if (count == 0)
                return OK;
!           *matches = ALLOC_MULT(char_u *, count);
!           if (*matches == NULL)
            {
                *numMatches = 0;
                *matches = NULL;
--- 2690,2700 ----
        {
            if (count == 0)
                return OK;
!           if (fuzzy)
!               fuzmatch = ALLOC_MULT(fuzmatch_str_T, count);
!           else
!               *matches = ALLOC_MULT(char_u *, count);
!           if ((fuzzy && (fuzmatch == NULL)) || (*matches == NULL))
            {
                *numMatches = 0;
                *matches = NULL;
***************
*** 2635,2645 ****
                || xp->xp_context == EXPAND_FUNCTIONS
                || 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)
--- 2712,2729 ----
                || xp->xp_context == EXPAND_FUNCTIONS
                || xp->xp_context == EXPAND_USER_FUNC
                || xp->xp_context == EXPAND_DISASSEMBLE)
+       {
            // <SNR> functions should be sorted to the end.
!           funcsort = TRUE;
!           if (!fuzzy)
!               qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *),
                                                           sort_func_compare);
+       }
        else
!       {
!           if (!fuzzy)
!               sort_strings(*matches, *numMatches);
!       }
      }
  
  #if defined(FEAT_SYN_HL)
***************
*** 2647,2652 ****
--- 2731,2741 ----
      // they don't show up when getting normal highlight names by ID.
      reset_expand_highlight();
  #endif
+ 
+     if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count,
+                                                       funcsort) == FAIL)
+       return FAIL;
+ 
      return OK;
  }
  
*** ../vim-8.2.4462/src/option.c        2022-02-23 12:05:54.482835946 +0000
--- src/option.c        2022-02-24 13:16:58.937971253 +0000
***************
*** 6447,6458 ****
      }
  }
  
      int
  ExpandSettings(
      expand_T  *xp,
      regmatch_T        *regmatch,
!     int               *num_file,
!     char_u    ***file)
  {
      int               num_normal = 0;     // Nr of matching non-term-code 
settings
      int               num_term = 0;       // Nr of matching terminal code 
settings
--- 6447,6516 ----
      }
  }
  
+ /*
+  * Returns TRUE if 'str' either matches 'regmatch' or fuzzy matches 'pat'.
+  *
+  * If 'test_only' is TRUE and 'fuzzy' is FALSE and if 'str' matches the 
regular
+  * expression 'regmatch', then returns TRUE.  Otherwise returns FALSE.
+  *
+  * If 'test_only' is FALSE and 'fuzzy' is FALSE and if 'str' matches the
+  * regular expression 'regmatch', then stores the match in matches[idx] and
+  * returns TRUE.
+  *
+  * If 'test_only' is TRUE and 'fuzzy' is TRUE and if 'str' fuzzy matches
+  * 'fuzzystr', then returns TRUE. Otherwise returns FALSE.
+  *
+  * If 'test_only' is FALSE and 'fuzzy' is TRUE and if 'str' fuzzy matches
+  * 'fuzzystr', then stores the match details in fuzmatch[idx] and returns 
TRUE.
+  */
+     static int
+ match_str(
+       char_u          *str,
+       regmatch_T      *regmatch,
+       char_u          **matches,
+       int             idx,
+       int             test_only,
+       int             fuzzy,
+       char_u          *fuzzystr,
+       fuzmatch_str_T  *fuzmatch)
+ {
+     if (!fuzzy)
+     {
+       if (vim_regexec(regmatch, str, (colnr_T)0))
+       {
+           if (!test_only)
+               matches[idx] = vim_strsave(str);
+           return TRUE;
+       }
+     }
+     else
+     {
+       int score;
+ 
+       score = fuzzy_match_str(str, fuzzystr);
+       if (score != 0)
+       {
+           if (!test_only)
+           {
+               fuzmatch[idx].idx = idx;
+               fuzmatch[idx].str = vim_strsave(str);
+               fuzmatch[idx].score = score;
+           }
+ 
+           return TRUE;
+       }
+     }
+ 
+     return FALSE;
+ }
+ 
      int
  ExpandSettings(
      expand_T  *xp,
      regmatch_T        *regmatch,
!     char_u    *fuzzystr,
!     int               *numMatches,
!     char_u    ***matches)
  {
      int               num_normal = 0;     // Nr of matching non-term-code 
settings
      int               num_term = 0;       // Nr of matching terminal code 
settings
***************
*** 6465,6470 ****
--- 6523,6532 ----
      char_u    name_buf[MAX_KEY_NAME_LEN];
      static char *(names[]) = {"all", "termcap"};
      int               ic = regmatch->rm_ic;   // remember the ignore-case flag
+     int               fuzzy;
+     fuzmatch_str_T  *fuzmatch = NULL;
+ 
+     fuzzy = cmdline_fuzzy_complete(fuzzystr);
  
      // do this loop twice:
      // loop == 0: count the number of matching options
***************
*** 6475,6487 ****
        if (xp->xp_context != EXPAND_BOOL_SETTINGS)
        {
            for (match = 0; match < (int)ARRAY_LENGTH(names); ++match)
!               if (vim_regexec(regmatch, (char_u *)names[match], (colnr_T)0))
                {
                    if (loop == 0)
                        num_normal++;
                    else
!                       (*file)[count++] = vim_strsave((char_u *)names[match]);
                }
        }
        for (opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
                                                                    opt_idx++)
--- 6537,6552 ----
        if (xp->xp_context != EXPAND_BOOL_SETTINGS)
        {
            for (match = 0; match < (int)ARRAY_LENGTH(names); ++match)
!           {
!               if (match_str((char_u *)names[match], regmatch, *matches,
!                           count, (loop == 0), fuzzy, fuzzystr, fuzmatch))
                {
                    if (loop == 0)
                        num_normal++;
                    else
!                       count++;
                }
+           }
        }
        for (opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
                                                                    opt_idx++)
***************
*** 6494,6505 ****
            is_term_opt = istermoption_idx(opt_idx);
            if (is_term_opt && num_normal > 0)
                continue;
!           match = FALSE;
!           if (vim_regexec(regmatch, str, (colnr_T)0)
!                   || (options[opt_idx].shortname != NULL
                        && vim_regexec(regmatch,
!                          (char_u *)options[opt_idx].shortname, (colnr_T)0)))
!               match = TRUE;
            else if (is_term_opt)
            {
                name_buf[0] = '<';
--- 6559,6595 ----
            is_term_opt = istermoption_idx(opt_idx);
            if (is_term_opt && num_normal > 0)
                continue;
! 
!           if (match_str(str, regmatch, *matches, count, (loop == 0),
!                                               fuzzy, fuzzystr, fuzmatch))
!           {
!               if (loop == 0)
!               {
!                   if (is_term_opt)
!                       num_term++;
!                   else
!                       num_normal++;
!               }
!               else
!                   count++;
!           }
!           else if (!fuzzy && options[opt_idx].shortname != NULL
                        && vim_regexec(regmatch,
!                              (char_u *)options[opt_idx].shortname, 
(colnr_T)0))
!           {
!               // Compare against the abbreviated option name (for regular
!               // expression match). Fuzzy matching (previous if) already
!               // matches against both the expanded and abbreviated names.
!               if (loop == 0)
!               {
!                   if (is_term_opt)
!                       num_term++;
!                   else
!                       num_normal++;
!               }
!               else
!                   (*matches)[count++] = vim_strsave(str);
!           }
            else if (is_term_opt)
            {
                name_buf[0] = '<';
***************
*** 6509,6533 ****
                name_buf[4] = str[3];
                name_buf[5] = '>';
                name_buf[6] = NUL;
!               if (vim_regexec(regmatch, name_buf, (colnr_T)0))
!               {
!                   match = TRUE;
!                   str = name_buf;
!               }
!           }
!           if (match)
!           {
!               if (loop == 0)
                {
!                   if (is_term_opt)
                        num_term++;
                    else
!                       num_normal++;
                }
-               else
-                   (*file)[count++] = vim_strsave(str);
            }
        }
        /*
         * Check terminal key codes, these are not in the option table
         */
--- 6599,6616 ----
                name_buf[4] = str[3];
                name_buf[5] = '>';
                name_buf[6] = NUL;
! 
!               if (match_str(name_buf, regmatch, *matches, count, (loop == 0),
!                           fuzzy, fuzzystr, fuzmatch))
                {
!                   if (loop == 0)
                        num_term++;
                    else
!                       count++;
                }
            }
        }
+ 
        /*
         * Check terminal key codes, these are not in the option table
         */
***************
*** 6544,6552 ****
                name_buf[3] = str[1];
                name_buf[4] = NUL;
  
!               match = FALSE;
!               if (vim_regexec(regmatch, name_buf, (colnr_T)0))
!                   match = TRUE;
                else
                {
                    name_buf[0] = '<';
--- 6627,6640 ----
                name_buf[3] = str[1];
                name_buf[4] = NUL;
  
!               if (match_str(name_buf, regmatch, *matches, count,
!                                       (loop == 0), fuzzy, fuzzystr, fuzmatch))
!               {
!                   if (loop == 0)
!                       num_term++;
!                   else
!                       count++;
!               }
                else
                {
                    name_buf[0] = '<';
***************
*** 6557,6571 ****
                    name_buf[5] = '>';
                    name_buf[6] = NUL;
  
!                   if (vim_regexec(regmatch, name_buf, (colnr_T)0))
!                       match = TRUE;
!               }
!               if (match)
!               {
!                   if (loop == 0)
!                       num_term++;
!                   else
!                       (*file)[count++] = vim_strsave(name_buf);
                }
            }
  
--- 6645,6659 ----
                    name_buf[5] = '>';
                    name_buf[6] = NUL;
  
!                   if (match_str(name_buf, regmatch, *matches, count,
!                                           (loop == 0), fuzzy, fuzzystr,
!                                           fuzmatch))
!                   {
!                       if (loop == 0)
!                           num_term++;
!                       else
!                           count++;
!                   }
                }
            }
  
***************
*** 6579,6609 ****
                STRCPY(name_buf + 1, str);
                STRCAT(name_buf, ">");
  
!               if (vim_regexec(regmatch, name_buf, (colnr_T)0))
                {
                    if (loop == 0)
                        num_term++;
                    else
!                       (*file)[count++] = vim_strsave(name_buf);
                }
            }
        }
        if (loop == 0)
        {
            if (num_normal > 0)
!               *num_file = num_normal;
            else if (num_term > 0)
!               *num_file = num_term;
            else
                return OK;
!           *file = ALLOC_MULT(char_u *, *num_file);
!           if (*file == NULL)
            {
!               *file = (char_u **)"";
!               return FAIL;
            }
        }
      }
      return OK;
  }
  
--- 6667,6715 ----
                STRCPY(name_buf + 1, str);
                STRCAT(name_buf, ">");
  
!               if (match_str(name_buf, regmatch, *matches, count, (loop == 0),
!                           fuzzy, fuzzystr, fuzmatch))
                {
                    if (loop == 0)
                        num_term++;
                    else
!                       count++;
                }
            }
        }
        if (loop == 0)
        {
            if (num_normal > 0)
!               *numMatches = num_normal;
            else if (num_term > 0)
!               *numMatches = num_term;
            else
                return OK;
!           if (!fuzzy)
            {
!               *matches = ALLOC_MULT(char_u *, *numMatches);
!               if (*matches == NULL)
!               {
!                   *matches = (char_u **)"";
!                   return FAIL;
!               }
!           }
!           else
!           {
!               fuzmatch = ALLOC_MULT(fuzmatch_str_T, *numMatches);
!               if (fuzmatch == NULL)
!               {
!                   *matches = (char_u **)"";
!                   return FAIL;
!               }
            }
        }
      }
+ 
+     if (fuzzy &&
+       fuzzymatches_to_strmatches(fuzmatch, matches, count, FALSE) == FAIL)
+       return FAIL;
+ 
      return OK;
  }
  
*** ../vim-8.2.4462/src/option.h        2022-02-08 12:07:41.835496899 +0000
--- src/option.h        2022-02-24 13:16:58.937971253 +0000
***************
*** 358,363 ****
--- 358,364 ----
  
  // flags for the 'wildoptions' option
  // each defined char should be unique over all values.
+ #define WOP_FUZZY     'z'
  #define WOP_TAGFILE   't'
  #define WOP_PUM               'p'
  
*** ../vim-8.2.4462/src/optionstr.c     2022-02-08 12:07:41.835496899 +0000
--- src/optionstr.c     2022-02-24 13:16:58.937971253 +0000
***************
*** 57,63 ****
  static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", 
"jsbterm", "pterm", "urxvt", "sgr", NULL};
  #endif
  static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", 
"NONE", NULL};
! static char *(p_wop_values[]) = {"tagfile", "pum", NULL};
  #ifdef FEAT_WAK
  static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
  #endif
--- 57,63 ----
  static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", 
"jsbterm", "pterm", "urxvt", "sgr", NULL};
  #endif
  static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", 
"NONE", NULL};
! static char *(p_wop_values[]) = {"fuzzy", "tagfile", "pum", NULL};
  #ifdef FEAT_WAK
  static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
  #endif
*** ../vim-8.2.4462/src/proto/cmdexpand.pro     2022-02-08 12:07:41.835496899 
+0000
--- src/proto/cmdexpand.pro     2022-02-24 13:16:58.937971253 +0000
***************
*** 1,4 ****
--- 1,5 ----
  /* cmdexpand.c */
+ int cmdline_fuzzy_complete(char_u *fuzzystr);
  int nextwild(expand_T *xp, int type, int options, int escape);
  char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int 
mode);
  void ExpandInit(expand_T *xp);
*** ../vim-8.2.4462/src/proto/option.pro        2022-02-23 12:05:54.482835946 
+0000
--- src/proto/option.pro        2022-02-24 13:16:58.937971253 +0000
***************
*** 63,69 ****
  void set_iminsert_global(void);
  void set_imsearch_global(void);
  void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags);
! int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u 
***file);
  int ExpandOldSetting(int *num_file, char_u ***file);
  int shortmess(int x);
  void vimrc_found(char_u *fname, char_u *envname);
--- 63,69 ----
  void set_iminsert_global(void);
  void set_imsearch_global(void);
  void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags);
! int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char_u *pat, int 
*numMatches, char_u ***matches);
  int ExpandOldSetting(int *num_file, char_u ***file);
  int shortmess(int x);
  void vimrc_found(char_u *fname, char_u *envname);
*** ../vim-8.2.4462/src/proto/search.pro        2021-12-12 14:16:34.989862195 
+0000
--- src/proto/search.pro        2022-02-24 13:16:58.937971253 +0000
***************
*** 40,43 ****
--- 40,46 ----
  int fuzzy_match(char_u *str, char_u *pat_arg, int matchseq, int *outScore, 
int_u *matches, int maxMatches);
  void f_matchfuzzy(typval_T *argvars, typval_T *rettv);
  void f_matchfuzzypos(typval_T *argvars, typval_T *rettv);
+ int fuzzy_match_str(char_u *str, char_u *pat);
+ int fuzzymatches_to_strmatches(fuzmatch_str_T *fuzmatch, char_u ***matches, 
int       count, int funcsort);
+ void fuzmatch_str_free(fuzmatch_str_T *fuzmatch, int count);
  /* vim: set ft=c : */
*** ../vim-8.2.4462/src/search.c        2022-02-16 19:24:03.626162408 +0000
--- src/search.c        2022-02-24 13:16:58.941971243 +0000
***************
*** 1166,1172 ****
      return submatch + 1;
  }
  
! #ifdef FEAT_EVAL
      void
  set_search_direction(int cdir)
  {
--- 1166,1172 ----
      return submatch + 1;
  }
  
! #if defined(FEAT_EVAL) || defined(FEAT_PROTO)
      void
  set_search_direction(int cdir)
  {
***************
*** 4107,4113 ****
  }
  #endif
  
! #ifdef FEAT_EVAL
  /*
   * "searchcount()" function
   */
--- 4107,4113 ----
  }
  #endif
  
! #if defined(FEAT_EVAL) || defined(FEAT_PROTO)
  /*
   * "searchcount()" function
   */
***************
*** 4230,4235 ****
--- 4230,4236 ----
      restore_incsearch_state();
  #endif
  }
+ #endif
  
  /*
   * Fuzzy string matching
***************
*** 4611,4616 ****
--- 4612,4618 ----
      return numMatches != 0;
  }
  
+ #if defined(FEAT_EVAL) || defined(FEAT_PROTO)
  /*
   * Sort the fuzzy matches in the descending order of the match score.
   * For items with same score, retain the order using the index (stable sort)
***************
*** 4933,4937 ****
  {
      do_fuzzymatch(argvars, rettv, TRUE);
  }
- 
  #endif
--- 4935,5065 ----
  {
      do_fuzzymatch(argvars, rettv, TRUE);
  }
  #endif
+ 
+ /*
+  * Same as fuzzy_match_item_compare() except for use with a string match
+  */
+     static int
+ fuzzy_match_str_compare(const void *s1, const void *s2)
+ {
+     int               v1 = ((fuzmatch_str_T *)s1)->score;
+     int               v2 = ((fuzmatch_str_T *)s2)->score;
+     int               idx1 = ((fuzmatch_str_T *)s1)->idx;
+     int               idx2 = ((fuzmatch_str_T *)s2)->idx;
+ 
+     return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1;
+ }
+ 
+ /*
+  * Sort fuzzy matches by score
+  */
+     static void
+ fuzzy_match_str_sort(fuzmatch_str_T *fm, int sz)
+ {
+     // Sort the list by the descending order of the match score
+     qsort((void *)fm, (size_t)sz, sizeof(fuzmatch_str_T),
+           fuzzy_match_str_compare);
+ }
+ 
+ /*
+  * Same as fuzzy_match_item_compare() except for use with a function name
+  * string match. <SNR> functions should be sorted to the end.
+  */
+     static int
+ fuzzy_match_func_compare(const void *s1, const void *s2)
+ {
+     int               v1 = ((fuzmatch_str_T *)s1)->score;
+     int               v2 = ((fuzmatch_str_T *)s2)->score;
+     int               idx1 = ((fuzmatch_str_T *)s1)->idx;
+     int               idx2 = ((fuzmatch_str_T *)s2)->idx;
+     char_u    *str1 = ((fuzmatch_str_T *)s1)->str;
+     char_u    *str2 = ((fuzmatch_str_T *)s2)->str;
+ 
+     if (*str1 != '<' && *str2 == '<') return -1;
+     if (*str1 == '<' && *str2 != '<') return 1;
+     return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1;
+ }
+ 
+ /*
+  * Sort fuzzy matches of function names by score.
+  * <SNR> functions should be sorted to the end.
+  */
+     static void
+ fuzzy_match_func_sort(fuzmatch_str_T *fm, int sz)
+ {
+     // Sort the list by the descending order of the match score
+     qsort((void *)fm, (size_t)sz, sizeof(fuzmatch_str_T),
+               fuzzy_match_func_compare);
+ }
+ 
+ /*
+  * Fuzzy match 'pat' in 'str'. Returns 0 if there is no match. Otherwise,
+  * returns the match score.
+  */
+     int
+ fuzzy_match_str(char_u *str, char_u *pat)
+ {
+     int               score = 0;
+     int_u     matchpos[256];
+ 
+     if (str == NULL || pat == NULL)
+       return 0;
+ 
+     fuzzy_match(str, pat, FALSE, &score, matchpos,
+                               sizeof(matchpos) / sizeof(matchpos[0]));
+ 
+     return score;
+ }
+ 
+ /*
+  * Copy a list of fuzzy matches into a string list after sorting the matches 
by
+  * the fuzzy score. Frees the memory allocated for 'fuzmatch'.
+  * Returns OK on success and FAIL on memory allocation failure.
+  */
+     int
+ fuzzymatches_to_strmatches(
+       fuzmatch_str_T  *fuzmatch,
+       char_u          ***matches,
+       int             count,
+       int             funcsort)
+ {
+     int               i;
+ 
+     if (count <= 0)
+       return OK;
+ 
+     *matches = ALLOC_MULT(char_u *, count);
+     if (*matches == NULL)
+     {
+       for (i = 0; i < count; i++)
+           vim_free(fuzmatch[i].str);
+       vim_free(fuzmatch);
+       return FAIL;
+     }
+ 
+     // Sort the list by the descending order of the match score
+     if (funcsort)
+       fuzzy_match_func_sort((void *)fuzmatch, (size_t)count);
+     else
+       fuzzy_match_str_sort((void *)fuzmatch, (size_t)count);
+ 
+     for (i = 0; i < count; i++)
+       (*matches)[i] = fuzmatch[i].str;
+     vim_free(fuzmatch);
+ 
+     return OK;
+ }
+ 
+ /*
+  * Free a list of fuzzy string matches.
+  */
+     void
+ fuzmatch_str_free(fuzmatch_str_T *fuzmatch, int count)
+ {
+     if (count <= 0 || fuzmatch == NULL)
+       return;
+     while (count--)
+       vim_free(fuzmatch[count].str);
+     vim_free(fuzmatch);
+ }
*** ../vim-8.2.4462/src/structs.h       2022-02-07 19:56:38.883286149 +0000
--- src/structs.h       2022-02-24 13:16:58.941971243 +0000
***************
*** 4516,4518 ****
--- 4516,4526 ----
      int               sw_same_win;        // VIsual_active was not reset
      int               sw_visual_active;
  } switchwin_T;
+ 
+ // Fuzzy matched string list item. Used for fuzzy match completion. Items are
+ // usually sorted by 'score'. The 'idx' member is used for stable-sort.
+ typedef struct {
+     int               idx;
+     char_u    *str;
+     int               score;
+ } fuzmatch_str_T;
*** ../vim-8.2.4462/src/testdir/gen_opt_test.vim        2020-10-30 
18:25:06.829693344 +0000
--- src/testdir/gen_opt_test.vim        2022-02-24 13:26:26.685789402 +0000
***************
*** 11,16 ****
--- 11,17 ----
  " Clear out t_WS, we don't want to resize the actual terminal.
  let script = [
        \ '" DO NOT EDIT: Generated with gen_opt_test.vim',
+       \ '" Used by test_options.vim.',
        \ '',
        \ 'let save_columns = &columns',
        \ 'let save_lines = &lines',
***************
*** 152,158 ****
        \ 'virtualedit': [['', 'all', 'all,block'], ['xxx']],
        \ 'whichwrap': [['', 'b,s', 'bs'], ['xxx']],
        \ 'wildmode': [['', 'full', 'list:full', 'full,longest'], ['xxx', 'a4', 
'full,full,full,full,full']],
!       \ 'wildoptions': [['', 'tagfile'], ['xxx']],
        \ 'winaltkeys': [['menu', 'no'], ['', 'xxx']],
        \
        \ 'luadll': [[], []],
--- 153,159 ----
        \ 'virtualedit': [['', 'all', 'all,block'], ['xxx']],
        \ 'whichwrap': [['', 'b,s', 'bs'], ['xxx']],
        \ 'wildmode': [['', 'full', 'list:full', 'full,longest'], ['xxx', 'a4', 
'full,full,full,full,full']],
!       \ 'wildoptions': [['', 'tagfile', 'pum', 'fuzzy'], ['xxx']],
        \ 'winaltkeys': [['menu', 'no'], ['', 'xxx']],
        \
        \ 'luadll': [[], []],
*** ../vim-8.2.4462/src/testdir/test_cmdline.vim        2022-02-16 
12:44:26.129908861 +0000
--- src/testdir/test_cmdline.vim        2022-02-24 13:16:58.941971243 +0000
***************
*** 1574,1579 ****
--- 1574,1585 ----
    call assert_equal(1, winnr('$'))
  endfunc
  
+ func Test_cmdwin_tabpage()
+   tabedit
+   call assert_fails("silent norm q/g  :I\<Esc>", 'E11:')
+   tabclose!
+ endfunc
+ 
  func Test_cmdwin_interrupted()
    CheckFeature cmdwin
    CheckScreendump
***************
*** 2438,2441 ****
--- 2444,2764 ----
    call assert_equal("\"dlist 10 /pat/ | chistory", @:)
  endfunc
  
+ " Test for 'fuzzy' in 'wildoptions' (fuzzy completion)
+ func Test_wildoptions_fuzzy()
+   " argument list (only for :argdel)
+   argadd change.py count.py charge.py
+   set wildoptions&
+   call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"argdel cge', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"argdel change.py charge.py', @:)
+   %argdelete
+ 
+   " autocmd group name fuzzy completion
+   set wildoptions&
+   augroup MyFuzzyGroup
+   augroup END
+   call feedkeys(":augroup mfg\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"augroup mfg', @:)
+   call feedkeys(":augroup My*p\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"augroup MyFuzzyGroup', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":augroup mfg\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"augroup MyFuzzyGroup', @:)
+   call feedkeys(":augroup My*p\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"augroup My*p', @:)
+   augroup! MyFuzzyGroup
+ 
+   " buffer name fuzzy completion
+   set wildoptions&
+   edit SomeFile.txt
+   enew
+   call feedkeys(":b SF\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"b SF', @:)
+   call feedkeys(":b S*File.txt\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"b SomeFile.txt', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":b SF\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"b SomeFile.txt', @:)
+   call feedkeys(":b S*File.txt\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"b S*File.txt', @:)
+   %bw!
+ 
+   " buffer name (full path) fuzzy completion
+   if has('unix')
+     set wildoptions&
+     call mkdir('Xcmd/Xstate/Xfile.js', 'p')
+     edit Xcmd/Xstate/Xfile.js
+     cd Xcmd/Xstate
+     enew
+     call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"b CmdStateFile', @:)
+     set wildoptions=fuzzy
+     call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_match('Xcmd/Xstate/Xfile.js$', @:)
+     cd -
+     call delete('Xcmd', 'rf')
+   endif
+ 
+   " :behave suboptions fuzzy completion
+   set wildoptions&
+   call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"behave xm', @:)
+   call feedkeys(":behave xt*m\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"behave xterm', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"behave xterm', @:)
+   call feedkeys(":behave xt*m\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"behave xt*m', @:)
+   let g:Sline = ''
+   call feedkeys(":behave win\<C-D>\<F4>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('mswin', g:Sline)
+   call assert_equal('"behave win', @:)
+ 
+   " colorscheme name fuzzy completion - NOT supported
+ 
+   " built-in command name fuzzy completion
+   set wildoptions&
+   call feedkeys(":sbwin\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"sbwin', @:)
+   call feedkeys(":sbr*d\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"sbrewind', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":sbwin\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"sbrewind', @:)
+   call feedkeys(":sbr*d\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"sbr*d', @:)
+ 
+   " compiler name fuzzy completion - NOT supported
+ 
+   " :cscope suboptions fuzzy completion
+   if has('cscope')
+     set wildoptions&
+     call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"cscope ret', @:)
+     call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"cscope reset', @:)
+     set wildoptions=fuzzy
+     call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"cscope reset', @:)
+     call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"cscope re*t', @:)
+   endif
+ 
+   " :diffget/:diffput buffer name fuzzy completion
+   new SomeBuffer
+   diffthis
+   new OtherBuffer
+   diffthis
+   set wildoptions&
+   call feedkeys(":diffget sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"diffget sbuf', @:)
+   call feedkeys(":diffput sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"diffput sbuf', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":diffget sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"diffget SomeBuffer', @:)
+   call feedkeys(":diffput sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"diffput SomeBuffer', @:)
+   %bw!
+ 
+   " directory name fuzzy completion - NOT supported
+ 
+   " environment variable name fuzzy completion
+   set wildoptions&
+   call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"echo $VUT', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"echo $VIMRUNTIME', @:)
+ 
+   " autocmd event fuzzy completion
+   set wildoptions&
+   call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"autocmd BWout', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"autocmd BufWipeout', @:)
+ 
+   " vim expression fuzzy completion
+   let g:PerPlaceCount = 10
+   set wildoptions&
+   call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"let c = ppc', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"let c = PerPlaceCount', @:)
+ 
+   " file name fuzzy completion - NOT supported
+ 
+   " files in path fuzzy completion - NOT supported
+ 
+   " filetype name fuzzy completion - NOT supported
+ 
+   " user defined function name completion
+   set wildoptions&
+   call feedkeys(":call Test_w_fuz\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"call Test_w_fuz', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":call Test_w_fuz\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"call Test_wildoptions_fuzzy()', @:)
+ 
+   " user defined command name completion
+   set wildoptions&
+   call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"MsFeat', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"MissingFeature', @:)
+ 
+   " :help tag fuzzy completion - NOT supported
+ 
+   " highlight group name fuzzy completion
+   set wildoptions&
+   call feedkeys(":highlight SKey\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"highlight SKey', @:)
+   call feedkeys(":highlight Sp*Key\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"highlight SpecialKey', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":highlight SKey\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"highlight SpecialKey', @:)
+   call feedkeys(":highlight Sp*Key\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"highlight Sp*Key', @:)
+ 
+   " :history suboptions fuzzy completion
+   set wildoptions&
+   call feedkeys(":history dg\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"history dg', @:)
+   call feedkeys(":history se*h\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"history search', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":history dg\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"history debug', @:)
+   call feedkeys(":history se*h\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"history se*h', @:)
+ 
+   " :language locale name fuzzy completion
+   if has('unix')
+     set wildoptions&
+     call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"lang psx', @:)
+     set wildoptions=fuzzy
+     call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"lang POSIX', @:)
+   endif
+ 
+   " :mapclear buffer argument fuzzy completion
+   set wildoptions&
+   call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"mapclear buf', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"mapclear <buffer>', @:)
+ 
+   " map name fuzzy completion - NOT supported
+ 
+   " menu name fuzzy completion
+   if has('gui_running')
+     set wildoptions&
+     call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"menu pup', @:)
+     set wildoptions=fuzzy
+     call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"menu PopUp.', @:)
+   endif
+ 
+   " :messages suboptions fuzzy completion
+   set wildoptions&
+   call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"messages clr', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"messages clear', @:)
+ 
+   " :set option name fuzzy completion
+   set wildoptions&
+   call feedkeys(":set brkopt\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set brkopt', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":set brkopt\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set breakindentopt', @:)
+   set wildoptions&
+   call feedkeys(":set fixeol\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set fixendofline', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":set fixeol\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set fixendofline', @:)
+ 
+   " :set <term_option>
+   set wildoptions&
+   call feedkeys(":set t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set t_EC', @:)
+   call feedkeys(":set <t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set <t_EC>', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":set t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set t_EC', @:)
+   call feedkeys(":set <t_E\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"set <t_EC>', @:)
+ 
+   " :packadd directory name fuzzy completion - NOT supported
+ 
+   " shell command name fuzzy completion - NOT supported
+ 
+   " :sign suboptions fuzzy completion
+   set wildoptions&
+   call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"sign ufe', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"sign undefine', @:)
+ 
+   " :syntax suboptions fuzzy completion
+   set wildoptions&
+   call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"syntax kwd', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"syntax keyword', @:)
+ 
+   " syntax group name fuzzy completion
+   set wildoptions&
+   call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"syntax list mpar', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"syntax list MatchParen', @:)
+ 
+   " :syntime suboptions fuzzy completion
+   if has('profile')
+     set wildoptions&
+     call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"syntime clr', @:)
+     set wildoptions=fuzzy
+     call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
+     call assert_equal('"syntime clear', @:)
+   endif
+ 
+   " tag name fuzzy completion - NOT supported
+ 
+   " tag name and file fuzzy completion - NOT supported
+ 
+   " user names fuzzy completion - how to test this functionality?
+ 
+   " user defined variable name fuzzy completion
+   let g:SomeVariable=10
+   set wildoptions&
+   call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"let SVar', @:)
+   set wildoptions=fuzzy
+   call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx')
+   call assert_equal('"let SomeVariable', @:)
+ 
+   set wildoptions&
+   %bw!
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.4462/src/version.c       2022-02-24 12:33:13.155233722 +0000
--- src/version.c       2022-02-24 13:19:06.419048552 +0000
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     4463,
  /**/

-- 
>From "know your smileys":
 [:-)   Frankenstein's monster

 /// 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/20220224132922.346521C04AB%40moolenaar.net.

Raspunde prin e-mail lui