# HG changeset patch # User Bram Moolenaar # Date 1646055004 -3600 # Node ID 6efa2f193c94e3ab0f203a68aba2022daccb4d2c # Parent 4058d89b7a4896ca28dcbb1539e3c5e0de5d20b8 patch 8.2.4483: command completion makes two rounds to collect matches Commit: https://github.com/vim/vim/commit/5de4c4372d4366bc85cb66efb3e373439b9471c5 Author: Yegappan Lakshmanan Date: Mon Feb 28 13:28:38 2022 +0000 patch 8.2.4483: command completion makes two rounds to collect matches Problem: Command completion makes two rounds to collect matches. Solution: Use a growarray to collect matches. (Yegappan Lakshmanan, closes #9860) diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -2814,38 +2814,39 @@ ExpandBufnames( } } - if (p != NULL) + if (p == NULL) + continue; + + if (round == 1) { - if (round == 1) - ++count; - else - { - if (options & WILD_HOME_REPLACE) - p = home_replace_save(buf, p); - else - p = vim_strsave(p); - - if (!fuzzy) - { + ++count; + continue; + } + + if (options & WILD_HOME_REPLACE) + p = home_replace_save(buf, p); + else + p = vim_strsave(p); + + if (!fuzzy) + { #ifdef FEAT_VIMINFO - if (matches != NULL) - { - matches[count].buf = buf; - matches[count].match = p; - count++; - } - else -#endif - (*file)[count++] = p; - } - else - { - fuzmatch[count].idx = count; - fuzmatch[count].str = p; - fuzmatch[count].score = score; - count++; - } + if (matches != NULL) + { + matches[count].buf = buf; + matches[count].match = p; + count++; } + else +#endif + (*file)[count++] = p; + } + else + { + fuzmatch[count].idx = count; + fuzmatch[count].str = p; + fuzmatch[count].score = score; + count++; } } if (count == 0) // no match found, break here diff --git a/src/cmdexpand.c b/src/cmdexpand.c --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -2633,116 +2633,134 @@ ExpandGeneric( int escaped) { int i; - int count = 0; - int round; + garray_T ga; char_u *str; fuzmatch_str_T *fuzmatch = NULL; - int score = 0; + int score = 0; int fuzzy; - int funcsort = FALSE; int match; fuzzy = cmdline_fuzzy_complete(pat); - - // do this loop twice: - // round == 0: count the number of matching names - // round == 1: copy the matching names into allocated memory - for (round = 0; round <= 1; ++round) + *matches = NULL; + *numMatches = 0; + + if (!fuzzy) + ga_init2(&ga, sizeof(char *), 30); + else + ga_init2(&ga, sizeof(fuzmatch_str_T), 30); + + for (i = 0; ; ++i) { - for (i = 0; ; ++i) + str = (*func)(xp, i); + if (str == NULL) // end of list + break; + if (*str == NUL) // skip empty strings + continue; + + if (xp->xp_pattern[0] != NUL) { - str = (*func)(xp, i); - if (str == NULL) // end of list - break; - if (*str == NUL) // skip empty strings - continue; - if (!fuzzy) - match = vim_regexec(regmatch, str, (colnr_T)0); + match = vim_regexec(regmatch, str, (colnr_T)0); else { score = fuzzy_match_str(str, pat); match = (score != 0); } - - if (!match) - continue; - - if (round) + } + else + match = TRUE; + + if (!match) + continue; + + if (escaped) + str = vim_strsave_escaped(str, (char_u *)" \t\\."); + else + str = vim_strsave(str); + if (str == NULL) + { + if (!fuzzy) { - if (escaped) - str = vim_strsave_escaped(str, (char_u *)" \t\\."); - else - str = vim_strsave(str); - if (str == NULL) - { - if (fuzzy) - fuzmatch_str_free(fuzmatch, count); - else if (count > 0) - FreeWild(count, *matches); - *numMatches = 0; - *matches = NULL; - return FAIL; - } - if (fuzzy) - { - fuzmatch[count].idx = count; - fuzmatch[count].str = str; - fuzmatch[count].score = score; - } - else - (*matches)[count] = str; -# ifdef FEAT_MENU - if (func == get_menu_names && str != NULL) - { - // test for separator added by get_menu_names() - str += STRLEN(str) - 1; - if (*str == '\001') - *str = '.'; - } -# endif - } - ++count; - } - if (round == 0) - { - if (count == 0) - return OK; - if (fuzzy) - fuzmatch = ALLOC_MULT(fuzmatch_str_T, count); - else - *matches = ALLOC_MULT(char_u *, count); - if ((!fuzzy && (*matches == NULL)) - || (fuzzy && (fuzmatch == NULL))) - { - *numMatches = 0; - *matches = NULL; + ga_clear_strings(&ga); return FAIL; } - *numMatches = count; - count = 0; + + for (i = 0; i < ga.ga_len; ++i) + { + fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[i]; + vim_free(fuzmatch->str); + } + ga_clear(&ga); + return FAIL; + } + + if (ga_grow(&ga, 1) == FAIL) + { + vim_free(str); + break; } + + if (fuzzy) + { + fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len]; + fuzmatch->idx = ga.ga_len; + fuzmatch->str = str; + fuzmatch->score = score; + } + else + ((char_u **)ga.ga_data)[ga.ga_len] = str; + +# ifdef FEAT_MENU + if (func == get_menu_names) + { + // test for separator added by get_menu_names() + str += STRLEN(str) - 1; + if (*str == '\001') + *str = '.'; + } +# endif + + ++ga.ga_len; } + if (ga.ga_len == 0) + return OK; + // Sort the results. Keep menu's in the specified order. - if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) + if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES + && xp->xp_context != EXPAND_MENUS) { if (xp->xp_context == EXPAND_EXPRESSION || xp->xp_context == EXPAND_FUNCTIONS || xp->xp_context == EXPAND_USER_FUNC || xp->xp_context == EXPAND_DISASSEMBLE) - { + // functions should be sorted to the end. + qsort((void *)ga.ga_data, (size_t)ga.ga_len, sizeof(char_u *), + sort_func_compare); + else + sort_strings((char_u **)ga.ga_data, ga.ga_len); + } + + if (!fuzzy) + { + *matches = ga.ga_data; + *numMatches = ga.ga_len; + } + else + { + int funcsort = FALSE; + + if (xp->xp_context == EXPAND_EXPRESSION + || xp->xp_context == EXPAND_FUNCTIONS + || xp->xp_context == EXPAND_USER_FUNC + || xp->xp_context == EXPAND_DISASSEMBLE) // functions should be sorted to the end. funcsort = TRUE; - if (!fuzzy) - qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *), - sort_func_compare); - } - else - { - if (!fuzzy) - sort_strings(*matches, *numMatches); - } + + if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, + funcsort) == FAIL) + return FAIL; + *numMatches = ga.ga_len; } #if defined(FEAT_SYN_HL) @@ -2751,10 +2769,6 @@ ExpandGeneric( reset_expand_highlight(); #endif - if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count, - funcsort) == FAIL) - return FAIL; - return OK; } @@ -2990,12 +3004,11 @@ ExpandUserDefined( int fuzzy; int match; int score; - int count = 0; fuzzy = cmdline_fuzzy_complete(pat); - *matches = NULL; *numMatches = 0; + retstr = call_user_expand_func(call_func_retstr, xp); if (retstr == NULL) return FAIL; @@ -3013,7 +3026,7 @@ ExpandUserDefined( keep = *e; *e = NUL; - if (xp->xp_pattern[0] || fuzzy) + if (xp->xp_pattern[0] != NUL) { if (!fuzzy) match = vim_regexec(regmatch, s, (colnr_T)0); @@ -3038,12 +3051,11 @@ ExpandUserDefined( { fuzmatch_str_T *fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len]; - fuzmatch->idx = count; + fuzmatch->idx = ga.ga_len; fuzmatch->str = vim_strnsave(s, e - s); fuzmatch->score = score; } ++ga.ga_len; - count++; } if (*e != NUL) @@ -3051,6 +3063,9 @@ ExpandUserDefined( } vim_free(retstr); + if (ga.ga_len == 0) + return OK; + if (!fuzzy) { *matches = ga.ga_data; @@ -3058,10 +3073,10 @@ ExpandUserDefined( } else { - if (fuzzymatches_to_strmatches(ga.ga_data, matches, count, - FALSE) == FAIL) + if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, + FALSE) == FAIL) return FAIL; - *numMatches = count; + *numMatches = ga.ga_len; } return OK; } diff --git a/src/map.c b/src/map.c --- a/src/map.c +++ b/src/map.c @@ -1263,15 +1263,15 @@ ExpandMappings( char_u ***matches) { mapblock_T *mp; + garray_T ga; int hash; int count; - int round; char_u *p; int i; int fuzzy; int match; int score; - fuzmatch_str_T *fuzmatch = NULL; + fuzmatch_str_T *fuzmatch; fuzzy = cmdline_fuzzy_complete(pat); @@ -1280,32 +1280,78 @@ ExpandMappings( *numMatches = 0; // return values in case of FAIL *matches = NULL; - // round == 1: Count the matches. - // round == 2: Build the array to keep the matches. - for (round = 1; round <= 2; ++round) + if (!fuzzy) + ga_init2(&ga, sizeof(char *), 3); + else + ga_init2(&ga, sizeof(fuzmatch_str_T), 3); + + // First search in map modifier arguments + for (i = 0; i < 7; ++i) { - count = 0; + if (i == 0) + p = (char_u *)""; + else if (i == 1) + p = (char_u *)""; +#ifdef FEAT_EVAL + else if (i == 2) + p = (char_u *)"