To: vim_dev@googlegroups.com Subject: Patch 8.2.4483 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4483 Problem: Command completion makes two rounds to collect matches. Solution: Use a growarray to collect matches. (Yegappan Lakshmanan, closes #9860) Files: src/buffer.c, src/cmdexpand.c, src/map.c, src/testdir/test_cmdline.vim *** ../vim-8.2.4482/src/buffer.c 2022-02-24 13:28:36.570222354 +0000 --- src/buffer.c 2022-02-28 13:20:51.285350690 +0000 *************** *** 2814,2851 **** } } ! if (p != NULL) { ! if (round == 1) ! ++count; ! else ! { ! 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 (count == 0) // no match found, break here --- 2814,2852 ---- } } ! if (p == NULL) ! continue; ! ! if (round == 1) { ! ++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 (count == 0) // no match found, break here *** ../vim-8.2.4482/src/cmdexpand.c 2022-02-27 21:03:17.937471865 +0000 --- src/cmdexpand.c 2022-02-28 13:25:48.676936485 +0000 *************** *** 2633,2748 **** int escaped) { int i; ! int count = 0; ! int round; char_u *str; fuzmatch_str_T *fuzmatch = NULL; ! 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) { ! for (i = 0; ; ++i) ! { ! 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); else { score = fuzzy_match_str(str, pat); match = (score != 0); } ! if (!match) ! continue; ! if (round) ! { ! 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; return FAIL; } ! *numMatches = count; ! count = 0; } } // Sort the results. Keep menu's in the specified order. ! if (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. ! funcsort = TRUE; ! if (!fuzzy) ! qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *), sort_func_compare); - } else ! { ! if (!fuzzy) ! sort_strings(*matches, *numMatches); ! } } #if defined(FEAT_SYN_HL) --- 2633,2766 ---- int escaped) { int i; ! garray_T ga; char_u *str; fuzmatch_str_T *fuzmatch = NULL; ! int score = 0; int fuzzy; int match; fuzzy = cmdline_fuzzy_complete(pat); + *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) { ! str = (*func)(xp, i); ! if (str == NULL) // end of list ! break; ! if (*str == NUL) // skip empty strings ! continue; + if (xp->xp_pattern[0] != NUL) + { if (!fuzzy) ! match = vim_regexec(regmatch, str, (colnr_T)0); else { score = fuzzy_match_str(str, pat); match = (score != 0); } + } + 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) { ! ga_clear_strings(&ga); return FAIL; } ! ! 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 (!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 (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, ! funcsort) == FAIL) ! return FAIL; ! *numMatches = ga.ga_len; } #if defined(FEAT_SYN_HL) *************** *** 2751,2760 **** reset_expand_highlight(); #endif - if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count, - funcsort) == FAIL) - return FAIL; - return OK; } --- 2769,2774 ---- *************** *** 2990,3001 **** 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; --- 3004,3014 ---- int fuzzy; int match; int score; fuzzy = cmdline_fuzzy_complete(pat); *matches = NULL; *numMatches = 0; + retstr = call_user_expand_func(call_func_retstr, xp); if (retstr == NULL) return FAIL; *************** *** 3013,3019 **** keep = *e; *e = NUL; ! if (xp->xp_pattern[0] || fuzzy) { if (!fuzzy) match = vim_regexec(regmatch, s, (colnr_T)0); --- 3026,3032 ---- keep = *e; *e = NUL; ! if (xp->xp_pattern[0] != NUL) { if (!fuzzy) match = vim_regexec(regmatch, s, (colnr_T)0); *************** *** 3038,3049 **** { fuzmatch_str_T *fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len]; ! fuzmatch->idx = count; fuzmatch->str = vim_strnsave(s, e - s); fuzmatch->score = score; } ++ga.ga_len; - count++; } if (*e != NUL) --- 3051,3061 ---- { fuzmatch_str_T *fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len]; ! fuzmatch->idx = ga.ga_len; fuzmatch->str = vim_strnsave(s, e - s); fuzmatch->score = score; } ++ga.ga_len; } if (*e != NUL) *************** *** 3051,3056 **** --- 3063,3071 ---- } vim_free(retstr); + if (ga.ga_len == 0) + return OK; + if (!fuzzy) { *matches = ga.ga_data; *************** *** 3058,3067 **** } else { ! if (fuzzymatches_to_strmatches(ga.ga_data, matches, count, ! FALSE) == FAIL) return FAIL; ! *numMatches = count; } return OK; } --- 3073,3082 ---- } else { ! if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, ! FALSE) == FAIL) return FAIL; ! *numMatches = ga.ga_len; } return OK; } *** ../vim-8.2.4482/src/map.c 2022-02-27 12:07:26.666960401 +0000 --- src/map.c 2022-02-28 13:20:51.289350682 +0000 *************** *** 1263,1277 **** char_u ***matches) { mapblock_T *mp; int hash; int count; - int round; char_u *p; int i; int fuzzy; int match; int score; ! fuzmatch_str_T *fuzmatch = NULL; fuzzy = cmdline_fuzzy_complete(pat); --- 1263,1277 ---- char_u ***matches) { mapblock_T *mp; + garray_T ga; int hash; int count; char_u *p; int i; int fuzzy; int match; int score; ! fuzmatch_str_T *fuzmatch; fuzzy = cmdline_fuzzy_complete(pat); *************** *** 1280,1311 **** *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) ! { ! count = 0; ! // First search in map modifier arguments ! for (i = 0; i < 7; ++i) ! { ! if (i == 0) ! p = (char_u *)""; ! else if (i == 1) ! p = (char_u *)""; #ifdef FEAT_EVAL ! else if (i == 2) ! p = (char_u *)"