Patch 8.2.4463

19 views
Skip to first unread message

Bram Moolenaar

unread,
Feb 24, 2022, 8:29:29 AM2/24/22
to vim...@googlegroups.com

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 -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages