vim-patch:8.2.4765: function matchfuzzy() sorts too many items

Problem:    Function matchfuzzy() sorts too many items.
Solution:   Only put matches in the array. (Yegappan Lakshmanan,
            closes vim/vim#10208)
047a7019b2
This commit is contained in:
zeertzjq
2022-04-25 08:15:00 +08:00
parent e6974114fb
commit 13e54f7130

View File

@@ -5093,34 +5093,28 @@ 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. /// 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 'retmatchpos' is true, then return a list of positions where 'str'
/// 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 l, 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) 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); long len = tv_list_len(l);
if (len == 0) { if (len == 0) {
return; return;
} }
if (max_matches > 0 && len > max_matches) {
len = max_matches;
}
// TODO(vim): when using a limit use that instead of "len" fuzzyItem_T *const items = xcalloc(len, sizeof(fuzzyItem_T));
fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T)); long match_count = 0;
long i = 0;
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
TV_LIST_ITER(items, li, { TV_LIST_ITER(l, li, {
ptrs[i].idx = i; if (max_matches > 0 && match_count >= max_matches) {
ptrs[i].item = li; break;
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;
@@ -5153,31 +5147,33 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo
int score; int score;
if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches, 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 // Copy the list of matching positions in itemstr to a list, if
// 'retmatchpos' is set. // 'retmatchpos' is set.
if (retmatchpos) { if (retmatchpos) {
ptrs[i].lmatchpos = tv_list_alloc(kListLenMayKnow); items[match_count].lmatchpos = tv_list_alloc(kListLenMayKnow);
int j = 0; int j = 0;
const char_u *p = str; const char_u *p = str;
while (*p != NUL) { while (*p != NUL) {
if (!ascii_iswhite(utf_ptr2char(p))) { 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++; j++;
} }
MB_PTR_ADV(p); MB_PTR_ADV(p);
} }
} }
ptrs[i].score = score; match_count++;
found_match++;
} }
i++;
tv_clear(&rettv); tv_clear(&rettv);
}); });
if (found_match > 0) { if (match_count > 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(items, match_count, sizeof(fuzzyItem_T), fuzzy_match_item_compare);
// For matchfuzzy(), return a list of matched strings. // For matchfuzzy(), return a list of matched strings.
// ['str1', 'str2', 'str3'] // ['str1', 'str2', 'str3']
@@ -5186,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 // 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. // character positions. The third item is a list of matching scores.
// [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]] // [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]]
list_T *l; list_T *retlist;
if (retmatchpos) { if (retmatchpos) {
const listitem_T *const li = tv_list_find(fmatchlist, 0); const listitem_T *const li = tv_list_find(fmatchlist, 0);
assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL); 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 { } else {
l = fmatchlist; retlist = fmatchlist;
} }
// Copy the matching strings with a valid score to the return list // Copy the matching strings with a valid score to the return list
for (i = 0; i < len; i++) { for (long i = 0; i < match_count; i++) {
if (ptrs[i].score == SCORE_NONE) { if (items[i].score == SCORE_NONE) {
break; 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 // next copy the list of matching positions
if (retmatchpos) { if (retmatchpos) {
const listitem_T *li = tv_list_find(fmatchlist, -2); const listitem_T *li = tv_list_find(fmatchlist, -2);
assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL); 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;
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; break;
} }
tv_list_append_list(l, ptrs[i].lmatchpos); tv_list_append_list(retlist, items[i].lmatchpos);
} }
// copy the matching scores // copy the matching scores
li = tv_list_find(fmatchlist, -1); li = tv_list_find(fmatchlist, -1);
assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL); 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;
for (i = 0; i < len; i++) { for (long i = 0; i < match_count; i++) {
if (ptrs[i].score == SCORE_NONE) { if (items[i].score == SCORE_NONE) {
break; 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'. /// Do fuzzy matching. Returns the list of matched strings in 'rettv'.