Commit: patch 9.1.1833: completion: fuzzy candidates are not sorted

2 views
Skip to first unread message

Christian Brabandt

unread,
Oct 6, 2025, 3:15:13 PM (20 hours ago) Oct 6
to vim...@googlegroups.com
patch 9.1.1833: completion: fuzzy candidates are not sorted

Commit: https://github.com/vim/vim/commit/10aa04e3d432de1d7328ed44b6fa7456750ea3e2
Author: Girish Palya <giri...@gmail.com>
Date: Mon Oct 6 19:06:02 2025 +0000

patch 9.1.1833: completion: fuzzy candidates are not sorted

Problem: completion: fuzzy candidates are not sorted
(ddad431)
Solution: Always sort fuzzy candidates (Girish Palya)

fixes: #18488
closes: #18497

Signed-off-by: Girish Palya <giri...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/insexpand.c b/src/insexpand.c
index 40ba81866..6f3845e09 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -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,
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index a340d476e..23c84b28b 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -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&
diff --git a/src/version.c b/src/version.c
index 0b342a2b1..f7466e493 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1833,
/**/
1832,
/**/
Reply all
Reply to author
Forward
0 new messages