1
0
forked from aniani/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 #10189)
This commit is contained in:
Yasuhiro Matsumoto
2022-04-16 12:35:35 +01:00
committed by Bram Moolenaar
parent 693ccd1160
commit 9029a6e993
4 changed files with 47 additions and 6 deletions

View File

@@ -5581,7 +5581,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
@@ -5589,6 +5589,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
@@ -5601,6 +5603,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.

View File

@@ -4648,19 +4648,21 @@ fuzzy_match_in_list(
char_u *key, char_u *key,
callback_T *item_cb, callback_T *item_cb,
int retmatchpos, int retmatchpos,
list_T *fmatchlist) list_T *fmatchlist,
long max_matches)
{ {
long len; long len;
fuzzyItem_T *ptrs; fuzzyItem_T *ptrs;
listitem_T *li; listitem_T *li;
long i = 0; long i = 0;
int found_match = FALSE; long found_match = 0;
int_u matches[MAX_FUZZY_MATCHES]; int_u matches[MAX_FUZZY_MATCHES];
len = list_len(items); len = list_len(items);
if (len == 0) if (len == 0)
return; return;
// TODO: when using a limit use that instead of "len"
ptrs = ALLOC_CLEAR_MULT(fuzzyItem_T, len); ptrs = ALLOC_CLEAR_MULT(fuzzyItem_T, len);
if (ptrs == NULL) if (ptrs == NULL)
return; return;
@@ -4675,6 +4677,15 @@ fuzzy_match_in_list(
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: instead of putting all items in ptrs[] should only add
// matching items there.
if (max_matches > 0 && found_match >= max_matches)
{
i++;
continue;
}
itemstr = NULL; itemstr = NULL;
rettv.v_type = VAR_UNKNOWN; rettv.v_type = VAR_UNKNOWN;
if (li->li_tv.v_type == VAR_STRING) // list of strings if (li->li_tv.v_type == VAR_STRING) // list of strings
@@ -4736,13 +4747,13 @@ fuzzy_match_in_list(
} }
} }
ptrs[i].score = score; ptrs[i].score = score;
found_match = TRUE; ++found_match;
} }
++i; ++i;
clear_tv(&rettv); clear_tv(&rettv);
} }
if (found_match) if (found_match > 0)
{ {
list_T *l; list_T *l;
@@ -4822,6 +4833,7 @@ do_fuzzymatch(typval_T *argvars, typval_T *rettv, int retmatchpos)
char_u *key = NULL; char_u *key = NULL;
int ret; int ret;
int matchseq = FALSE; int matchseq = FALSE;
long max_matches = 0;
if (in_vim9script() if (in_vim9script()
&& (check_for_list_arg(argvars, 0) == FAIL && (check_for_list_arg(argvars, 0) == FAIL
@@ -4879,6 +4891,16 @@ do_fuzzymatch(typval_T *argvars, typval_T *rettv, int retmatchpos)
return; return;
} }
} }
else if ((di = dict_find(d, (char_u *)"limit", -1)) != NULL)
{
if (di->di_tv.v_type != VAR_NUMBER)
{
semsg(_(e_invalid_argument_str), tv_get_string(&di->di_tv));
return;
}
max_matches = (long)tv_get_number_chk(&di->di_tv, NULL);
}
if (dict_has_key(d, "matchseq")) if (dict_has_key(d, "matchseq"))
matchseq = TRUE; matchseq = TRUE;
} }
@@ -4913,7 +4935,7 @@ do_fuzzymatch(typval_T *argvars, typval_T *rettv, int retmatchpos)
} }
fuzzy_match_in_list(argvars[0].vval.v_list, tv_get_string(&argvars[1]), fuzzy_match_in_list(argvars[0].vval.v_list, tv_get_string(&argvars[1]),
matchseq, key, &cb, retmatchpos, rettv->vval.v_list); matchseq, key, &cb, retmatchpos, rettv->vval.v_list, max_matches);
done: done:
free_callback(&cb); free_callback(&cb);

View File

@@ -230,4 +230,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

View File

@@ -746,6 +746,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 */
/**/
4760,
/**/ /**/
4759, 4759,
/**/ /**/