Merge pull request #18249 from zeertzjq/vim-8.2.4760

vim-patch:8.2.{4760,4765}: matchfuzzy() limit
This commit is contained in:
zeertzjq 2022-04-26 08:38:21 +08:00 committed by GitHub
commit d7a7315957
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 37 deletions

View File

@ -4975,7 +4975,7 @@ 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
@ -4983,6 +4983,8 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()*
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
@ -4995,6 +4997,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.

View File

@ -5093,26 +5093,30 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2)
/// 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 items, char_u *const str, const bool matchseq,
static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool matchseq,
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)
{
const long len = tv_list_len(items);
long len = tv_list_len(l);
if (len == 0) {
return;
}
if (max_matches > 0 && len > max_matches) {
len = max_matches;
}
fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T));
long i = 0;
bool found_match = false;
fuzzyItem_T *const items = xcalloc(len, sizeof(fuzzyItem_T));
long match_count = 0;
uint32_t matches[MAX_FUZZY_MATCHES];
// For all the string items in items, get the fuzzy matching score
TV_LIST_ITER(items, li, {
ptrs[i].idx = i;
ptrs[i].item = li;
ptrs[i].score = SCORE_NONE;
TV_LIST_ITER(l, li, {
if (max_matches > 0 && match_count >= max_matches) {
break;
}
char_u *itemstr = NULL;
typval_T rettv;
rettv.v_type = VAR_UNKNOWN;
@ -5143,31 +5147,33 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo
int score;
if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches,
sizeof(matches) / sizeof(matches[0]))) {
MAX_FUZZY_MATCHES)) {
items[match_count].idx = 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.
if (retmatchpos) {
ptrs[i].lmatchpos = tv_list_alloc(kListLenMayKnow);
items[match_count].lmatchpos = tv_list_alloc(kListLenMayKnow);
int j = 0;
const char_u *p = str;
while (*p != NUL) {
if (!ascii_iswhite(utf_ptr2char(p))) {
tv_list_append_number(ptrs[i].lmatchpos, matches[j]);
tv_list_append_number(items[match_count].lmatchpos, matches[j]);
j++;
}
MB_PTR_ADV(p);
}
}
ptrs[i].score = score;
found_match = true;
match_count++;
}
i++;
tv_clear(&rettv);
});
if (found_match) {
if (match_count > 0) {
// Sort the list by the descending order of the match score
qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_match_item_compare);
qsort(items, match_count, sizeof(fuzzyItem_T), fuzzy_match_item_compare);
// For matchfuzzy(), return a list of matched strings.
// ['str1', 'str2', 'str3']
@ -5176,48 +5182,49 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo
// is a list of lists where each list item is a list of matched
// character positions. The third item is a list of matching scores.
// [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]]
list_T *l;
list_T *retlist;
if (retmatchpos) {
const listitem_T *const li = tv_list_find(fmatchlist, 0);
assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL);
l = TV_LIST_ITEM_TV(li)->vval.v_list;
retlist = TV_LIST_ITEM_TV(li)->vval.v_list;
} else {
l = fmatchlist;
retlist = fmatchlist;
}
// Copy the matching strings with a valid score to the return list
for (i = 0; i < len; i++) {
if (ptrs[i].score == SCORE_NONE) {
for (long i = 0; i < match_count; i++) {
if (items[i].score == SCORE_NONE) {
break;
}
tv_list_append_tv(l, TV_LIST_ITEM_TV(ptrs[i].item));
tv_list_append_tv(retlist, TV_LIST_ITEM_TV(items[i].item));
}
// next copy the list of matching positions
if (retmatchpos) {
const listitem_T *li = tv_list_find(fmatchlist, -2);
assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL);
l = TV_LIST_ITEM_TV(li)->vval.v_list;
for (i = 0; i < len; i++) {
if (ptrs[i].score == SCORE_NONE) {
retlist = TV_LIST_ITEM_TV(li)->vval.v_list;
for (long i = 0; i < match_count; i++) {
if (items[i].score == SCORE_NONE) {
break;
}
tv_list_append_list(l, ptrs[i].lmatchpos);
tv_list_append_list(retlist, items[i].lmatchpos);
}
// copy the matching scores
li = tv_list_find(fmatchlist, -1);
assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL);
l = TV_LIST_ITEM_TV(li)->vval.v_list;
for (i = 0; i < len; i++) {
if (ptrs[i].score == SCORE_NONE) {
retlist = TV_LIST_ITEM_TV(li)->vval.v_list;
for (long i = 0; i < match_count; i++) {
if (items[i].score == SCORE_NONE) {
break;
}
tv_list_append_number(l, ptrs[i].score);
tv_list_append_number(retlist, items[i].score);
}
}
}
xfree(ptrs);
xfree(items);
}
/// Do fuzzy matching. Returns the list of matched strings in 'rettv'.
@ -5239,6 +5246,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
Callback cb = CALLBACK_NONE;
const char_u *key = NULL;
bool matchseq = false;
long max_matches = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) {
emsg(_(e_dictreq));
@ -5248,8 +5256,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
// specified.
dict_T *const d = argvars[2].vval.v_dict;
const dictitem_T *const di = tv_dict_find(d, "key", -1);
if (di != NULL) {
const dictitem_T *di;
if ((di = tv_dict_find(d, "key", -1)) != NULL) {
if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL
|| *di->di_tv.vval.v_string == NUL) {
semsg(_(e_invarg2), tv_get_string(&di->di_tv));
@ -5259,7 +5267,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)) {
semsg(_(e_invargval), "text_cb");
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) {
matchseq = true;
}
@ -5278,7 +5293,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,
&cb, retmatchpos, rettv->vval.v_list);
&cb, retmatchpos, rettv->vval.v_list, max_matches);
callback_free(&cb);
}

View File

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