Commit: patch 9.2.1007: fuzzy completion list wrongly sorted after complete()

2 views
Skip to first unread message

Christian Brabandt

unread,
Aug 25, 2026, 4:30:15 PM (13 hours ago) Aug 25
to vim...@googlegroups.com
patch 9.2.1007: fuzzy completion list wrongly sorted after complete()

Commit: https://github.com/vim/vim/commit/7e747229993bf784768fbb22b4bdd3a13dbb3d13
Author: glepnir <gleph...@gmail.com>
Date: Tue Aug 25 20:10:40 2026 +0000

patch 9.2.1007: fuzzy completion list wrongly sorted after complete()

Problem: With 'completeopt' "fuzzy", a re-sort after complete() leaves the
last match unsorted at the end of the list.
Solution: Find the original text by its flag instead of assuming
compl_shows_dir points at it.

closes: #21103

Signed-off-by: glepnir <gleph...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/insexpand.c b/src/insexpand.c
index 2145fc199..e35fe54ca 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -621,6 +621,23 @@ is_first_match(compl_T *match)
return match == compl_first_match;
}

+/*
+ * Return the entry holding the original text, NULL if not found.
+ * It is the first item, or the last one for backward completion.
+ */
+ static compl_T *
+find_original_text_match(void)
+{
+ if (compl_first_match == NULL)
+ return NULL;
+ if (match_at_original_text(compl_first_match))
+ return compl_first_match;
+ if (compl_first_match->cp_prev != NULL
+ && match_at_original_text(compl_first_match->cp_prev))
+ return compl_first_match->cp_prev;
+ return NULL;
+}
+
/*
* Return TRUE when character "c" is part of the item currently being
* completed. Used to decide whether to abandon complete mode when the menu
@@ -1711,19 +1728,22 @@ set_fuzzy_score(void)
}

/*
- * Sort completion matches, excluding the node that contains the leader.
+ * Sort completion matches, leaving the entry with the original text in place.
*/
static void
sort_compl_match_list(int (*compare)(const void *, const void *))
{
- compl_T *compl;
+ compl_T *orig_text;

if (!compl_first_match || is_first_match(compl_first_match->cp_next))
return;

- compl = compl_first_match->cp_prev;
+ orig_text = find_original_text_match();
+ if (orig_text == NULL)
+ return;
+
ins_compl_make_linear();
- if (compl_shows_dir_forward())
+ if (orig_text == compl_first_match)
{
compl_first_match->cp_next->cp_prev = NULL;
compl_first_match->cp_next = mergesort_list(compl_first_match->cp_next,
@@ -1732,14 +1752,16 @@ sort_compl_match_list(int (*compare)(const void *, const void *))
}
else
{
- compl->cp_prev->cp_next = NULL;
+ compl_T *tail;
+
+ orig_text->cp_prev->cp_next = NULL;
compl_first_match = mergesort_list(compl_first_match, cp_get_next,
cp_set_next, cp_get_prev, cp_set_prev, compare);
- compl_T *tail = compl_first_match;
+ tail = compl_first_match;
while (tail->cp_next != NULL)
tail = tail->cp_next;
- tail->cp_next = compl;
- compl->cp_prev = tail;
+ tail->cp_next = orig_text;
+ orig_text->cp_prev = tail;
}
(void)ins_compl_make_cyclic();
}
@@ -2832,30 +2854,20 @@ ins_compl_restart(void)
static void
ins_compl_set_original_text(char_u *str, size_t len)
{
+ compl_T *match = find_original_text_match();
+ char_u *p;
+
// Replace the original text entry.
- // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
- // be at the last item for backward completion
- if (match_at_original_text(compl_first_match)) // safety check
- {
- char_u *p = vim_strnsave(str, len);
- if (p != NULL)
- {
- VIM_CLEAR_STRING(compl_first_match->cp_str);
- compl_first_match->cp_str.string = p;
- compl_first_match->cp_str.length = len;
- }
- }
- else if (compl_first_match->cp_prev != NULL
- && match_at_original_text(compl_first_match->cp_prev))
- {
- char_u *p = vim_strnsave(str, len);
- if (p != NULL)
- {
- VIM_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
- compl_first_match->cp_prev->cp_str.string = p;
- compl_first_match->cp_prev->cp_str.length = len;
- }
- }
+ if (match == NULL)
+ return;
+
+ p = vim_strnsave(str, len);
+ if (p == NULL)
+ return;
+
+ VIM_CLEAR_STRING(match->cp_str);
+ match->cp_str.string = p;
+ match->cp_str.length = len;
}

/*
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index b345a45e4..c8a28961d 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -6795,4 +6795,21 @@ func Test_complete_info_auto()
bwipe!
endfunc

+func Test_complete_fuzzy_resort()
+ new
+ set completeopt=menu,menuone,noselect,fuzzy
+
+ inoremap <buffer> <F5> <Cmd>call complete(1, ['xxxb', 'xb', 'b'])<CR>
+ call feedkeys("i\<F5>b\<C-R>=string(map(complete_info(['items']).items, 'v:val.word'))\<CR>\<Esc>", 'tx')
+ call assert_equal("b['b', 'xb', 'xxxb']", getline(1))
+
+ %d _
+ call setline(1, ['xxxbar', 'xbar', 'bar'])
+ call feedkeys("Go\<C-P>b\<C-R>=string(map(complete_info(['items']).items, 'v:val.word'))\<CR>\<Esc>", 'tx')
+ call assert_equal("b['bar', 'xbar', 'xxxbar']", getline('$'))
+
+ bwipe!
+ set completeopt&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/src/version.c b/src/version.c
index 9104f3c39..218ee0cc8 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =

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