]> granicus.if.org Git - vim/commitdiff
patch 8.2.4483: command completion makes two rounds to collect matches v8.2.4483
authorYegappan Lakshmanan <yegappan@yahoo.com>
Mon, 28 Feb 2022 13:28:38 +0000 (13:28 +0000)
committerBram Moolenaar <Bram@vim.org>
Mon, 28 Feb 2022 13:28:38 +0000 (13:28 +0000)
Problem:    Command completion makes two rounds to collect matches.
Solution:   Use a growarray to collect matches. (Yegappan Lakshmanan,
            closes #9860)

src/buffer.c
src/cmdexpand.c
src/map.c
src/testdir/test_cmdline.vim
src/version.c

index e580874906a64980107d5eda90bcc9f32d073e70..8e68d94248245fea3fe87971d3889bff6ee80987 100644 (file)
@@ -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);
+                   ++count;
+                   continue;
+               }
 
-                       if (!fuzzy)
-                       {
+               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
index fee9f660995d7ce453f7d331158fb4d05322f91c..f46045e6e17f262c93269aa92fb0835348a255fc 100644 (file)
@@ -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);
+    *matches = NULL;
+    *numMatches = 0;
+
+    if (!fuzzy)
+       ga_init2(&ga, sizeof(char *), 30);
+    else
+       ga_init2(&ga, sizeof(fuzmatch_str_T), 30);
 
-    // 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)
     {
-       for (i = 0; ; ++i)
-       {
-           str = (*func)(xp, i);
-           if (str == NULL)        // end of list
-               break;
-           if (*str == NUL)        // skip empty strings
-               continue;
+       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);
+               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 (!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 (escaped)
+           str = vim_strsave_escaped(str, (char_u *)" \t\\.");
+       else
+           str = vim_strsave(str);
+       if (str == NULL)
        {
-           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)))
+           if (!fuzzy)
            {
-               *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)
-       {
            // <SNR> functions should be sorted to the end.
-           funcsort = TRUE;
-           if (!fuzzy)
-               qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *),
+           qsort((void *)ga.ga_data, (size_t)ga.ga_len, sizeof(char_u *),
                                                           sort_func_compare);
-       }
        else
-       {
-           if (!fuzzy)
-               sort_strings(*matches, *numMatches);
-       }
+           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,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;
 }
index 594c934d23e1f78597a039d0f93aadb3904e912f..d99a84fc398226e9ab5f10a77cb635b23204484e 100644 (file)
--- 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)
-    {
-       count = 0;
+    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>";
+    // 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>";
+       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
+       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,95 +1363,48 @@ ExpandMappings(
            }
 
            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);
+               vim_free(p);
+               continue;
            }
-           ++count;
-       }
 
-       for (hash = 0; hash < 256; ++hash)
-       {
-           if (expand_isabbrev)
+           if (ga_grow(&ga, 1) == FAIL)
            {
-               if (hash > 0)   // only one abbrev list
-                   break; // for (hash)
-               mp = first_abbr;
+               vim_free(p);
+               break;
            }
-           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;
+               fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
+               fuzmatch->idx = ga.ga_len;
+               fuzmatch->str = p;
+               fuzmatch->score = score;
            }
            else
-           {
-               *matches = ALLOC_MULT(char_u *, count);
-               if (*matches == NULL)
-                   return FAIL;
-           }
-       }
-    } // for (round)
+               ((char_u **)ga.ga_data)[ga.ga_len] = p;
 
-    if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count,
-                                                       FALSE) == FAIL)
+           ++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;
index 1e68f472e2d458ccf9cf0eb6570db071f5b9d0ac..c354fa1023174b85594162e4cb20f972285d3ef5 100644 (file)
@@ -2661,6 +2661,24 @@ func Test_fuzzy_completion_userdefined_func()
   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&
index 9c9ddeb311bcf156351e0def5b2d1a1341ae8311..9ba8bb9bbb6e501af2e065ce31c8da8a76a9d05d 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4483,
 /**/
     4482,
 /**/