mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 19:38:20 +00:00
vim-patch:8.2.4760: using matchfuzzy() on a long list can take a while
Problem: Using matchfuzzy() on a long list can take a while.
Solution: Add a limit to the number of matches. (Yasuhiro Matsumoto,
closes vim/vim#10189)
9029a6e993
This commit is contained in:
@@ -4975,7 +4975,7 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*
|
|||||||
|
|
||||||
If {list} is a list of dictionaries, then the optional {dict}
|
If {list} is a list of dictionaries, then the optional {dict}
|
||||||
argument supports the following additional items:
|
argument supports the following additional items:
|
||||||
key key of the item which is fuzzy matched against
|
key Key of the item which is fuzzy matched against
|
||||||
{str}. The value of this item should be a
|
{str}. The value of this item should be a
|
||||||
string.
|
string.
|
||||||
text_cb |Funcref| that will be called for every item
|
text_cb |Funcref| that will be called for every item
|
||||||
@@ -4983,6 +4983,8 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*
|
|||||||
This should accept a dictionary item as the
|
This should accept a dictionary item as the
|
||||||
argument and return the text for that item to
|
argument and return the text for that item to
|
||||||
use for fuzzy matching.
|
use for fuzzy matching.
|
||||||
|
limit Maximum number of matches in {list} to be
|
||||||
|
returned. Zero means no limit.
|
||||||
|
|
||||||
{str} is treated as a literal string and regular expression
|
{str} is treated as a literal string and regular expression
|
||||||
matching is NOT supported. The maximum supported {str} length
|
matching is NOT supported. The maximum supported {str} length
|
||||||
@@ -4995,6 +4997,9 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*
|
|||||||
empty list is returned. If length of {str} is greater than
|
empty list is returned. If length of {str} is greater than
|
||||||
256, then returns an empty list.
|
256, then returns an empty list.
|
||||||
|
|
||||||
|
When {limit} is given, matchfuzzy() will find up to this
|
||||||
|
number of matches in {list} and return them in sorted order.
|
||||||
|
|
||||||
Refer to |fuzzy-matching| for more information about fuzzy
|
Refer to |fuzzy-matching| for more information about fuzzy
|
||||||
matching strings.
|
matching strings.
|
||||||
|
|
||||||
|
@@ -5095,7 +5095,8 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2)
|
|||||||
/// matches for each item.
|
/// matches for each item.
|
||||||
static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bool matchseq,
|
static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bool matchseq,
|
||||||
const char_u *const key, Callback *const item_cb,
|
const char_u *const key, Callback *const item_cb,
|
||||||
const bool retmatchpos, list_T *const fmatchlist)
|
const bool retmatchpos, list_T *const fmatchlist,
|
||||||
|
const long max_matches)
|
||||||
FUNC_ATTR_NONNULL_ARG(2, 5, 7)
|
FUNC_ATTR_NONNULL_ARG(2, 5, 7)
|
||||||
{
|
{
|
||||||
const long len = tv_list_len(items);
|
const long len = tv_list_len(items);
|
||||||
@@ -5103,9 +5104,10 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(vim): when using a limit use that instead of "len"
|
||||||
fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T));
|
fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T));
|
||||||
long i = 0;
|
long i = 0;
|
||||||
bool found_match = false;
|
long found_match = 0;
|
||||||
uint32_t matches[MAX_FUZZY_MATCHES];
|
uint32_t matches[MAX_FUZZY_MATCHES];
|
||||||
|
|
||||||
// For all the string items in items, get the fuzzy matching score
|
// For all the string items in items, get the fuzzy matching score
|
||||||
@@ -5113,6 +5115,14 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo
|
|||||||
ptrs[i].idx = i;
|
ptrs[i].idx = i;
|
||||||
ptrs[i].item = li;
|
ptrs[i].item = li;
|
||||||
ptrs[i].score = SCORE_NONE;
|
ptrs[i].score = SCORE_NONE;
|
||||||
|
|
||||||
|
// TODO(vim): instead of putting all items in ptrs[] should only add
|
||||||
|
// matching items there.
|
||||||
|
if (max_matches > 0 && found_match >= max_matches) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
char_u *itemstr = NULL;
|
char_u *itemstr = NULL;
|
||||||
typval_T rettv;
|
typval_T rettv;
|
||||||
rettv.v_type = VAR_UNKNOWN;
|
rettv.v_type = VAR_UNKNOWN;
|
||||||
@@ -5159,13 +5169,13 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ptrs[i].score = score;
|
ptrs[i].score = score;
|
||||||
found_match = true;
|
found_match++;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
tv_clear(&rettv);
|
tv_clear(&rettv);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (found_match) {
|
if (found_match > 0) {
|
||||||
// Sort the list by the descending order of the match score
|
// Sort the list by the descending order of the match score
|
||||||
qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_match_item_compare);
|
qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_match_item_compare);
|
||||||
|
|
||||||
@@ -5239,6 +5249,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
|
|||||||
Callback cb = CALLBACK_NONE;
|
Callback cb = CALLBACK_NONE;
|
||||||
const char_u *key = NULL;
|
const char_u *key = NULL;
|
||||||
bool matchseq = false;
|
bool matchseq = false;
|
||||||
|
long max_matches = 0;
|
||||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||||
if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
|
if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
|
||||||
emsg(_(e_dictreq));
|
emsg(_(e_dictreq));
|
||||||
@@ -5248,8 +5259,8 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
|
|||||||
// To search a dict, either a callback function or a key can be
|
// To search a dict, either a callback function or a key can be
|
||||||
// specified.
|
// specified.
|
||||||
dict_T *const d = argvars[2].vval.v_dict;
|
dict_T *const d = argvars[2].vval.v_dict;
|
||||||
const dictitem_T *const di = tv_dict_find(d, "key", -1);
|
const dictitem_T *di;
|
||||||
if (di != NULL) {
|
if ((di = tv_dict_find(d, "key", -1)) != NULL) {
|
||||||
if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL
|
if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL
|
||||||
|| *di->di_tv.vval.v_string == NUL) {
|
|| *di->di_tv.vval.v_string == NUL) {
|
||||||
semsg(_(e_invarg2), tv_get_string(&di->di_tv));
|
semsg(_(e_invarg2), tv_get_string(&di->di_tv));
|
||||||
@@ -5259,7 +5270,14 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
|
|||||||
} else if (!tv_dict_get_callback(d, "text_cb", -1, &cb)) {
|
} else if (!tv_dict_get_callback(d, "text_cb", -1, &cb)) {
|
||||||
semsg(_(e_invargval), "text_cb");
|
semsg(_(e_invargval), "text_cb");
|
||||||
return;
|
return;
|
||||||
|
} else if ((di = tv_dict_find(d, "limit", -1)) != NULL) {
|
||||||
|
if (di->di_tv.v_type != VAR_NUMBER) {
|
||||||
|
semsg(_(e_invarg2), tv_get_string(&di->di_tv));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
max_matches = (long)tv_get_number_chk(&di->di_tv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (tv_dict_find(d, "matchseq", -1) != NULL) {
|
if (tv_dict_find(d, "matchseq", -1) != NULL) {
|
||||||
matchseq = true;
|
matchseq = true;
|
||||||
}
|
}
|
||||||
@@ -5278,7 +5296,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fuzzy_match_in_list(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), matchseq, key,
|
fuzzy_match_in_list(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), matchseq, key,
|
||||||
&cb, retmatchpos, rettv->vval.v_list);
|
&cb, retmatchpos, rettv->vval.v_list, max_matches);
|
||||||
callback_free(&cb);
|
callback_free(&cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -245,4 +245,16 @@ func Test_matchfuzzypos_mbyte()
|
|||||||
call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 'фйд'))
|
call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 'фйд'))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test for matchfuzzy() with limit
|
||||||
|
func Test_matchfuzzy_limit()
|
||||||
|
let x = ['1', '2', '3', '2']
|
||||||
|
call assert_equal(['2', '2'], x->matchfuzzy('2'))
|
||||||
|
call assert_equal(['2', '2'], x->matchfuzzy('2', #{}))
|
||||||
|
call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 0}))
|
||||||
|
call assert_equal(['2'], x->matchfuzzy('2', #{limit: 1}))
|
||||||
|
call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 2}))
|
||||||
|
call assert_equal(['2', '2'], x->matchfuzzy('2', #{limit: 3}))
|
||||||
|
call assert_fails("call matchfuzzy(x, '2', #{limit: '2'})", 'E475:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
Reference in New Issue
Block a user