patch 8.2.4463: completion only uses strict matching v8.2.4463
authorYegappan Lakshmanan <yegappan@yahoo.com>
Thu, 24 Feb 2022 13:28:41 +0000 (13:28 +0000)
committerBram Moolenaar <Bram@vim.org>
Thu, 24 Feb 2022 13:28:41 +0000 (13:28 +0000)
Problem:    Completion only uses strict matching.
Solution:   Add the "fuzzy" item for 'wildoptions'. (Yegappan Lakshmanan,
            closes #9803)

14 files changed:
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
src/version.c

index 3f7756203212d400d9c9d552fbeb31df6e1f8701..c8b40ec0483d02b68aee05f1702ae2be7cb5b9e7 100644 (file)
@@ -9087,6 +9087,14 @@ A jump table for the options with a short description can be found at |Q_op|.
                        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
index 27e8643870d697f1d73f396ea31e069acaaefb82..e580874906a64980107d5eda90bcc9f32d073e70 100644 (file)
@@ -2728,10 +2728,12 @@ ExpandBufnames(
     int                round;
     char_u     *p;
     int                attempt;
-    char_u     *patc;
+    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,32 +2743,42 @@ ExpandBufnames(
        return FAIL;
 #endif
 
-    // Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
-    if (*pat == '^')
+    fuzzy = cmdline_fuzzy_complete(pat);
+
+    // Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular
+    // expression matching)
+    if (!fuzzy)
     {
-       patc = alloc(STRLEN(pat) + 11);
-       if (patc == NULL)
-           return FAIL;
-       STRCPY(patc, "\\(^\\|[\\/]\\)");
-       STRCPY(patc + 11, pat + 1);
+       if (*pat == '^')
+       {
+           patc = alloc(STRLEN(pat) + 11);
+           if (patc == NULL)
+               return FAIL;
+           STRCPY(patc, "\\(^\\|[\\/]\\)");
+           STRCPY(patc + 11, pat + 1);
+       }
+       else
+           patc = pat;
     }
-    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)
+    for (attempt = 0; attempt <= (fuzzy ? 0 : 1); ++attempt)
     {
        regmatch_T      regmatch;
+       int             score = 0;
 
-       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 (!fuzzy)
        {
-           if (patc != pat)
-               vim_free(patc);
-           return FAIL;
+           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,7 +2798,22 @@ ExpandBufnames(
                        continue;
 #endif
 
-               p = buflist_match(&regmatch, buf, p_wic);
+               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,16 +2824,27 @@ ExpandBufnames(
                            p = home_replace_save(buf, p);
                        else
                            p = vim_strsave(p);
+
+                       if (!fuzzy)
+                       {
 #ifdef FEAT_VIMINFO
-                       if (matches != NULL)
+                           if (matches != NULL)
+                           {
+                               matches[count].buf = buf;
+                               matches[count].match = p;
+                               count++;
+                           }
+                           else
+#endif
+                               (*file)[count++] = p;
+                       }
+                       else
                        {
-                           matches[count].buf = buf;
-                           matches[count].match = p;
+                           fuzmatch[count].idx = count;
+                           fuzmatch[count].str = p;
+                           fuzmatch[count].score = score;
                            count++;
                        }
-                       else
-#endif
-                           (*file)[count++] = p;
                    }
                }
            }
@@ -2814,47 +2852,72 @@ ExpandBufnames(
                break;
            if (round == 1)
            {
-               *file = ALLOC_MULT(char_u *, count);
-               if (*file == NULL)
+               if (!fuzzy)
                {
-                   vim_regfree(regmatch.regprog);
-                   if (patc != pat)
-                       vim_free(patc);
-                   return FAIL;
-               }
+                   *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);
+                   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;
+                   }
+               }
            }
        }
-       vim_regfree(regmatch.regprog);
-       if (count)              // match(es) found, break here
-           break;
+
+       if (!fuzzy)
+       {
+           vim_regfree(regmatch.regprog);
+           if (count)          // match(es) found, break here
+               break;
+       }
     }
 
-    if (patc != pat)
+    if (!fuzzy && patc != pat)
        vim_free(patc);
 
 #ifdef FEAT_VIMINFO
-    if (matches != NULL)
+    if (!fuzzy)
     {
-       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)
+       if (matches != NULL)
        {
-           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;
+           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);
        }
-       vim_free(matches);
+    }
+    else
+    {
+       if (fuzzymatches_to_strmatches(fuzmatch, file, count, FALSE) == FAIL)
+           return FAIL;
     }
 #endif
 
index 2e4a0ebdee0cfe4e6c87dbe84eb8adb2e7c3f040..909706d1de9cf3cb9751d7c93b8d143949d23c7d 100644 (file)
@@ -18,7 +18,8 @@ static int    cmd_showtail;   // Only show path tail in lists ?
 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 *((*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);
@@ -39,6 +40,43 @@ static int compl_selected;
 
 #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,9 +233,14 @@ nextwild(
     }
     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 = addstar(xp->xp_pattern, xp->xp_pattern_len,
-                                                    xp->xp_context)) == NULL)
+       if (p1 == NULL)
            p2 = NULL;
        else
        {
@@ -2188,9 +2231,15 @@ expand_cmdline(
 
     // 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 (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,6 +2366,7 @@ get_mapclear_arg(expand_T *xp UNUSED, int idx)
  */
     static int
 ExpandOther(
+       char_u          *pat,
        expand_T        *xp, 
        regmatch_T      *rmp,
        char_u          ***matches,
@@ -2386,10 +2436,16 @@ ExpandOther(
     {
        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);
+                                               tab[i].func, tab[i].escaped,
+                                               fuzzy ? pat : NULL);
            break;
        }
     }
@@ -2530,7 +2586,7 @@ ExpandFromContext(
 
     if (xp->xp_context == EXPAND_SETTINGS
            || xp->xp_context == EXPAND_BOOL_SETTINGS)
-       ret = ExpandSettings(xp, &regmatch, numMatches, matches);
+       ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches);
     else if (xp->xp_context == EXPAND_MAPPINGS)
        ret = ExpandMappings(&regmatch, numMatches, matches);
 # if defined(FEAT_EVAL)
@@ -2538,7 +2594,7 @@ ExpandFromContext(
        ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
 # endif
     else
-       ret = ExpandOther(xp, &regmatch, matches, numMatches);
+       ret = ExpandOther(pat, xp, &regmatch, matches, numMatches);
 
     vim_regfree(regmatch.regprog);
     vim_free(tofree);
@@ -2553,6 +2609,9 @@ ExpandFromContext(
  * 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,12 +2622,17 @@ ExpandGeneric(
     int                *numMatches,
     char_u     *((*func)(expand_T *, int)),
                                          // returns a string from the list
-    int                escaped)
+    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,7 +2647,8 @@ ExpandGeneric(
            if (*str == NUL)        // skip empty strings
                continue;
 
-           if (vim_regexec(regmatch, str, (colnr_T)0))
+           if (vim_regexec(regmatch, str, (colnr_T)0) ||
+                   (fuzzy && ((score = fuzzy_match_str(str, fuzzystr)) != 0)))
            {
                if (round)
                {
@@ -2594,11 +2659,20 @@ ExpandGeneric(
                    if (str == NULL)
                    {
                        FreeWild(count, *matches);
+                       if (fuzzy)
+                           fuzmatch_str_free(fuzmatch, count);
                        *numMatches = 0;
                        *matches = NULL;
                        return FAIL;
                    }
-                   (*matches)[count] = str;
+                   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,8 +2690,11 @@ ExpandGeneric(
        {
            if (count == 0)
                return OK;
-           *matches = ALLOC_MULT(char_u *, count);
-           if (*matches == NULL)
+           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,11 +2712,18 @@ ExpandGeneric(
                || 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 *),
+           funcsort = TRUE;
+           if (!fuzzy)
+               qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *),
                                                           sort_func_compare);
+       }
        else
-           sort_strings(*matches, *numMatches);
+       {
+           if (!fuzzy)
+               sort_strings(*matches, *numMatches);
+       }
     }
 
 #if defined(FEAT_SYN_HL)
@@ -2647,6 +2731,11 @@ ExpandGeneric(
     // 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;
 }
 
index 44a0520ad02aee1b00bdfc458afaf790ca27aa0d..936906649dde0b8e1ecb898cd4407633054206ac 100644 (file)
@@ -6447,12 +6447,70 @@ set_context_in_set_cmd(
     }
 }
 
+/*
+ * 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,
-    int                *num_file,
-    char_u     ***file)
+    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,6 +6523,10 @@ ExpandSettings(
     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,13 +6537,16 @@ ExpandSettings(
        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 (match_str((char_u *)names[match], regmatch, *matches,
+                           count, (loop == 0), fuzzy, fuzzystr, fuzmatch))
                {
                    if (loop == 0)
                        num_normal++;
                    else
-                       (*file)[count++] = vim_strsave((char_u *)names[match]);
+                       count++;
                }
+           }
        }
        for (opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
                                                                    opt_idx++)
@@ -6494,12 +6559,37 @@ ExpandSettings(
            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
+
+           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)))
-               match = TRUE;
+                             (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,25 +6599,18 @@ ExpandSettings(
                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 (match_str(name_buf, regmatch, *matches, count, (loop == 0),
+                           fuzzy, fuzzystr, fuzmatch))
                {
-                   if (is_term_opt)
+                   if (loop == 0)
                        num_term++;
                    else
-                       num_normal++;
+                       count++;
                }
-               else
-                   (*file)[count++] = vim_strsave(str);
            }
        }
+
        /*
         * Check terminal key codes, these are not in the option table
         */
@@ -6544,9 +6627,14 @@ ExpandSettings(
                name_buf[3] = str[1];
                name_buf[4] = NUL;
 
-               match = FALSE;
-               if (vim_regexec(regmatch, name_buf, (colnr_T)0))
-                   match = TRUE;
+               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,15 +6645,15 @@ ExpandSettings(
                    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);
+                   if (match_str(name_buf, regmatch, *matches, count,
+                                           (loop == 0), fuzzy, fuzzystr,
+                                           fuzmatch))
+                   {
+                       if (loop == 0)
+                           num_term++;
+                       else
+                           count++;
+                   }
                }
            }
 
@@ -6579,31 +6667,49 @@ ExpandSettings(
                STRCPY(name_buf + 1, str);
                STRCAT(name_buf, ">");
 
-               if (vim_regexec(regmatch, name_buf, (colnr_T)0))
+               if (match_str(name_buf, regmatch, *matches, count, (loop == 0),
+                           fuzzy, fuzzystr, fuzmatch))
                {
                    if (loop == 0)
                        num_term++;
                    else
-                       (*file)[count++] = vim_strsave(name_buf);
+                       count++;
                }
            }
        }
        if (loop == 0)
        {
            if (num_normal > 0)
-               *num_file = num_normal;
+               *numMatches = num_normal;
            else if (num_term > 0)
-               *num_file = num_term;
+               *numMatches = num_term;
            else
                return OK;
-           *file = ALLOC_MULT(char_u *, *num_file);
-           if (*file == NULL)
+           if (!fuzzy)
            {
-               *file = (char_u **)"";
-               return FAIL;
+               *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;
 }
 
index ebbf94b069abcaa029ccba6a94642d2ce5542194..2116fe9951805dfe49b007ff78e436790fa8b767 100644 (file)
@@ -358,6 +358,7 @@ typedef enum {
 
 // 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'
 
index c8bef03099efae899a4839c590422e36038c76f9..5c3f76dc9e7e6ce3811fd2e5443d50a873dd5574 100644 (file)
@@ -57,7 +57,7 @@ static char *(p_tbis_values[]) = {"tiny", "small", "medium", "large", "huge", "g
 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};
+static char *(p_wop_values[]) = {"fuzzy", "tagfile", "pum", NULL};
 #ifdef FEAT_WAK
 static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
 #endif
index b64c6bbdc64bddc70953306a15c19cfc396f8f1d..5ffab8c54566f2cddace640c0fa15bfed709e5aa 100644 (file)
@@ -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);
index b58e79afefbdbb4db10d3a8b05b57453ee830288..373a60ca0e4785852a07180c8015ad6808df0dbe 100644 (file)
@@ -63,7 +63,7 @@ void reset_modifiable(void);
 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 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);
index 27c609e200743d04b5e12a9af918330ddc7d5374..bdd9213c757725449bc6b78894bcf56c7e04cd62 100644 (file)
@@ -40,4 +40,7 @@ void f_searchcount(typval_T *argvars, typval_T *rettv);
 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 : */
index dbc766f9f5bbc54de3a735cf3a039f255ba01184..00a9fdfc6df065ef4ba5c43c8bc4b3bf26261625 100644 (file)
@@ -1166,7 +1166,7 @@ searchit(
     return submatch + 1;
 }
 
-#ifdef FEAT_EVAL
+#if defined(FEAT_EVAL) || defined(FEAT_PROTO)
     void
 set_search_direction(int cdir)
 {
@@ -4107,7 +4107,7 @@ get_spat_last_idx(void)
 }
 #endif
 
-#ifdef FEAT_EVAL
+#if defined(FEAT_EVAL) || defined(FEAT_PROTO)
 /*
  * "searchcount()" function
  */
@@ -4230,6 +4230,7 @@ the_end:
     restore_incsearch_state();
 #endif
 }
+#endif
 
 /*
  * Fuzzy string matching
@@ -4611,6 +4612,7 @@ fuzzy_match(
     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,5 +4935,131 @@ f_matchfuzzypos(typval_T *argvars, typval_T *rettv)
 {
     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);
+}
index 1e759f5dfd0b77d814dcdaf5af4827dc0a4dce55..1a98004527c76d8695f309ca4f3685d183ed4daf 100644 (file)
@@ -4516,3 +4516,11 @@ typedef struct {
     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;
index abf11fc9dced85996c554fbf2cbb15fe5db6c925..e6b16c9def0f7b5f83c312e29130ab87afc027cb 100644 (file)
@@ -11,6 +11,7 @@ set nomore
 " 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,7 +153,7 @@ let test_values = {
       \ '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']],
+      \ 'wildoptions': [['', 'tagfile', 'pum', 'fuzzy'], ['xxx']],
       \ 'winaltkeys': [['menu', 'no'], ['', 'xxx']],
       \
       \ 'luadll': [[], []],
index 337cb429885b148493a03e648f959e4d8f9f5c8c..1ee0d718e2b5b5ca6a9a55df471b7bfb8322bb96 100644 (file)
@@ -1574,6 +1574,12 @@ func Test_cmdwin_jump_to_win()
   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,4 +2444,321 @@ func Test_cmdline_complete_dlist()
   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
index a80c568e4ac1dfc0e69eec611a0df6a0db052214..1491e81da6a1adb1c2824d5c7635a0e9ba5f909f 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4463,
 /**/
     4462,
 /**/