]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1833: completion: fuzzy candidates are not sorted v9.1.1833
authorGirish Palya <girishji@gmail.com>
Mon, 6 Oct 2025 19:06:02 +0000 (19:06 +0000)
committerChristian Brabandt <cb@256bit.org>
Mon, 6 Oct 2025 19:06:02 +0000 (19:06 +0000)
Problem:  completion: fuzzy candidates are not sorted
          (ddad431)
Solution: Always sort fuzzy candidates (Girish Palya)

fixes: #18488
closes: #18497

Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/insexpand.c
src/testdir/test_ins_complete.vim
src/version.c

index 40ba818668517eb6a4e621bda8e04d02aa674d92..6f3845e097bd2cf23be86322b400574bd1064cd5 100644 (file)
@@ -1560,24 +1560,41 @@ theend:
 }
 
 /*
- * Set fuzzy score.
+ * Set fuzzy score for completion matches.
  */
     static void
 set_fuzzy_score(void)
 {
     compl_T *compl;
+    char_u  *pattern;
+    int            use_leader;
 
-    if (!compl_first_match
-           || compl_leader.string == NULL || compl_leader.length == 0)
+    if (compl_first_match == NULL)
        return;
 
-    (void)get_leader_for_startcol(NULL, TRUE); // Clear the cache
+    /* Determine the pattern to match against */
+    use_leader = (compl_leader.string != NULL && compl_leader.length > 0);
+    if (!use_leader)
+    {
+       if (compl_orig_text.string == NULL || compl_orig_text.length == 0)
+           return;
+       pattern = compl_orig_text.string;
+    }
+    else
+    {
+       /* Clear the leader cache once before the loop */
+       (void)get_leader_for_startcol(NULL, TRUE);
+       pattern = NULL;  /* Will be computed per-completion */
+    }
 
+    /* Score all completion matches */
     compl = compl_first_match;
     do
     {
-       compl->cp_score = fuzzy_match_str(compl->cp_str.string,
-               get_leader_for_startcol(compl, TRUE)->string);
+       if (use_leader)
+           pattern = get_leader_for_startcol(compl, TRUE)->string;
+
+       compl->cp_score = fuzzy_match_str(compl->cp_str.string, pattern);
        compl = compl->cp_next;
     } while (compl != NULL && !is_first_match(compl));
 }
@@ -2471,6 +2488,30 @@ ins_compl_has_autocomplete(void)
     return curbuf->b_p_ac >= 0 ? curbuf->b_p_ac : p_ac;
 }
 
+/*
+ * Cacluate fuzzy score and sort completion matches unless sorting is disabled.
+ */
+    static void
+ins_compl_fuzzy_sort(void)
+{
+    int            cur_cot_flags = get_cot_flags();
+
+    // set the fuzzy score in cp_score
+    set_fuzzy_score();
+    // Sort the matches linked list based on fuzzy score
+    if (!(cur_cot_flags & COT_NOSORT))
+    {
+       sort_compl_match_list(cp_compare_fuzzy);
+       if ((cur_cot_flags & (COT_NOINSERT | COT_NOSELECT)) == COT_NOINSERT
+               && compl_first_match)
+       {
+           compl_shown_match = compl_first_match;
+           if (compl_shows_dir_forward() && !compl_autocomplete)
+               compl_shown_match = compl_first_match->cp_next;
+       }
+    }
+}
+
 /*
  * Called after changing "compl_leader".
  * Show the popup menu with a different set of matches.
@@ -2479,7 +2520,6 @@ ins_compl_has_autocomplete(void)
     static void
 ins_compl_new_leader(void)
 {
-    int            cur_cot_flags = get_cot_flags();
     int            save_w_wrow = curwin->w_wrow;
     int            save_w_leftcol = curwin->w_leftcol;
 
@@ -2499,6 +2539,8 @@ ins_compl_new_leader(void)
        ins_compl_set_original_text(compl_leader.string, compl_leader.length);
        if (is_cpt_func_refresh_always())
            cpt_compl_refresh();
+       if (get_cot_flags() & COT_FUZZY)
+           ins_compl_fuzzy_sort();
     }
     else
     {
@@ -2529,24 +2571,6 @@ ins_compl_new_leader(void)
        compl_restarting = FALSE;
     }
 
-    // When 'cot' contains "fuzzy" set the cp_score and maybe sort
-    if (cur_cot_flags & COT_FUZZY)
-    {
-       set_fuzzy_score();
-       // Sort the matches linked list based on fuzzy score
-       if (!(cur_cot_flags & COT_NOSORT))
-       {
-           sort_compl_match_list(cp_compare_fuzzy);
-           if ((cur_cot_flags & (COT_NOINSERT | COT_NOSELECT)) == COT_NOINSERT
-                   && compl_first_match)
-           {
-               compl_shown_match = compl_first_match;
-               if (compl_shows_dir_forward() && !compl_autocomplete)
-                   compl_shown_match = compl_first_match->cp_next;
-           }
-       }
-    }
-
     compl_enter_selects = !compl_used_match && compl_selected_item != -1;
 
     // Show the popup menu with a different set of matches.
@@ -5677,6 +5701,9 @@ ins_compl_get_exp(pos_T *ini)
     if (is_nearest_active() && !ins_compl_has_preinsert())
        sort_compl_match_list(cp_compare_nearest);
 
+    if ((get_cot_flags() & COT_FUZZY) && ins_compl_leader_len() > 0)
+       ins_compl_fuzzy_sort();
+
     return match_count;
 }
 
@@ -6436,7 +6463,9 @@ ins_compl_check_keys(int frequency, int in_compl_func)
            check_elapsed_time();
     }
 
-    if (compl_pending && !got_int && !(cot_flags & COT_NOINSERT)
+    if (compl_pending
+           && !got_int
+           && !(cot_flags & (COT_NOINSERT | COT_FUZZY))
            && (!compl_autocomplete || ins_compl_has_preinsert()))
     {
        // Insert the first match immediately and advance compl_shown_match,
index a340d476e673e1715c0f07a1c884f9cb260c4f76..23c84b28b1f5d5bdc50f12da39153696c7736ac7 100644 (file)
@@ -3531,9 +3531,9 @@ func Test_complete_opt_fuzzy()
 
   set cot=menu,fuzzy
   call feedkeys("Sblue\<CR>bar\<CR>b\<C-X>\<C-P>\<C-Y>\<ESC>", 'tx')
-  call assert_equal('bar', getline('.'))
-  call feedkeys("Sb\<C-X>\<C-N>\<C-Y>\<ESC>", 'tx')
   call assert_equal('blue', getline('.'))
+  call feedkeys("Sb\<C-X>\<C-N>\<C-Y>\<ESC>", 'tx')
+  call assert_equal('bar', getline('.'))
   call feedkeys("Sb\<C-X>\<C-P>\<C-N>\<C-Y>\<ESC>", 'tx')
   call assert_equal('b', getline('.'))
 
@@ -3558,15 +3558,29 @@ func Test_complete_opt_fuzzy()
   call feedkeys("S\<C-X>\<C-O>c\<C-Y>", 'tx')
   call assert_equal('cp_str', getline('.'))
 
+  " Issue 18488: sort after collection when "fuzzy" (unless "nosort")
+  %d
+  set completeopt&
+  set completeopt+=fuzzy,noselect completefuzzycollect=keyword
+  func! PrintMenuWords()
+    let info = complete_info(["items"])
+    call map(info.items, {_, v -> v.word})
+    return info
+  endfunc
+  call setline(1, ['func1', 'xfunc', 'func2'])
+  call feedkeys("Gof\<C-N>\<C-R>=PrintMenuWords()\<CR>\<Esc>0", 'tx')
+  call assert_equal('f{''items'': [''func1'', ''func2'', ''xfunc'']}', getline('.'))
+
   " clean up
   set omnifunc=
   bw!
-  set complete& completeopt&
+  set complete& completeopt& completefuzzycollect&
   autocmd! AAAAA_Group
   augroup! AAAAA_Group
   delfunc OnPumChange
   delfunc Omni_test
   delfunc Comp
+  delfunc PrintMenuWords
   unlet g:item
   unlet g:word
   unlet g:abbr
@@ -4734,18 +4748,6 @@ func Test_nearest_cpt_option()
   call setline(1, ["fo", "foo", "foobar", "foobarbaz"])
   exe "normal! 2jof\<c-p>\<c-r>=PrintMenuWords()\<cr>"
   call assert_equal('fo{''matches'': [''foobarbaz'', ''foobar'', ''foo'', ''fo''], ''selected'': -1}', getline(4))
-
-  " No effect if 'fuzzy' is present
-  set completeopt&
-  set completeopt+=fuzzy,nearest
-  %d
-  call setline(1, ["foo", "fo", "foobarbaz", "foobar"])
-  exe "normal! of\<c-n>\<c-r>=PrintMenuWords()\<cr>"
-  call assert_equal('fo{''matches'': [''fo'', ''foobarbaz'', ''foobar'', ''foo''], ''selected'': 0}', getline(2))
-  %d
-  call setline(1, ["fo", "foo", "foobar", "foobarbaz"])
-  exe "normal! 2jof\<c-p>\<c-r>=PrintMenuWords()\<cr>"
-  call assert_equal('foobar{''matches'': [''foobarbaz'', ''fo'', ''foo'', ''foobar''], ''selected'': 3}', getline(4))
   bw!
 
   set completeopt&
index 0b342a2b1c0fd63923ac8a1a6c088a7959521ac0..f7466e4932f30caedb94ef7bcaa4cbc0746c6f5f 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1833,
 /**/
     1832,
 /**/