mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #21850 from zeertzjq/vim-8.2.4463
vim-patch:8.2.{4463,4465,4475,4477,4478,4479,4608}: fuzzy cmdline builtin completion
This commit is contained in:
commit
2093e574c6
@ -3000,6 +3000,10 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
|
||||
is applied to filter the results. Otherwise all the matches
|
||||
are returned. The 'wildignorecase' option always applies.
|
||||
|
||||
If the 'wildoptions' option contains "fuzzy", then fuzzy
|
||||
matching is used to get the completion matches. Otherwise
|
||||
regular expression matching is used.
|
||||
|
||||
If {type} is "cmdline", then the |cmdline-completion| result is
|
||||
returned. For example, to complete the possible values after
|
||||
a ":call" command: >
|
||||
|
@ -7126,6 +7126,14 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
global
|
||||
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 popup menu
|
||||
in the same style as the |ins-completion-menu|.
|
||||
tagfile When using CTRL-D to list matching tags, the kind of
|
||||
|
@ -88,6 +88,7 @@
|
||||
#include "nvim/regexp.h"
|
||||
#include "nvim/runtime.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/search.h"
|
||||
#include "nvim/sign.h"
|
||||
#include "nvim/spell.h"
|
||||
#include "nvim/statusline.h"
|
||||
@ -2337,7 +2338,6 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
|
||||
int round;
|
||||
char *p;
|
||||
int attempt;
|
||||
char *patc;
|
||||
bufmatch_T *matches = NULL;
|
||||
|
||||
*num_file = 0; // return values in case of FAIL
|
||||
@ -2347,31 +2347,40 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
|
||||
if (*pat == '^') {
|
||||
patc = xmalloc(strlen(pat) + 11);
|
||||
STRCPY(patc, "\\(^\\|[\\/]\\)");
|
||||
STRCPY(patc + 11, pat + 1);
|
||||
} else {
|
||||
patc = pat;
|
||||
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
||||
|
||||
char *patc = NULL;
|
||||
// Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular
|
||||
// expression matching)
|
||||
if (!fuzzy) {
|
||||
if (*pat == '^') {
|
||||
patc = xmalloc(strlen(pat) + 11);
|
||||
STRCPY(patc, "\\(^\\|[\\/]\\)");
|
||||
STRCPY(patc + 11, pat + 1);
|
||||
} else {
|
||||
patc = pat;
|
||||
}
|
||||
}
|
||||
|
||||
fuzmatch_str_T *fuzmatch = NULL;
|
||||
// attempt == 0: try match with '\<', match at start of word
|
||||
// attempt == 1: try match without '\<', match anywhere
|
||||
for (attempt = 0; attempt <= 1; attempt++) {
|
||||
if (attempt > 0 && patc == pat) {
|
||||
break; // there was no anchor, no need to try again
|
||||
}
|
||||
|
||||
for (attempt = 0; attempt <= (fuzzy ? 0 : 1); attempt++) {
|
||||
regmatch_T regmatch;
|
||||
regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC);
|
||||
if (regmatch.regprog == NULL) {
|
||||
if (patc != pat) {
|
||||
xfree(patc);
|
||||
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) {
|
||||
xfree(patc);
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
int score = 0;
|
||||
// round == 1: Count the matches.
|
||||
// round == 2: Build the array to keep the matches.
|
||||
for (round = 1; round <= 2; round++) {
|
||||
@ -2387,7 +2396,23 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p = buflist_match(®match, buf, p_wic);
|
||||
|
||||
if (!fuzzy) {
|
||||
p = buflist_match(®match, 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) {
|
||||
count++;
|
||||
@ -2397,12 +2422,20 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
|
||||
} else {
|
||||
p = xstrdup(p);
|
||||
}
|
||||
if (matches != NULL) {
|
||||
matches[count].buf = buf;
|
||||
matches[count].match = p;
|
||||
count++;
|
||||
|
||||
if (!fuzzy) {
|
||||
if (matches != NULL) {
|
||||
matches[count].buf = buf;
|
||||
matches[count].match = p;
|
||||
count++;
|
||||
} else {
|
||||
(*file)[count++] = p;
|
||||
}
|
||||
} else {
|
||||
(*file)[count++] = p;
|
||||
fuzmatch[count].idx = count;
|
||||
fuzmatch[count].str = p;
|
||||
fuzmatch[count].score = score;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2411,40 +2444,50 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
|
||||
break;
|
||||
}
|
||||
if (round == 1) {
|
||||
*file = xmalloc((size_t)count * sizeof(**file));
|
||||
|
||||
if (options & WILD_BUFLASTUSED) {
|
||||
matches = xmalloc((size_t)count * sizeof(*matches));
|
||||
if (!fuzzy) {
|
||||
*file = xmalloc((size_t)count * sizeof(**file));
|
||||
if (options & WILD_BUFLASTUSED) {
|
||||
matches = xmalloc((size_t)count * sizeof(*matches));
|
||||
}
|
||||
} else {
|
||||
fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T));
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
xfree(patc);
|
||||
}
|
||||
|
||||
if (matches != NULL) {
|
||||
if (count > 1) {
|
||||
qsort(matches, (size_t)count, sizeof(bufmatch_T), buf_time_compare);
|
||||
}
|
||||
if (!fuzzy) {
|
||||
if (matches != NULL) {
|
||||
if (count > 1) {
|
||||
qsort(matches, (size_t)count, sizeof(bufmatch_T), buf_time_compare);
|
||||
}
|
||||
|
||||
// if the current buffer is first in the list, place it at the end
|
||||
if (matches[0].buf == curbuf) {
|
||||
for (int i = 1; i < count; i++) {
|
||||
(*file)[i - 1] = matches[i].match;
|
||||
}
|
||||
(*file)[count - 1] = matches[0].match;
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
(*file)[i] = matches[i].match;
|
||||
// if the current buffer is first in the list, place it at the end
|
||||
if (matches[0].buf == curbuf) {
|
||||
for (int i = 1; i < count; i++) {
|
||||
(*file)[i - 1] = matches[i].match;
|
||||
}
|
||||
(*file)[count - 1] = matches[0].match;
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
(*file)[i] = matches[i].match;
|
||||
}
|
||||
}
|
||||
xfree(matches);
|
||||
}
|
||||
xfree(matches);
|
||||
} else {
|
||||
fuzzymatches_to_strmatches(fuzmatch, file, count, false);
|
||||
}
|
||||
|
||||
*num_file = count;
|
||||
|
@ -91,6 +91,38 @@ static int compl_selected;
|
||||
|
||||
#define SHOW_MATCH(m) (showtail ? showmatches_gettail(matches[m], false) : matches[m])
|
||||
|
||||
/// Returns true if fuzzy completion is supported for a given cmdline completion
|
||||
/// context.
|
||||
static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
|
||||
{
|
||||
return (wop_flags & WOP_FUZZY)
|
||||
&& 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_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.
|
||||
bool cmdline_fuzzy_complete(const char *const fuzzystr)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
|
||||
{
|
||||
return (wop_flags & WOP_FUZZY) && *fuzzystr != NUL;
|
||||
}
|
||||
|
||||
/// Sort function for the completion matches.
|
||||
/// <SNR> functions should be sorted to the end.
|
||||
static int sort_func_compare(const void *s1, const void *s2)
|
||||
@ -223,8 +255,13 @@ int nextwild(expand_T *xp, int type, int options, bool escape)
|
||||
// Get next/previous match for a previous expanded pattern.
|
||||
p2 = ExpandOne(xp, NULL, NULL, 0, type);
|
||||
} else {
|
||||
if (cmdline_fuzzy_completion_supported(xp)) {
|
||||
// If fuzzy matching, don't modify the search string
|
||||
p1 = xstrdup(xp->xp_pattern);
|
||||
} else {
|
||||
p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
|
||||
}
|
||||
// Translate string into pattern and expand it.
|
||||
p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
|
||||
const int use_options = (options
|
||||
| WILD_HOME_REPLACE
|
||||
| WILD_ADD_SLASH
|
||||
@ -1335,13 +1372,16 @@ static const char *set_cmd_index(const char *cmd, exarg_T *eap, expand_T *xp, in
|
||||
{
|
||||
const char *p = NULL;
|
||||
size_t len = 0;
|
||||
const bool fuzzy = cmdline_fuzzy_complete(cmd);
|
||||
|
||||
// Isolate the command and search for it in the command table.
|
||||
// Exceptions:
|
||||
// - the 'k' command can directly be followed by any character, but
|
||||
// do accept "keepmarks", "keepalt" and "keepjumps".
|
||||
// - the 'k' command can directly be followed by any character, but do
|
||||
// accept "keepmarks", "keepalt" and "keepjumps". As fuzzy matching can
|
||||
// find matches anywhere in the command name, do this only for command
|
||||
// expansion based on regular expression and not for fuzzy matching.
|
||||
// - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
|
||||
if (*cmd == 'k' && cmd[1] != 'e') {
|
||||
if (!fuzzy && (*cmd == 'k' && cmd[1] != 'e')) {
|
||||
eap->cmdidx = CMD_k;
|
||||
p = cmd + 1;
|
||||
} else {
|
||||
@ -1375,7 +1415,9 @@ static const char *set_cmd_index(const char *cmd, exarg_T *eap, expand_T *xp, in
|
||||
|
||||
eap->cmdidx = excmd_get_cmdidx(cmd, len);
|
||||
|
||||
if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
|
||||
// User defined commands support alphanumeric characters.
|
||||
// Also when doing fuzzy expansion, support alphanumeric characters.
|
||||
if ((cmd[0] >= 'A' && cmd[0] <= 'Z') || (fuzzy && *p != NUL)) {
|
||||
while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card
|
||||
p++;
|
||||
}
|
||||
@ -2330,7 +2372,12 @@ int expand_cmdline(expand_T *xp, const char *str, int col, int *matchcount, char
|
||||
// add star to file name, or convert to regexp if not exp. files.
|
||||
assert((str + col) - xp->xp_pattern >= 0);
|
||||
xp->xp_pattern_len = (size_t)((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 fuzzy matching, don't modify the search string
|
||||
file_str = xstrdup(xp->xp_pattern);
|
||||
} else {
|
||||
file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
|
||||
}
|
||||
|
||||
if (p_wic) {
|
||||
options += WILD_ICASE;
|
||||
@ -2490,7 +2537,7 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx)
|
||||
}
|
||||
|
||||
/// Do the expansion based on xp->xp_context and "rmp".
|
||||
static int ExpandOther(expand_T *xp, regmatch_T *rmp, char ***matches, int *numMatches)
|
||||
static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches, int *numMatches)
|
||||
{
|
||||
typedef CompleteListItemGetter ExpandFunc;
|
||||
static struct expgen {
|
||||
@ -2538,10 +2585,16 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, char ***matches, int *numM
|
||||
// right function to do the expansion.
|
||||
for (int i = 0; i < (int)ARRAY_SIZE(tab); i++) {
|
||||
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.
|
||||
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
||||
|
||||
if (tab[i].ic) {
|
||||
rmp->rm_ic = true;
|
||||
}
|
||||
ExpandGeneric(xp, rmp, matches, numMatches, tab[i].func, tab[i].escaped);
|
||||
ExpandGeneric(xp, rmp, matches, numMatches, tab[i].func, tab[i].escaped,
|
||||
fuzzy ? pat : NULL);
|
||||
ret = OK;
|
||||
break;
|
||||
}
|
||||
@ -2581,9 +2634,11 @@ static int map_wildopts_to_ewflags(int options)
|
||||
/// @param options WILD_ flags
|
||||
static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numMatches, int options)
|
||||
{
|
||||
regmatch_T regmatch;
|
||||
regmatch_T regmatch = { .rm_ic = false };
|
||||
int ret;
|
||||
int flags = map_wildopts_to_ewflags(options);
|
||||
const bool fuzzy = cmdline_fuzzy_complete(pat)
|
||||
&& cmdline_fuzzy_completion_supported(xp);
|
||||
|
||||
if (xp->xp_context == EXPAND_FILES
|
||||
|| xp->xp_context == EXPAND_DIRECTORIES
|
||||
@ -2664,26 +2719,30 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
|
||||
return nlua_expand_pat(xp, pat, numMatches, matches);
|
||||
}
|
||||
|
||||
regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
|
||||
if (regmatch.regprog == NULL) {
|
||||
return FAIL;
|
||||
}
|
||||
if (!fuzzy) {
|
||||
regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
|
||||
if (regmatch.regprog == NULL) {
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// set ignore-case according to p_ic, p_scs and pat
|
||||
regmatch.rm_ic = ignorecase(pat);
|
||||
// set ignore-case according to p_ic, p_scs and pat
|
||||
regmatch.rm_ic = ignorecase(pat);
|
||||
}
|
||||
|
||||
if (xp->xp_context == EXPAND_SETTINGS
|
||||
|| xp->xp_context == EXPAND_BOOL_SETTINGS) {
|
||||
ret = ExpandSettings(xp, ®match, numMatches, matches);
|
||||
ret = ExpandSettings(xp, ®match, pat, numMatches, matches);
|
||||
} else if (xp->xp_context == EXPAND_MAPPINGS) {
|
||||
ret = ExpandMappings(®match, numMatches, matches);
|
||||
ret = ExpandMappings(pat, ®match, numMatches, matches);
|
||||
} else if (xp->xp_context == EXPAND_USER_DEFINED) {
|
||||
ret = ExpandUserDefined(xp, ®match, matches, numMatches);
|
||||
} else {
|
||||
ret = ExpandOther(xp, ®match, matches, numMatches);
|
||||
ret = ExpandOther(pat, xp, ®match, matches, numMatches);
|
||||
}
|
||||
|
||||
vim_regfree(regmatch.regprog);
|
||||
if (!fuzzy) {
|
||||
vim_regfree(regmatch.regprog);
|
||||
}
|
||||
xfree(tofree);
|
||||
|
||||
return ret;
|
||||
@ -2695,13 +2754,17 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
|
||||
/// obtain strings, one by one. The strings are matched against a regexp
|
||||
/// program. Matching strings are copied into an array, which is returned.
|
||||
///
|
||||
/// If "fuzzystr" is not NULL, then fuzzy matching is used. Otherwise,
|
||||
/// regex matching is used.
|
||||
///
|
||||
/// @param func returns a string from the list
|
||||
static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, char ***matches, int *numMatches,
|
||||
CompleteListItemGetter func, int escaped)
|
||||
CompleteListItemGetter func, int escaped, const char *const fuzzystr)
|
||||
{
|
||||
int i;
|
||||
size_t count = 0;
|
||||
char *str;
|
||||
const bool fuzzy = fuzzystr != NULL;
|
||||
|
||||
// count the number of matching names
|
||||
for (i = 0;; i++) {
|
||||
@ -2712,7 +2775,14 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, char ***matches, i
|
||||
if (*str == NUL) { // skip empty strings
|
||||
continue;
|
||||
}
|
||||
if (vim_regexec(regmatch, str, (colnr_T)0)) {
|
||||
|
||||
bool match;
|
||||
if (!fuzzy) {
|
||||
match = vim_regexec(regmatch, str, (colnr_T)0);
|
||||
} else {
|
||||
match = fuzzy_match_str(str, fuzzystr) != 0;
|
||||
}
|
||||
if (match) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@ -2721,7 +2791,12 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, char ***matches, i
|
||||
}
|
||||
assert(count < INT_MAX);
|
||||
*numMatches = (int)count;
|
||||
*matches = xmalloc(count * sizeof(char *));
|
||||
fuzmatch_str_T *fuzmatch = NULL;
|
||||
if (fuzzy) {
|
||||
fuzmatch = xmalloc(count * sizeof(fuzmatch_str_T));
|
||||
} else {
|
||||
*matches = xmalloc(count * sizeof(char *));
|
||||
}
|
||||
|
||||
// copy the matching names into allocated memory
|
||||
count = 0;
|
||||
@ -2733,38 +2808,66 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, char ***matches, i
|
||||
if (*str == NUL) { // Skip empty strings.
|
||||
continue;
|
||||
}
|
||||
if (vim_regexec(regmatch, str, (colnr_T)0)) {
|
||||
if (escaped) {
|
||||
str = vim_strsave_escaped(str, " \t\\.");
|
||||
} else {
|
||||
str = xstrdup(str);
|
||||
}
|
||||
(*matches)[count++] = str;
|
||||
if (func == get_menu_names) {
|
||||
// Test for separator added by get_menu_names().
|
||||
str += strlen(str) - 1;
|
||||
if (*str == '\001') {
|
||||
*str = '.';
|
||||
}
|
||||
|
||||
bool match;
|
||||
int score = 0;
|
||||
if (!fuzzy) {
|
||||
match = vim_regexec(regmatch, str, (colnr_T)0);
|
||||
} else {
|
||||
score = fuzzy_match_str(str, fuzzystr);
|
||||
match = (score != 0);
|
||||
}
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (escaped) {
|
||||
str = vim_strsave_escaped(str, " \t\\.");
|
||||
} else {
|
||||
str = xstrdup(str);
|
||||
}
|
||||
if (fuzzy) {
|
||||
fuzmatch[count].idx = (int)count;
|
||||
fuzmatch[count].str = str;
|
||||
fuzmatch[count].score = score;
|
||||
} else {
|
||||
(*matches)[count] = str;
|
||||
}
|
||||
count++;
|
||||
if (func == get_menu_names) {
|
||||
// Test for separator added by get_menu_names().
|
||||
str += strlen(str) - 1;
|
||||
if (*str == '\001') {
|
||||
*str = '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the results. Keep menu's in the specified order.
|
||||
bool funcsort = false;
|
||||
if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
|
||||
if (xp->xp_context == EXPAND_EXPRESSION
|
||||
|| xp->xp_context == EXPAND_FUNCTIONS
|
||||
|| xp->xp_context == EXPAND_USER_FUNC) {
|
||||
// <SNR> functions should be sorted to the end.
|
||||
qsort((void *)(*matches), (size_t)(*numMatches), sizeof(char *), sort_func_compare);
|
||||
funcsort = true;
|
||||
if (!fuzzy) {
|
||||
qsort(*matches, (size_t)(*numMatches), sizeof(char *), sort_func_compare);
|
||||
}
|
||||
} else {
|
||||
sort_strings(*matches, *numMatches);
|
||||
if (!fuzzy) {
|
||||
sort_strings(*matches, *numMatches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the variables used for special highlight names expansion, so that
|
||||
// they don't show up when getting normal highlight names by ID.
|
||||
reset_expand_highlight();
|
||||
|
||||
if (fuzzy) {
|
||||
fuzzymatches_to_strmatches(fuzmatch, matches, (int)count, funcsort);
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand shell command matches in one directory of $PATH.
|
||||
@ -3369,7 +3472,13 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
}
|
||||
|
||||
theend:
|
||||
pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
|
||||
if (cmdline_fuzzy_completion_supported(&xpc)) {
|
||||
// when fuzzy matching, don't modify the search string
|
||||
pat = xstrdup(xpc.xp_pattern);
|
||||
} else {
|
||||
pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
|
||||
}
|
||||
|
||||
ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
|
||||
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nvim/ascii.h"
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/cmdexpand.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
@ -39,6 +40,7 @@
|
||||
#include "nvim/pos.h"
|
||||
#include "nvim/regexp.h"
|
||||
#include "nvim/runtime.h"
|
||||
#include "nvim/search.h"
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/vim.h"
|
||||
|
||||
@ -1268,7 +1270,7 @@ char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char *arg, bool forceit,
|
||||
/// Find all mapping/abbreviation names that match regexp "regmatch".
|
||||
/// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
|
||||
/// @return OK if matches found, FAIL otherwise.
|
||||
int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
|
||||
int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***matches)
|
||||
{
|
||||
mapblock_T *mp;
|
||||
int hash;
|
||||
@ -1277,14 +1279,18 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
*num_file = 0; // return values in case of FAIL
|
||||
*file = NULL;
|
||||
fuzmatch_str_T *fuzmatch = NULL;
|
||||
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
||||
|
||||
*numMatches = 0; // return values in case of FAIL
|
||||
*matches = NULL;
|
||||
|
||||
// round == 1: Count the matches.
|
||||
// round == 2: Build the array to keep the matches.
|
||||
for (round = 1; round <= 2; round++) {
|
||||
count = 0;
|
||||
|
||||
// First search in map modifier arguments
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (i == 0) {
|
||||
p = "<silent>";
|
||||
@ -1304,13 +1310,29 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vim_regexec(regmatch, p, (colnr_T)0)) {
|
||||
if (round == 1) {
|
||||
count++;
|
||||
bool match;
|
||||
int score = 0;
|
||||
if (!fuzzy) {
|
||||
match = vim_regexec(regmatch, p, (colnr_T)0);
|
||||
} else {
|
||||
score = fuzzy_match_str(p, pat);
|
||||
match = (score != 0);
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (round == 2) {
|
||||
if (fuzzy) {
|
||||
fuzmatch[count].idx = count;
|
||||
fuzmatch[count].str = xstrdup(p);
|
||||
fuzmatch[count].score = score;
|
||||
} else {
|
||||
(*file)[count++] = xstrdup(p);
|
||||
(*matches)[count] = xstrdup(p);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
for (hash = 0; hash < 256; hash++) {
|
||||
@ -1327,12 +1349,28 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
|
||||
for (; mp; mp = mp->m_next) {
|
||||
if (mp->m_mode & expand_mapmodes) {
|
||||
p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS);
|
||||
if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) {
|
||||
if (round == 1) {
|
||||
count++;
|
||||
if (p != NULL) {
|
||||
bool match;
|
||||
int score = 0;
|
||||
if (!fuzzy) {
|
||||
match = vim_regexec(regmatch, p, (colnr_T)0);
|
||||
} else {
|
||||
(*file)[count++] = p;
|
||||
p = NULL;
|
||||
score = fuzzy_match_str(p, pat);
|
||||
match = (score != 0);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (round == 2) {
|
||||
if (fuzzy) {
|
||||
fuzmatch[count].idx = count;
|
||||
fuzmatch[count].str = p;
|
||||
fuzmatch[count].score = score;
|
||||
} else {
|
||||
(*matches)[count] = p;
|
||||
}
|
||||
p = NULL;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
xfree(p);
|
||||
@ -1345,16 +1383,27 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
|
||||
}
|
||||
|
||||
if (round == 1) {
|
||||
*file = xmalloc((size_t)count * sizeof(char *));
|
||||
if (fuzzy) {
|
||||
fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T));
|
||||
} else {
|
||||
*matches = xmalloc((size_t)count * sizeof(char *));
|
||||
}
|
||||
}
|
||||
} // for (round)
|
||||
|
||||
if (fuzzy) {
|
||||
fuzzymatches_to_strmatches(fuzmatch, matches, count, false);
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
// Sort the matches
|
||||
sort_strings(*file, count);
|
||||
// Fuzzy matching already sorts the matches
|
||||
if (!fuzzy) {
|
||||
sort_strings(*matches, count);
|
||||
}
|
||||
|
||||
// Remove multiple entries
|
||||
char **ptr1 = *file;
|
||||
char **ptr1 = *matches;
|
||||
char **ptr2 = ptr1 + 1;
|
||||
char **ptr3 = ptr1 + count;
|
||||
|
||||
@ -1368,7 +1417,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
|
||||
}
|
||||
}
|
||||
|
||||
*num_file = count;
|
||||
*numMatches = count;
|
||||
return count == 0 ? FAIL : OK;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "nvim/buffer.h"
|
||||
#include "nvim/change.h"
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/cmdexpand.h"
|
||||
#include "nvim/cursor_shape.h"
|
||||
#include "nvim/decoration_provider.h"
|
||||
#include "nvim/diff.h"
|
||||
@ -81,6 +82,7 @@
|
||||
#include "nvim/regexp.h"
|
||||
#include "nvim/runtime.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/search.h"
|
||||
#include "nvim/sign_defs.h"
|
||||
#include "nvim/spell.h"
|
||||
#include "nvim/spellfile.h"
|
||||
@ -4698,13 +4700,56 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
|
||||
}
|
||||
}
|
||||
|
||||
int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
|
||||
/// 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 bool match_str(char *const str, regmatch_T *const regmatch, char **const matches,
|
||||
const int idx, const bool test_only, const bool fuzzy,
|
||||
const char *const fuzzystr, fuzmatch_str_T *const fuzmatch)
|
||||
{
|
||||
if (!fuzzy) {
|
||||
if (vim_regexec(regmatch, str, (colnr_T)0)) {
|
||||
if (!test_only) {
|
||||
matches[idx] = xstrdup(str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
const int score = fuzzy_match_str(str, fuzzystr);
|
||||
if (score != 0) {
|
||||
if (!test_only) {
|
||||
fuzmatch[idx].idx = idx;
|
||||
fuzmatch[idx].str = xstrdup(str);
|
||||
fuzmatch[idx].score = score;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numMatches,
|
||||
char ***matches)
|
||||
{
|
||||
int num_normal = 0; // Nr of matching non-term-code settings
|
||||
int count = 0;
|
||||
static char *(names[]) = { "all" };
|
||||
int ic = regmatch->rm_ic; // remember the ignore-case flag
|
||||
|
||||
fuzmatch_str_T *fuzmatch = NULL;
|
||||
const bool fuzzy = cmdline_fuzzy_complete(fuzzystr);
|
||||
|
||||
// do this loop twice:
|
||||
// loop == 0: count the number of matching options
|
||||
// loop == 1: copy the matching options into allocated memory
|
||||
@ -4714,11 +4759,12 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
|
||||
if (xp->xp_context != EXPAND_BOOL_SETTINGS) {
|
||||
for (match = 0; match < (int)ARRAY_SIZE(names);
|
||||
match++) {
|
||||
if (vim_regexec(regmatch, names[match], (colnr_T)0)) {
|
||||
if (match_str(names[match], regmatch, *matches,
|
||||
count, (loop == 0), fuzzy, fuzzystr, fuzmatch)) {
|
||||
if (loop == 0) {
|
||||
num_normal++;
|
||||
} else {
|
||||
(*file)[count++] = xstrdup(names[match]);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4733,33 +4779,45 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
|
||||
&& !(options[opt_idx].flags & P_BOOL)) {
|
||||
continue;
|
||||
}
|
||||
match = false;
|
||||
if (vim_regexec(regmatch, str, (colnr_T)0)
|
||||
|| (options[opt_idx].shortname != NULL
|
||||
&& vim_regexec(regmatch,
|
||||
options[opt_idx].shortname,
|
||||
(colnr_T)0))) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (match_str(str, regmatch, *matches, count, (loop == 0),
|
||||
fuzzy, fuzzystr, fuzmatch)) {
|
||||
if (loop == 0) {
|
||||
num_normal++;
|
||||
} else {
|
||||
(*file)[count++] = xstrdup(str);
|
||||
count++;
|
||||
}
|
||||
} else if (!fuzzy && options[opt_idx].shortname != NULL
|
||||
&& vim_regexec(regmatch, 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) {
|
||||
num_normal++;
|
||||
} else {
|
||||
(*matches)[count++] = xstrdup(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loop == 0) {
|
||||
if (num_normal > 0) {
|
||||
*num_file = num_normal;
|
||||
*numMatches = num_normal;
|
||||
} else {
|
||||
return OK;
|
||||
}
|
||||
*file = xmalloc((size_t)(*num_file) * sizeof(char *));
|
||||
if (!fuzzy) {
|
||||
*matches = xmalloc((size_t)(*numMatches) * sizeof(char *));
|
||||
} else {
|
||||
fuzmatch = xmalloc((size_t)(*numMatches) * sizeof(fuzmatch_str_T));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fuzzy) {
|
||||
fuzzymatches_to_strmatches(fuzmatch, matches, count, false);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -795,6 +795,7 @@ EXTERN char *p_wop; // 'wildoptions'
|
||||
EXTERN unsigned wop_flags;
|
||||
#define WOP_TAGFILE 0x01
|
||||
#define WOP_PUM 0x02
|
||||
#define WOP_FUZZY 0x04
|
||||
EXTERN long p_window; // 'window'
|
||||
EXTERN char *p_wak; // 'winaltkeys'
|
||||
EXTERN char *p_wig; // 'wildignore'
|
||||
|
@ -91,7 +91,7 @@ static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vspli
|
||||
static char *(p_spk_values[]) = { "cursor", "screen", "topline", NULL };
|
||||
static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", 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[]) = { "tagfile", "pum", "fuzzy", NULL };
|
||||
static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
|
||||
static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL };
|
||||
static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL };
|
||||
|
@ -5230,8 +5230,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
|
||||
const size_t sz = sizeof(matches) / sizeof(matches[0]);
|
||||
|
||||
// Fuzzy string match
|
||||
while (fuzzy_match((char_u *)str + col, (char_u *)spat, false, &score, matches,
|
||||
(int)sz) > 0) {
|
||||
while (fuzzy_match(str + col, spat, false, &score, matches, (int)sz) > 0) {
|
||||
// Pass the buffer number so that it gets used even for a
|
||||
// dummy buffer, unless duplicate_name is set, then the
|
||||
// buffer will be wiped out below.
|
||||
|
@ -2942,7 +2942,7 @@ typedef struct {
|
||||
#define FUZZY_MATCH_RECURSION_LIMIT 10
|
||||
|
||||
/// Compute a score for a fuzzy matched string. The matching character locations
|
||||
/// are in 'matches'.
|
||||
/// are in "matches".
|
||||
static int fuzzy_match_compute_score(const char *const str, const int strSz,
|
||||
const uint32_t *const matches, const int numMatches)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
|
||||
@ -3007,7 +3007,7 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz,
|
||||
return score;
|
||||
}
|
||||
|
||||
/// Perform a recursive search for fuzzy matching 'fuzpat' in 'str'.
|
||||
/// Perform a recursive search for fuzzy matching "fuzpat" in "str".
|
||||
/// @return the number of matching characters.
|
||||
static int fuzzy_match_recursive(const char *fuzpat, const char *str, uint32_t strIdx,
|
||||
int *const outScore, const char *const strBegin, const int strLen,
|
||||
@ -3107,23 +3107,23 @@ static int fuzzy_match_recursive(const char *fuzpat, const char *str, uint32_t s
|
||||
/// Uses char_u for match indices. Therefore patterns are limited to
|
||||
/// MAX_FUZZY_MATCHES characters.
|
||||
///
|
||||
/// @return true if 'pat_arg' matches 'str'. Also returns the match score in
|
||||
/// 'outScore' and the matching character positions in 'matches'.
|
||||
bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matchseq,
|
||||
/// @return true if "pat_arg" matches "str". Also returns the match score in
|
||||
/// "outScore" and the matching character positions in "matches".
|
||||
bool fuzzy_match(char *const str, const char *const pat_arg, const bool matchseq,
|
||||
int *const outScore, uint32_t *const matches, const int maxMatches)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const int len = mb_charlen((char *)str);
|
||||
const int len = mb_charlen(str);
|
||||
bool complete = false;
|
||||
int numMatches = 0;
|
||||
|
||||
*outScore = 0;
|
||||
|
||||
char *const save_pat = xstrdup((char *)pat_arg);
|
||||
char *const save_pat = xstrdup(pat_arg);
|
||||
char *pat = save_pat;
|
||||
char *p = pat;
|
||||
|
||||
// Try matching each word in 'pat_arg' in 'str'
|
||||
// Try matching each word in "pat_arg" in "str"
|
||||
while (true) {
|
||||
if (matchseq) {
|
||||
complete = true;
|
||||
@ -3146,7 +3146,7 @@ bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matc
|
||||
int score = 0;
|
||||
int recursionCount = 0;
|
||||
const int matchCount
|
||||
= fuzzy_match_recursive(pat, (char *)str, 0, &score, (char *)str, len, NULL,
|
||||
= fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL,
|
||||
matches + numMatches,
|
||||
maxMatches - numMatches, 0, &recursionCount);
|
||||
if (matchCount == 0) {
|
||||
@ -3183,14 +3183,14 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2)
|
||||
return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1;
|
||||
}
|
||||
|
||||
/// Fuzzy search the string 'str' in a list of 'items' and return the matching
|
||||
/// strings in 'fmatchlist'.
|
||||
/// If 'matchseq' is true, then for multi-word search strings, match all the
|
||||
/// Fuzzy search the string "str" in a list of "items" and return the matching
|
||||
/// strings in "fmatchlist".
|
||||
/// If "matchseq" is true, then for multi-word search strings, match all the
|
||||
/// words in sequence.
|
||||
/// If 'items' is a list of strings, then search for 'str' in the list.
|
||||
/// If 'items' is a list of dicts, then either use 'key' to lookup the string
|
||||
/// for each item or use 'item_cb' Funcref function to get the string.
|
||||
/// If 'retmatchpos' is true, then return a list of positions where 'str'
|
||||
/// If "items" is a list of strings, then search for "str" in the list.
|
||||
/// If "items" is a list of dicts, then either use "key" to lookup the string
|
||||
/// for each item or use "item_cb" Funcref function to get the string.
|
||||
/// If "retmatchpos" is true, then return a list of positions where "str"
|
||||
/// matches for each item.
|
||||
static void fuzzy_match_in_list(list_T *const l, char *const str, const bool matchseq,
|
||||
const char *const key, Callback *const item_cb,
|
||||
@ -3245,14 +3245,14 @@ static void fuzzy_match_in_list(list_T *const l, char *const str, const bool mat
|
||||
}
|
||||
|
||||
int score;
|
||||
if (itemstr != NULL && fuzzy_match((char_u *)itemstr, (char_u *)str, matchseq, &score, matches,
|
||||
if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches,
|
||||
MAX_FUZZY_MATCHES)) {
|
||||
items[match_count].idx = (int)match_count;
|
||||
items[match_count].item = li;
|
||||
items[match_count].score = score;
|
||||
|
||||
// Copy the list of matching positions in itemstr to a list, if
|
||||
// 'retmatchpos' is set.
|
||||
// "retmatchpos" is set.
|
||||
if (retmatchpos) {
|
||||
items[match_count].lmatchpos = tv_list_alloc(kListLenMayKnow);
|
||||
int j = 0;
|
||||
@ -3326,8 +3326,8 @@ static void fuzzy_match_in_list(list_T *const l, char *const str, const bool mat
|
||||
xfree(items);
|
||||
}
|
||||
|
||||
/// Do fuzzy matching. Returns the list of matched strings in 'rettv'.
|
||||
/// If 'retmatchpos' is true, also returns the matching character positions.
|
||||
/// Do fuzzy matching. Returns the list of matched strings in "rettv".
|
||||
/// If "retmatchpos" is true, also returns the matching character positions.
|
||||
static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
|
||||
const bool retmatchpos)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
@ -3411,6 +3411,109 @@ void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
do_fuzzymatch(argvars, rettv, true);
|
||||
}
|
||||
|
||||
/// Same as fuzzy_match_item_compare() except for use with a string match
|
||||
static int fuzzy_match_str_compare(const void *const s1, const void *const s2)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
|
||||
{
|
||||
const int v1 = ((fuzmatch_str_T *)s1)->score;
|
||||
const int v2 = ((fuzmatch_str_T *)s2)->score;
|
||||
const int idx1 = ((fuzmatch_str_T *)s1)->idx;
|
||||
const 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 *const fm, const int sz)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Sort the list by the descending order of the match score
|
||||
qsort(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 *const s1, const void *const s2)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
|
||||
{
|
||||
const int v1 = ((fuzmatch_str_T *)s1)->score;
|
||||
const int v2 = ((fuzmatch_str_T *)s2)->score;
|
||||
const int idx1 = ((fuzmatch_str_T *)s1)->idx;
|
||||
const int idx2 = ((fuzmatch_str_T *)s2)->idx;
|
||||
const char *const str1 = ((fuzmatch_str_T *)s1)->str;
|
||||
const char *const 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 *const fm, const int sz)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
// Sort the list by the descending order of the match score
|
||||
qsort(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 *const str, const char *const pat)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
if (str == NULL || pat == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int score = 0;
|
||||
uint32_t matchpos[MAX_FUZZY_MATCHES];
|
||||
fuzzy_match(str, pat, true, &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".
|
||||
void fuzzymatches_to_strmatches(fuzmatch_str_T *const fuzmatch, char ***const matches,
|
||||
const int count, const bool funcsort)
|
||||
FUNC_ATTR_NONNULL_ARG(2)
|
||||
{
|
||||
if (count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
*matches = xmalloc((size_t)count * sizeof(char *));
|
||||
|
||||
// Sort the list by the descending order of the match score
|
||||
if (funcsort) {
|
||||
fuzzy_match_func_sort(fuzmatch, count);
|
||||
} else {
|
||||
fuzzy_match_str_sort(fuzmatch, count);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
(*matches)[i] = fuzmatch[i].str;
|
||||
}
|
||||
xfree(fuzmatch);
|
||||
}
|
||||
|
||||
/// Free a list of fuzzy string matches.
|
||||
void fuzmatch_str_free(fuzmatch_str_T *const fuzmatch, int count)
|
||||
{
|
||||
if (count <= 0 || fuzmatch == NULL) {
|
||||
return;
|
||||
}
|
||||
while (count--) {
|
||||
xfree(fuzmatch[count].str);
|
||||
}
|
||||
xfree(fuzmatch);
|
||||
}
|
||||
|
||||
/// Get line "lnum" and copy it into "buf[LSIZE]".
|
||||
/// The copy is made because the regexp may make the line invalid when using a
|
||||
/// mark.
|
||||
|
@ -99,6 +99,14 @@ typedef struct searchstat {
|
||||
int last_maxcount; // the max count of the last search
|
||||
} searchstat_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 *str;
|
||||
int score;
|
||||
} fuzmatch_str_T;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "search.h.generated.h"
|
||||
#endif
|
||||
|
@ -650,6 +650,22 @@ func Test_getcompletion()
|
||||
call assert_fails('call getcompletion("abc", [])', 'E475:')
|
||||
endfunc
|
||||
|
||||
" Test for getcompletion() with "fuzzy" in 'wildoptions'
|
||||
func Test_getcompletion_wildoptions()
|
||||
let save_wildoptions = &wildoptions
|
||||
set wildoptions&
|
||||
let l = getcompletion('space', 'option')
|
||||
call assert_equal([], l)
|
||||
let l = getcompletion('ier', 'command')
|
||||
call assert_equal([], l)
|
||||
set wildoptions=fuzzy
|
||||
let l = getcompletion('space', 'option')
|
||||
call assert_true(index(l, 'backspace') >= 0)
|
||||
let l = getcompletion('ier', 'command')
|
||||
call assert_true(index(l, 'compiler') >= 0)
|
||||
let &wildoptions = save_wildoptions
|
||||
endfunc
|
||||
|
||||
func Test_fullcommand()
|
||||
let tests = {
|
||||
\ '': '',
|
||||
@ -2654,6 +2670,412 @@ 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
|
||||
" test regex completion works
|
||||
set wildoptions=fuzzy
|
||||
call feedkeys(":cnoremap <ex\<Tab> <esc> \<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"cnoremap <expr> <esc> \<Tab>", @:)
|
||||
nmap <plug>MyLongMap :p<CR>
|
||||
call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"nmap <Plug>MyLongMap", @:)
|
||||
call feedkeys(":nmap MLM \<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"nmap MLM \t", @:)
|
||||
call feedkeys(":nmap <F2> one two \<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"nmap <F2> one two \t", @:)
|
||||
" duplicate entries should be removed
|
||||
vmap <plug>MyLongMap :<C-U>#<CR>
|
||||
call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"nmap <Plug>MyLongMap", @:)
|
||||
nunmap <plug>MyLongMap
|
||||
vunmap <plug>MyLongMap
|
||||
call feedkeys(":nmap ABC\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"nmap ABC\t", @:)
|
||||
" results should be sorted by best match
|
||||
nmap <Plug>format :
|
||||
nmap <Plug>goformat :
|
||||
nmap <Plug>TestFOrmat :
|
||||
nmap <Plug>fendoff :
|
||||
nmap <Plug>state :
|
||||
nmap <Plug>FendingOff :
|
||||
call feedkeys(":nmap <Plug>fo\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"nmap <Plug>format <Plug>TestFOrmat <Plug>FendingOff <Plug>goformat <Plug>fendoff", @:)
|
||||
nunmap <Plug>format
|
||||
nunmap <Plug>goformat
|
||||
nunmap <Plug>TestFOrmat
|
||||
nunmap <Plug>fendoff
|
||||
nunmap <Plug>state
|
||||
nunmap <Plug>FendingOff
|
||||
|
||||
" abbreviation fuzzy completion
|
||||
set wildoptions=fuzzy
|
||||
call feedkeys(":iabbr wait\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"iabbr <nowait>", @:)
|
||||
iabbr WaitForCompletion WFC
|
||||
call feedkeys(":iabbr fcl\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"iabbr WaitForCompletion", @:)
|
||||
call feedkeys(":iabbr a1z\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"iabbr a1z\t", @:)
|
||||
iunabbrev WaitForCompletion
|
||||
|
||||
" 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>
|
||||
" Nvim does not support term options
|
||||
" 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')
|
||||
" Fuzzy match favours NvimParenthesis over MatchParen
|
||||
" call assert_equal('"syntax list MatchParen', @:)
|
||||
call assert_equal('"syntax list NvimParenthesis', @:)
|
||||
|
||||
" :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', @:)
|
||||
|
||||
" Test for sorting the results by the best match
|
||||
%bw!
|
||||
command T123format :
|
||||
command T123goformat :
|
||||
command T123TestFOrmat :
|
||||
command T123fendoff :
|
||||
command T123state :
|
||||
command T123FendingOff :
|
||||
set wildoptions=fuzzy
|
||||
call feedkeys(":T123fo\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"T123format T123TestFOrmat T123FendingOff T123goformat T123fendoff', @:)
|
||||
delcommand T123format
|
||||
delcommand T123goformat
|
||||
delcommand T123TestFOrmat
|
||||
delcommand T123fendoff
|
||||
delcommand T123state
|
||||
delcommand T123FendingOff
|
||||
%bw
|
||||
|
||||
" Test for fuzzy completion of a command with lower case letters and a
|
||||
" number
|
||||
command Foo2Bar :
|
||||
set wildoptions=fuzzy
|
||||
call feedkeys(":foo2\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"Foo2Bar', @:)
|
||||
call feedkeys(":foo\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"Foo2Bar', @:)
|
||||
call feedkeys(":bar\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"Foo2Bar', @:)
|
||||
delcommand Foo2Bar
|
||||
|
||||
" Test for command completion for a command starting with 'k'
|
||||
command KillKillKill :
|
||||
set wildoptions&
|
||||
call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal("\"killkill\<Tab>", @:)
|
||||
set wildoptions=fuzzy
|
||||
call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
|
||||
call assert_equal('"KillKillKill', @:)
|
||||
delcom KillKillKill
|
||||
|
||||
set wildoptions&
|
||||
%bw!
|
||||
endfunc
|
||||
|
||||
" Test for :breakadd argument completion
|
||||
func Test_cmdline_complete_breakadd()
|
||||
call feedkeys(":breakadd \<C-A>\<C-B>\"\<CR>", 'tx')
|
||||
|
Loading…
Reference in New Issue
Block a user