Skip to content

Commit

Permalink
patch 8.2.4760: using matchfuzzy() on a long list can take a while
Browse files Browse the repository at this point in the history
Problem:    Using matchfuzzy() on a long list can take a while.
Solution:   Add a limit to the number of matches. (Yasuhiro Matsumoto,
            closes #10189)
  • Loading branch information
mattn authored and brammool committed Apr 16, 2022
1 parent 693ccd1 commit 9029a6e
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 6 deletions.
7 changes: 6 additions & 1 deletion runtime/doc/builtin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5581,14 +5581,16 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*

If {list} is a list of dictionaries, then the optional {dict}
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
string.
text_cb |Funcref| that will be called for every item
in {list} to get the text for fuzzy matching.
This should accept a dictionary item as the
argument and return the text for that item to
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
matching is NOT supported. The maximum supported {str} length
Expand All @@ -5601,6 +5603,9 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*
empty list is returned. If length of {str} is greater than
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
matching strings.

Expand Down
32 changes: 27 additions & 5 deletions src/search.c
Original file line number Diff line number Diff line change
Expand Up @@ -4648,19 +4648,21 @@ fuzzy_match_in_list(
char_u *key,
callback_T *item_cb,
int retmatchpos,
list_T *fmatchlist)
list_T *fmatchlist,
long max_matches)
{
long len;
fuzzyItem_T *ptrs;
listitem_T *li;
long i = 0;
int found_match = FALSE;
long found_match = 0;
int_u matches[MAX_FUZZY_MATCHES];

len = list_len(items);
if (len == 0)
return;

// TODO: when using a limit use that instead of "len"
ptrs = ALLOC_CLEAR_MULT(fuzzyItem_T, len);
if (ptrs == NULL)
return;
Expand All @@ -4675,6 +4677,15 @@ fuzzy_match_in_list(
ptrs[i].idx = i;
ptrs[i].item = li;
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;
rettv.v_type = VAR_UNKNOWN;
if (li->li_tv.v_type == VAR_STRING) // list of strings
Expand Down Expand Up @@ -4736,13 +4747,13 @@ fuzzy_match_in_list(
}
}
ptrs[i].score = score;
found_match = TRUE;
++found_match;
}
++i;
clear_tv(&rettv);
}

if (found_match)
if (found_match > 0)
{
list_T *l;

Expand Down Expand Up @@ -4822,6 +4833,7 @@ do_fuzzymatch(typval_T *argvars, typval_T *rettv, int retmatchpos)
char_u *key = NULL;
int ret;
int matchseq = FALSE;
long max_matches = 0;

if (in_vim9script()
&& (check_for_list_arg(argvars, 0) == FAIL
Expand Down Expand Up @@ -4879,6 +4891,16 @@ do_fuzzymatch(typval_T *argvars, typval_T *rettv, int retmatchpos)
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"))
matchseq = TRUE;
}
Expand Down Expand Up @@ -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]),
matchseq, key, &cb, retmatchpos, rettv->vval.v_list);
matchseq, key, &cb, retmatchpos, rettv->vval.v_list, max_matches);

done:
free_callback(&cb);
Expand Down
12 changes: 12 additions & 0 deletions src/testdir/test_matchfuzzy.vim
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,16 @@ func Test_matchfuzzypos_mbyte()
call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 'фйд'))
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
2 changes: 2 additions & 0 deletions src/version.c
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4760,
/**/
4759,
/**/
Expand Down

0 comments on commit 9029a6e

Please sign in to comment.