Commit: patch 9.1.1799: completion: crash with autcompletion

1 view
Skip to first unread message

Christian Brabandt

unread,
Sep 28, 2025, 1:15:19 PMSep 28
to vim...@googlegroups.com
patch 9.1.1799: completion: crash with autcompletion

Commit: https://github.com/vim/vim/commit/3aa2edb5572ee0dbc34ab2317041b4cf65b37cc5
Author: Girish Palya <giri...@gmail.com>
Date: Sun Sep 28 17:07:29 2025 +0000

patch 9.1.1799: completion: crash with autcompletion

Problem: completion: crash with autcompletion
(Maxim Kim)
Solution: Rework remove_old_matches (Girish Palya)

fixes: #18378
fixes: #18390
fixes: #18391
closes: #18427

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 bf28bb2c1..2aabacd83 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -7590,40 +7590,52 @@ ins_compl_make_linear(void)
* cpt_sources_index) from the completion list.
*/
#ifdef FEAT_COMPL_FUNC
- static compl_T *
+ static void
remove_old_matches(void)
{
- compl_T *sublist_start = NULL, *sublist_end = NULL, *insert_at = NULL;
- compl_T *current, *next;
- int compl_shown_removed = FALSE;
+ compl_T *current;
+ int shown_match_removed = FALSE;
int forward = (compl_first_match->cp_cpt_source_idx < 0);

+ if (cpt_sources_index < 0)
+ return;
+
compl_direction = forward ? FORWARD : BACKWARD;
compl_shows_dir = compl_direction;

- // Identify the sublist of old matches that needs removal
- for (current = compl_first_match; current != NULL; current = current->cp_next)
+ // When 'fuzzy' is enabled, items are not ordered by their original source
+ // order (cpt_sources_index). So, remove items one by one.
+ for (current = compl_first_match; current != NULL; )
{
- if (current->cp_cpt_source_idx < cpt_sources_index &&
- (forward || (!forward && !insert_at)))
- insert_at = current;
-
if (current->cp_cpt_source_idx == cpt_sources_index)
{
- if (!sublist_start)
- sublist_start = current;
- sublist_end = current;
- if (!compl_shown_removed && compl_shown_match == current)
- compl_shown_removed = TRUE;
- }
+ compl_T *to_delete = current;

- if ((forward && current->cp_cpt_source_idx > cpt_sources_index)
- || (!forward && insert_at))
- break;
+ if (!shown_match_removed && compl_shown_match == current)
+ shown_match_removed = TRUE;
+
+ current = current->cp_next;
+
+ if (to_delete == compl_first_match) // node to remove is at head
+ {
+ compl_first_match = to_delete->cp_next;
+ compl_first_match->cp_prev = NULL;
+ }
+ else if (to_delete->cp_next == NULL) // node to remove is at tail
+ to_delete->cp_prev->cp_next = NULL;
+ else // node is in the moddle
+ {
+ to_delete->cp_prev->cp_next = to_delete->cp_next;
+ to_delete->cp_next->cp_prev = to_delete->cp_prev;
+ }
+ ins_compl_item_free(to_delete);
+ }
+ else
+ current = current->cp_next;
}

// Re-assign compl_shown_match if necessary
- if (compl_shown_removed)
+ if (shown_match_removed)
{
if (forward)
compl_shown_match = compl_first_match;
@@ -7636,27 +7648,19 @@ remove_old_matches(void)
}
}

- if (!sublist_start) // No nodes to remove
- return insert_at;
-
- // Update links to remove sublist
- if (sublist_start->cp_prev)
- sublist_start->cp_prev->cp_next = sublist_end->cp_next;
- else
- compl_first_match = sublist_end->cp_next;
-
- if (sublist_end->cp_next)
- sublist_end->cp_next->cp_prev = sublist_start->cp_prev;
-
- // Free all nodes in the sublist
- sublist_end->cp_next = NULL;
- for (current = sublist_start; current != NULL; current = next)
+ // Re-assign compl_curr_match
+ compl_curr_match = compl_first_match;
+ for (current = compl_first_match; current != NULL; )
{
- next = current->cp_next;
- ins_compl_item_free(current);
+ if ((forward ? current->cp_cpt_source_idx < cpt_sources_index
+ : current->cp_cpt_source_idx > cpt_sources_index))
+ {
+ compl_curr_match = forward ? current : current->cp_next;
+ current = current->cp_next;
+ }
+ else
+ break;
}
-
- return insert_at;
}
#endif

@@ -7716,7 +7720,7 @@ cpt_compl_refresh(void)
cb = get_callback_if_cpt_func(p, cpt_sources_index);
if (cb)
{
- compl_curr_match = remove_old_matches();
+ remove_old_matches();
ret = get_userdefined_compl_info(curwin->w_cursor.col, cb,
&startcol);
if (ret == FAIL)
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index ba449f2db..48623f26a 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -5942,4 +5942,33 @@ func Test_fuzzy_select_item_when_acl()
call StopVimInTerminal(buf)
endfunc

+" Issue #18378: crash when fuzzy reorders items during refresh:always
+func Test_refresh_always_with_fuzzy()
+ func ComplFunc1(findstart, base)
+ if a:findstart
+ return 1
+ else
+ return ['foo', 'foobar']
+ endif
+ endfunc
+ func ComplFunc2(findstart, base)
+ if a:findstart
+ return 1
+ else
+ return #{words: ['foo'], refresh: 'always'}
+ endif
+ endfunc
+ set complete=.,FComplFunc1,FComplFunc2
+ set autocomplete
+ call test_override("char_avail", 1)
+ new
+ call setline(1, ['fox'])
+ exe "normal! Gofo"
+ bw!
+ delfunc ComplFunc1
+ delfunc ComplFunc2
+ set complete& autocomplete&
+ call test_override("char_avail", 0)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/src/version.c b/src/version.c
index 490b882f9..5c1424ed3 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 */
+/**/
+ 1799,
/**/
1798,
/**/
Reply all
Reply to author
Forward
0 new messages