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) - { // <SNR> 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) // <SNR> 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) ! // <SNR> 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 *)"<silent>"; ! else if (i == 1) ! p = (char_u *)"<unique>"; #ifdef FEAT_EVAL ! else if (i == 2) ! p = (char_u *)"<script>"; ! else if (i == 3) ! p = (char_u *)"<expr>"; #endif ! else if (i == 4 && !expand_buffer) ! p = (char_u *)"<buffer>"; ! else if (i == 5) ! p = (char_u *)"<nowait>"; ! else if (i == 6) ! p = (char_u *)"<special>"; ! else continue; if (!fuzzy) --- 1280,1357 ---- *numMatches = 0; // return values in case of FAIL *matches = NULL; ! 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) ! { ! if (i == 0) ! p = (char_u *)"<silent>"; ! else if (i == 1) ! p = (char_u *)"<unique>"; #ifdef FEAT_EVAL ! else if (i == 2) ! p = (char_u *)"<script>"; ! else if (i == 3) ! p = (char_u *)"<expr>"; #endif ! else if (i == 4 && !expand_buffer) ! p = (char_u *)"<buffer>"; ! else if (i == 5) ! p = (char_u *)"<nowait>"; ! else if (i == 6) ! p = (char_u *)"<special>"; ! else ! continue; ! ! if (!fuzzy) ! match = vim_regexec(regmatch, p, (colnr_T)0); ! else ! { ! score = fuzzy_match_str(p, pat); ! match = (score != 0); ! } ! ! if (!match) ! continue; ! ! if (ga_grow(&ga, 1) == FAIL) ! break; ! ! if (fuzzy) ! { ! fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len]; ! fuzmatch->idx = ga.ga_len; ! fuzmatch->str = vim_strsave(p); ! fuzmatch->score = score; ! } ! else ! ((char_u **)ga.ga_data)[ga.ga_len] = vim_strsave(p); ! ++ga.ga_len; ! } ! ! for (hash = 0; hash < 256; ++hash) ! { ! if (expand_isabbrev) ! { ! if (hash > 0) // only one abbrev list ! break; // for (hash) ! mp = first_abbr; ! } ! else if (expand_buffer) ! mp = curbuf->b_maphash[hash]; ! else ! mp = maphash[hash]; ! for (; mp; mp = mp->m_next) ! { ! if (!(mp->m_mode & expand_mapmodes)) ! continue; ! ! p = translate_mapping(mp->m_keys); ! if (p == NULL) continue; if (!fuzzy) *************** *** 1317,1411 **** } if (!match) - continue; - - if (round == 2) { ! if (fuzzy) ! { ! fuzmatch[count].idx = count; ! fuzmatch[count].str = vim_strsave(p); ! fuzmatch[count].score = score; ! } ! else ! (*matches)[count] = vim_strsave(p); } - ++count; - } ! for (hash = 0; hash < 256; ++hash) ! { ! if (expand_isabbrev) { ! if (hash > 0) // only one abbrev list ! break; // for (hash) ! mp = first_abbr; } - else if (expand_buffer) - mp = curbuf->b_maphash[hash]; - else - mp = maphash[hash]; - for (; mp; mp = mp->m_next) - { - if (mp->m_mode & expand_mapmodes) - { - p = translate_mapping(mp->m_keys); - if (p != NULL) - { - if (!fuzzy) - match = vim_regexec(regmatch, p, (colnr_T)0); - else - { - score = fuzzy_match_str(p, pat); - match = (score != 0); - } - - if (match) - { - if (round == 2) - { - if (fuzzy) - { - fuzmatch[count].idx = count; - fuzmatch[count].str = p; - fuzmatch[count].score = score; - } - else - (*matches)[count] = p; - p = NULL; - } - ++count; - } - } - vim_free(p); - } - } // for (mp) - } // for (hash) - if (count == 0) // no match found - break; // for (round) - - if (round == 1) - { if (fuzzy) { ! fuzmatch = ALLOC_MULT(fuzmatch_str_T, count); ! if (fuzmatch == NULL) ! return FAIL; } else ! { ! *matches = ALLOC_MULT(char_u *, count); ! if (*matches == NULL) ! return FAIL; ! } ! } ! } // for (round) ! if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count, ! FALSE) == FAIL) return FAIL; if (count > 1) { char_u **ptr1; --- 1363,1410 ---- } if (!match) { ! vim_free(p); ! continue; } ! if (ga_grow(&ga, 1) == FAIL) { ! vim_free(p); ! break; } if (fuzzy) { ! fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len]; ! fuzmatch->idx = ga.ga_len; ! fuzmatch->str = p; ! fuzmatch->score = score; } else ! ((char_u **)ga.ga_data)[ga.ga_len] = p; ! ++ga.ga_len; ! } // for (mp) ! } // for (hash) ! ! if (ga.ga_len == 0) return FAIL; + if (!fuzzy) + { + *matches = ga.ga_data; + *numMatches = ga.ga_len; + } + else + { + if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, + FALSE) == FAIL) + return FAIL; + *numMatches = ga.ga_len; + } + + count = *numMatches; if (count > 1) { char_u **ptr1; *** ../vim-8.2.4482/src/testdir/test_cmdline.vim 2022-02-27 21:03:17.937471865 +0000 --- src/testdir/test_cmdline.vim 2022-02-28 13:20:51.289350682 +0000 *************** *** 2661,2666 **** --- 2661,2684 ---- set wildoptions& endfunc + " <SNR> functions should be sorted to the end + func Test_fuzzy_completion_userdefined_snr_func() + func s:Sendmail() + endfunc + func SendSomemail() + endfunc + func S1e2n3dmail() + endfunc + set wildoptions=fuzzy + call feedkeys(":call sendmail\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"call SendSomemail() S1e2n3dmail() ' + \ .. expand("<SID>") .. 'Sendmail()', @:) + set wildoptions& + delfunc s:Sendmail + delfunc SendSomemail + delfunc S1e2n3dmail + endfunc + " user defined command name completion func Test_fuzzy_completion_userdefined_cmd() set wildoptions& *** ../vim-8.2.4482/src/version.c 2022-02-27 21:03:17.937471865 +0000 --- src/version.c 2022-02-28 13:22:51.085181069 +0000 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 4483, /**/ -- hundred-and-one symptoms of being an internet addict: 122. You ask if the Netaholics Anonymous t-shirt you ordered can be sent to you via e-mail. /// Bram Moolenaar -- b...@moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org /// -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/vim_dev/20220228133052.1AD291C14BE%40moolenaar.net.