patch 9.1.1799: completion: crash with autcompletion

Commit: 
https://github.com/vim/vim/commit/3aa2edb5572ee0dbc34ab2317041b4cf65b37cc5
Author: Girish Palya <[email protected]>
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 <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

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,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1v2uzK-00H8d1-V6%40256bit.org.

Raspunde prin e-mail lui