0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 8.2.4463: completion only uses strict matching

Problem:    Completion only uses strict matching.
Solution:   Add the "fuzzy" item for 'wildoptions'. (Yegappan Lakshmanan,
            closes #9803)
This commit is contained in:
Yegappan Lakshmanan
2022-02-24 13:28:41 +00:00
committed by Bram Moolenaar
parent 9c9be05b17
commit 38b85cb4d7
14 changed files with 856 additions and 123 deletions

View File

@@ -9087,6 +9087,14 @@ A jump table for the options with a short description can be found at |Q_op|.
feature} feature}
A list of words that change how |cmdline-completion| is done. A list of words that change how |cmdline-completion| is done.
The following values are supported: 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 pum Display the completion matches using the popupmenu
in the same style as the |ins-completion-menu|. in the same style as the |ins-completion-menu|.
tagfile When using CTRL-D to list matching tags, the kind of tagfile When using CTRL-D to list matching tags, the kind of

View File

@@ -2728,10 +2728,12 @@ ExpandBufnames(
int round; int round;
char_u *p; char_u *p;
int attempt; int attempt;
char_u *patc; char_u *patc = NULL;
#ifdef FEAT_VIMINFO #ifdef FEAT_VIMINFO
bufmatch_T *matches = NULL; bufmatch_T *matches = NULL;
#endif #endif
int fuzzy;
fuzmatch_str_T *fuzmatch = NULL;
*num_file = 0; // return values in case of FAIL *num_file = 0; // return values in case of FAIL
*file = NULL; *file = NULL;
@@ -2741,32 +2743,42 @@ ExpandBufnames(
return FAIL; return FAIL;
#endif #endif
// Make a copy of "pat" and change "^" to "\(^\|[\/]\)". fuzzy = cmdline_fuzzy_complete(pat);
if (*pat == '^')
// Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular
// expression matching)
if (!fuzzy)
{ {
patc = alloc(STRLEN(pat) + 11); if (*pat == '^')
if (patc == NULL) {
return FAIL; patc = alloc(STRLEN(pat) + 11);
STRCPY(patc, "\\(^\\|[\\/]\\)"); if (patc == NULL)
STRCPY(patc + 11, pat + 1); 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 == 0: try match with '\<', match at start of word
// attempt == 1: try match without '\<', match anywhere // attempt == 1: try match without '\<', match anywhere
for (attempt = 0; attempt <= 1; ++attempt) for (attempt = 0; attempt <= (fuzzy ? 0 : 1); ++attempt)
{ {
regmatch_T regmatch; regmatch_T regmatch;
int score = 0;
if (attempt > 0 && patc == pat) if (!fuzzy)
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) if (attempt > 0 && patc == pat)
vim_free(patc); break; // there was no anchor, no need to try again
return FAIL; 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. // round == 1: Count the matches.
@@ -2786,7 +2798,22 @@ ExpandBufnames(
continue; continue;
#endif #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 (p != NULL)
{ {
if (round == 1) if (round == 1)
@@ -2797,16 +2824,27 @@ ExpandBufnames(
p = home_replace_save(buf, p); p = home_replace_save(buf, p);
else else
p = vim_strsave(p); p = vim_strsave(p);
#ifdef FEAT_VIMINFO
if (matches != NULL) if (!fuzzy)
{ {
matches[count].buf = buf; #ifdef FEAT_VIMINFO
matches[count].match = p; if (matches != NULL)
count++; {
matches[count].buf = buf;
matches[count].match = p;
count++;
}
else
#endif
(*file)[count++] = p;
} }
else else
#endif {
(*file)[count++] = p; fuzmatch[count].idx = count;
fuzmatch[count].str = p;
fuzmatch[count].score = score;
count++;
}
} }
} }
} }
@@ -2814,47 +2852,72 @@ ExpandBufnames(
break; break;
if (round == 1) if (round == 1)
{ {
*file = ALLOC_MULT(char_u *, count); if (!fuzzy)
if (*file == NULL)
{ {
vim_regfree(regmatch.regprog); *file = ALLOC_MULT(char_u *, count);
if (patc != pat) if (*file == NULL)
vim_free(patc); {
return FAIL; vim_regfree(regmatch.regprog);
} if (patc != pat)
vim_free(patc);
return FAIL;
}
#ifdef FEAT_VIMINFO #ifdef FEAT_VIMINFO
if (options & WILD_BUFLASTUSED) if (options & WILD_BUFLASTUSED)
matches = ALLOC_MULT(bufmatch_T, count); matches = ALLOC_MULT(bufmatch_T, count);
#endif #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 if (!fuzzy)
break; {
vim_regfree(regmatch.regprog);
if (count) // match(es) found, break here
break;
}
} }
if (patc != pat) if (!fuzzy && patc != pat)
vim_free(patc); vim_free(patc);
#ifdef FEAT_VIMINFO #ifdef FEAT_VIMINFO
if (matches != NULL) if (!fuzzy)
{ {
int i; if (matches != NULL)
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++) int i;
(*file)[i-1] = matches[i].match; if (count > 1)
(*file)[count-1] = matches[0].match; 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 }
{ else
for (i = 0; i < count; i++) {
(*file)[i] = matches[i].match; if (fuzzymatches_to_strmatches(fuzmatch, file, count, FALSE) == FAIL)
} return FAIL;
vim_free(matches);
} }
#endif #endif

View File

@@ -18,7 +18,8 @@ static int cmd_showtail; // Only show path tail in lists ?
static void set_expand_context(expand_T *xp); static void set_expand_context(expand_T *xp);
static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, static int ExpandGeneric(expand_T *xp, regmatch_T *regmatch,
char_u ***matches, int *numMatches, 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 ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int);
static int expand_showtail(expand_T *xp); static int expand_showtail(expand_T *xp);
static int expand_shellcmd(char_u *filepat, char_u ***matches, int *numMatches, int flagsarg); 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]) #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. * sort function for the completion matches.
* <SNR> functions should be sorted to the end. * <SNR> functions should be sorted to the end.
@@ -195,9 +233,14 @@ nextwild(
} }
else 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. // Translate string into pattern and expand it.
if ((p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, if (p1 == NULL)
xp->xp_context)) == NULL)
p2 = NULL; p2 = NULL;
else else
{ {
@@ -2188,9 +2231,15 @@ expand_cmdline(
// add star to file name, or convert to regexp if not exp. files. // add star to file name, or convert to regexp if not exp. files.
xp->xp_pattern_len = (int)(str + col - xp->xp_pattern); xp->xp_pattern_len = (int)(str + col - xp->xp_pattern);
file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); if (cmdline_fuzzy_completion_supported(xp))
if (file_str == NULL) // If fuzzy matching, don't modify the search string
return EXPAND_UNSUCCESSFUL; 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) if (p_wic)
options += WILD_ICASE; options += WILD_ICASE;
@@ -2317,6 +2366,7 @@ get_mapclear_arg(expand_T *xp UNUSED, int idx)
*/ */
static int static int
ExpandOther( ExpandOther(
char_u *pat,
expand_T *xp, expand_T *xp,
regmatch_T *rmp, regmatch_T *rmp,
char_u ***matches, char_u ***matches,
@@ -2386,10 +2436,16 @@ ExpandOther(
{ {
if (xp->xp_context == tab[i].context) 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) if (tab[i].ic)
rmp->rm_ic = TRUE; rmp->rm_ic = TRUE;
ret = ExpandGeneric(xp, rmp, matches, numMatches, ret = ExpandGeneric(xp, rmp, matches, numMatches,
tab[i].func, tab[i].escaped); tab[i].func, tab[i].escaped,
fuzzy ? pat : NULL);
break; break;
} }
} }
@@ -2530,7 +2586,7 @@ ExpandFromContext(
if (xp->xp_context == EXPAND_SETTINGS if (xp->xp_context == EXPAND_SETTINGS
|| xp->xp_context == EXPAND_BOOL_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) else if (xp->xp_context == EXPAND_MAPPINGS)
ret = ExpandMappings(&regmatch, numMatches, matches); ret = ExpandMappings(&regmatch, numMatches, matches);
# if defined(FEAT_EVAL) # if defined(FEAT_EVAL)
@@ -2538,7 +2594,7 @@ ExpandFromContext(
ret = ExpandUserDefined(xp, &regmatch, matches, numMatches); ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
# endif # endif
else else
ret = ExpandOther(xp, &regmatch, matches, numMatches); ret = ExpandOther(pat, xp, &regmatch, matches, numMatches);
vim_regfree(regmatch.regprog); vim_regfree(regmatch.regprog);
vim_free(tofree); vim_free(tofree);
@@ -2553,6 +2609,9 @@ ExpandFromContext(
* obtain strings, one by one. The strings are matched against a regexp * obtain strings, one by one. The strings are matched against a regexp
* program. Matching strings are copied into an array, which is returned. * 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). * Returns OK when no problems encountered, FAIL for error (out of memory).
*/ */
static int static int
@@ -2563,12 +2622,17 @@ ExpandGeneric(
int *numMatches, int *numMatches,
char_u *((*func)(expand_T *, int)), char_u *((*func)(expand_T *, int)),
// returns a string from the list // returns a string from the list
int escaped) int escaped,
char_u *fuzzystr)
{ {
int i; int i;
int count = 0; int count = 0;
int round; int round;
char_u *str; char_u *str;
fuzmatch_str_T *fuzmatch = NULL;
int score = 0;
int fuzzy = (fuzzystr != NULL);
int funcsort = FALSE;
// do this loop twice: // do this loop twice:
// round == 0: count the number of matching names // round == 0: count the number of matching names
@@ -2583,7 +2647,8 @@ ExpandGeneric(
if (*str == NUL) // skip empty strings if (*str == NUL) // skip empty strings
continue; 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) if (round)
{ {
@@ -2594,11 +2659,20 @@ ExpandGeneric(
if (str == NULL) if (str == NULL)
{ {
FreeWild(count, *matches); FreeWild(count, *matches);
if (fuzzy)
fuzmatch_str_free(fuzmatch, count);
*numMatches = 0; *numMatches = 0;
*matches = NULL; *matches = NULL;
return FAIL; 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 # ifdef FEAT_MENU
if (func == get_menu_names && str != NULL) if (func == get_menu_names && str != NULL)
{ {
@@ -2616,8 +2690,11 @@ ExpandGeneric(
{ {
if (count == 0) if (count == 0)
return OK; return OK;
*matches = ALLOC_MULT(char_u *, count); if (fuzzy)
if (*matches == NULL) fuzmatch = ALLOC_MULT(fuzmatch_str_T, count);
else
*matches = ALLOC_MULT(char_u *, count);
if ((fuzzy && (fuzmatch == NULL)) || (*matches == NULL))
{ {
*numMatches = 0; *numMatches = 0;
*matches = NULL; *matches = NULL;
@@ -2635,11 +2712,18 @@ ExpandGeneric(
|| xp->xp_context == EXPAND_FUNCTIONS || xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC || xp->xp_context == EXPAND_USER_FUNC
|| xp->xp_context == EXPAND_DISASSEMBLE) || xp->xp_context == EXPAND_DISASSEMBLE)
{
// <SNR> functions should be sorted to the end. // <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); sort_func_compare);
}
else else
sort_strings(*matches, *numMatches); {
if (!fuzzy)
sort_strings(*matches, *numMatches);
}
} }
#if defined(FEAT_SYN_HL) #if defined(FEAT_SYN_HL)
@@ -2647,6 +2731,11 @@ ExpandGeneric(
// they don't show up when getting normal highlight names by ID. // they don't show up when getting normal highlight names by ID.
reset_expand_highlight(); reset_expand_highlight();
#endif #endif
if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count,
funcsort) == FAIL)
return FAIL;
return OK; return OK;
} }

View 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 int
ExpandSettings( ExpandSettings(
expand_T *xp, expand_T *xp,
regmatch_T *regmatch, regmatch_T *regmatch,
int *num_file, char_u *fuzzystr,
char_u ***file) int *numMatches,
char_u ***matches)
{ {
int num_normal = 0; // Nr of matching non-term-code settings int num_normal = 0; // Nr of matching non-term-code settings
int num_term = 0; // Nr of matching terminal 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]; char_u name_buf[MAX_KEY_NAME_LEN];
static char *(names[]) = {"all", "termcap"}; static char *(names[]) = {"all", "termcap"};
int ic = regmatch->rm_ic; // remember the ignore-case flag 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: // do this loop twice:
// loop == 0: count the number of matching options // loop == 0: count the number of matching options
@@ -6475,13 +6537,16 @@ ExpandSettings(
if (xp->xp_context != EXPAND_BOOL_SETTINGS) if (xp->xp_context != EXPAND_BOOL_SETTINGS)
{ {
for (match = 0; match < (int)ARRAY_LENGTH(names); ++match) 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) if (loop == 0)
num_normal++; num_normal++;
else else
(*file)[count++] = vim_strsave((char_u *)names[match]); count++;
} }
}
} }
for (opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL; for (opt_idx = 0; (str = (char_u *)options[opt_idx].fullname) != NULL;
opt_idx++) opt_idx++)
@@ -6494,28 +6559,9 @@ ExpandSettings(
is_term_opt = istermoption_idx(opt_idx); is_term_opt = istermoption_idx(opt_idx);
if (is_term_opt && num_normal > 0) if (is_term_opt && num_normal > 0)
continue; continue;
match = FALSE;
if (vim_regexec(regmatch, str, (colnr_T)0) if (match_str(str, regmatch, *matches, count, (loop == 0),
|| (options[opt_idx].shortname != NULL fuzzy, fuzzystr, fuzmatch))
&& vim_regexec(regmatch,
(char_u *)options[opt_idx].shortname, (colnr_T)0)))
match = TRUE;
else if (is_term_opt)
{
name_buf[0] = '<';
name_buf[1] = 't';
name_buf[2] = '_';
name_buf[3] = str[2];
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 (loop == 0)
{ {
@@ -6525,9 +6571,46 @@ ExpandSettings(
num_normal++; num_normal++;
} }
else else
(*file)[count++] = vim_strsave(str); 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] = '<';
name_buf[1] = 't';
name_buf[2] = '_';
name_buf[3] = str[2];
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 * Check terminal key codes, these are not in the option table
*/ */
@@ -6544,9 +6627,14 @@ ExpandSettings(
name_buf[3] = str[1]; name_buf[3] = str[1];
name_buf[4] = NUL; name_buf[4] = NUL;
match = FALSE; if (match_str(name_buf, regmatch, *matches, count,
if (vim_regexec(regmatch, name_buf, (colnr_T)0)) (loop == 0), fuzzy, fuzzystr, fuzmatch))
match = TRUE; {
if (loop == 0)
num_term++;
else
count++;
}
else else
{ {
name_buf[0] = '<'; name_buf[0] = '<';
@@ -6557,15 +6645,15 @@ ExpandSettings(
name_buf[5] = '>'; name_buf[5] = '>';
name_buf[6] = NUL; name_buf[6] = NUL;
if (vim_regexec(regmatch, name_buf, (colnr_T)0)) if (match_str(name_buf, regmatch, *matches, count,
match = TRUE; (loop == 0), fuzzy, fuzzystr,
} fuzmatch))
if (match) {
{ if (loop == 0)
if (loop == 0) num_term++;
num_term++; else
else count++;
(*file)[count++] = vim_strsave(name_buf); }
} }
} }
@@ -6579,31 +6667,49 @@ ExpandSettings(
STRCPY(name_buf + 1, str); STRCPY(name_buf + 1, str);
STRCAT(name_buf, ">"); 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) if (loop == 0)
num_term++; num_term++;
else else
(*file)[count++] = vim_strsave(name_buf); count++;
} }
} }
} }
if (loop == 0) if (loop == 0)
{ {
if (num_normal > 0) if (num_normal > 0)
*num_file = num_normal; *numMatches = num_normal;
else if (num_term > 0) else if (num_term > 0)
*num_file = num_term; *numMatches = num_term;
else else
return OK; return OK;
*file = ALLOC_MULT(char_u *, *num_file); if (!fuzzy)
if (*file == NULL)
{ {
*file = (char_u **)""; *matches = ALLOC_MULT(char_u *, *numMatches);
return FAIL; 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; return OK;
} }

View File

@@ -358,6 +358,7 @@ typedef enum {
// flags for the 'wildoptions' option // flags for the 'wildoptions' option
// each defined char should be unique over all values. // each defined char should be unique over all values.
#define WOP_FUZZY 'z'
#define WOP_TAGFILE 't' #define WOP_TAGFILE 't'
#define WOP_PUM 'p' #define WOP_PUM 'p'

View 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}; static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", "jsbterm", "pterm", "urxvt", "sgr", NULL};
#endif #endif
static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", "NONE", NULL}; 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 #ifdef FEAT_WAK
static char *(p_wak_values[]) = {"yes", "menu", "no", NULL}; static char *(p_wak_values[]) = {"yes", "menu", "no", NULL};
#endif #endif

View File

@@ -1,4 +1,5 @@
/* cmdexpand.c */ /* cmdexpand.c */
int cmdline_fuzzy_complete(char_u *fuzzystr);
int nextwild(expand_T *xp, int type, int options, int escape); 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); char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode);
void ExpandInit(expand_T *xp); void ExpandInit(expand_T *xp);

View File

@@ -63,7 +63,7 @@ void reset_modifiable(void);
void set_iminsert_global(void); void set_iminsert_global(void);
void set_imsearch_global(void); void set_imsearch_global(void);
void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags); 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 ExpandOldSetting(int *num_file, char_u ***file);
int shortmess(int x); int shortmess(int x);
void vimrc_found(char_u *fname, char_u *envname); void vimrc_found(char_u *fname, char_u *envname);

View 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); 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_matchfuzzy(typval_T *argvars, typval_T *rettv);
void f_matchfuzzypos(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: set ft=c : */

View File

@@ -1166,7 +1166,7 @@ searchit(
return submatch + 1; return submatch + 1;
} }
#ifdef FEAT_EVAL #if defined(FEAT_EVAL) || defined(FEAT_PROTO)
void void
set_search_direction(int cdir) set_search_direction(int cdir)
{ {
@@ -4107,7 +4107,7 @@ get_spat_last_idx(void)
} }
#endif #endif
#ifdef FEAT_EVAL #if defined(FEAT_EVAL) || defined(FEAT_PROTO)
/* /*
* "searchcount()" function * "searchcount()" function
*/ */
@@ -4230,6 +4230,7 @@ the_end:
restore_incsearch_state(); restore_incsearch_state();
#endif #endif
} }
#endif
/* /*
* Fuzzy string matching * Fuzzy string matching
@@ -4611,6 +4612,7 @@ fuzzy_match(
return numMatches != 0; return numMatches != 0;
} }
#if defined(FEAT_EVAL) || defined(FEAT_PROTO)
/* /*
* Sort the fuzzy matches in the descending order of the match score. * 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) * 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); do_fuzzymatch(argvars, rettv, TRUE);
} }
#endif #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);
}

View File

@@ -4516,3 +4516,11 @@ typedef struct {
int sw_same_win; // VIsual_active was not reset int sw_same_win; // VIsual_active was not reset
int sw_visual_active; int sw_visual_active;
} switchwin_T; } 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;

View File

@@ -11,6 +11,7 @@ set nomore
" Clear out t_WS, we don't want to resize the actual terminal. " Clear out t_WS, we don't want to resize the actual terminal.
let script = [ let script = [
\ '" DO NOT EDIT: Generated with gen_opt_test.vim', \ '" DO NOT EDIT: Generated with gen_opt_test.vim',
\ '" Used by test_options.vim.',
\ '', \ '',
\ 'let save_columns = &columns', \ 'let save_columns = &columns',
\ 'let save_lines = &lines', \ 'let save_lines = &lines',
@@ -152,7 +153,7 @@ let test_values = {
\ 'virtualedit': [['', 'all', 'all,block'], ['xxx']], \ 'virtualedit': [['', 'all', 'all,block'], ['xxx']],
\ 'whichwrap': [['', 'b,s', 'bs'], ['xxx']], \ 'whichwrap': [['', 'b,s', 'bs'], ['xxx']],
\ 'wildmode': [['', 'full', 'list:full', 'full,longest'], ['xxx', 'a4', 'full,full,full,full,full']], \ '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']], \ 'winaltkeys': [['menu', 'no'], ['', 'xxx']],
\ \
\ 'luadll': [[], []], \ 'luadll': [[], []],

View File

@@ -1574,6 +1574,12 @@ func Test_cmdwin_jump_to_win()
call assert_equal(1, winnr('$')) call assert_equal(1, winnr('$'))
endfunc endfunc
func Test_cmdwin_tabpage()
tabedit
call assert_fails("silent norm q/g :I\<Esc>", 'E11:')
tabclose!
endfunc
func Test_cmdwin_interrupted() func Test_cmdwin_interrupted()
CheckFeature cmdwin CheckFeature cmdwin
CheckScreendump CheckScreendump
@@ -2438,4 +2444,321 @@ func Test_cmdline_complete_dlist()
call assert_equal("\"dlist 10 /pat/ | chistory", @:) call assert_equal("\"dlist 10 /pat/ | chistory", @:)
endfunc 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: shiftwidth=2 sts=2 expandtab

View File

@@ -754,6 +754,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
4463,
/**/ /**/
4462, 4462,
/**/ /**/