Patch 8.2.3953
Problem:    Insert completion code is too complicated.
Solution:   More refactoring.  Move function arguments into a struct.
            (Yegappan Lakshmanan, closes #9437)
Files:      src/insexpand.c


*** ../vim-8.2.3952/src/insexpand.c     2021-12-30 11:40:49.936654054 +0000
--- src/insexpand.c     2021-12-31 12:58:29.772742482 +0000
***************
*** 134,139 ****
--- 134,140 ----
   * "compl_curr_match" points to the currently selected entry.
   * "compl_shown_match" is different from compl_curr_match during
   * ins_compl_get_exp().
+  * "compl_old_match" points to previous "compl_curr_match".
   */
  static compl_T    *compl_first_match = NULL;
  static compl_T    *compl_curr_match = NULL;
***************
*** 227,234 ****
  {
      if (!ctrl_x_mode_cmdline())
      {
!       // if the next ^X<> won't ADD nothing, then reset
!       // compl_cont_status
        if (compl_cont_status & CONT_N_ADDS)
            compl_cont_status |= CONT_INTRPT;
        else
--- 228,234 ----
  {
      if (!ctrl_x_mode_cmdline())
      {
!       // if the next ^X<> won't ADD nothing, then reset compl_cont_status
        if (compl_cont_status & CONT_N_ADDS)
            compl_cont_status |= CONT_INTRPT;
        else
***************
*** 392,397 ****
--- 392,407 ----
  }
  
  /*
+  * Returns TRUE if the currently shown text is the original text when the
+  * completion began.
+  */
+     static int
+ ins_compl_at_original_text(compl_T *match)
+ {
+     return match->cp_flags & CP_ORIGINAL_TEXT;
+ }
+ 
+ /*
   * Return TRUE when character "c" is part of the item currently being
   * completed.  Used to decide whether to abandon complete mode when the menu
   * is visible.
***************
*** 426,431 ****
--- 436,546 ----
  }
  
  /*
+  * Get the completed text by inferring the case of the originally typed text.
+  */
+     static char_u *
+ ins_compl_infercase_gettext(
+       char_u  *str,
+       int     actual_len,
+       int     actual_compl_length,
+       int     min_len)
+ {
+     int               *wca;                   // Wide character array.
+     char_u    *p;
+     int               i, c;
+     int               has_lower = FALSE;
+     int               was_letter = FALSE;
+ 
+     IObuff[0] = NUL;
+ 
+     // Allocate wide character array for the completion and fill it.
+     wca = ALLOC_MULT(int, actual_len);
+     if (wca == NULL)
+       return IObuff;
+ 
+     p = str;
+     for (i = 0; i < actual_len; ++i)
+       if (has_mbyte)
+           wca[i] = mb_ptr2char_adv(&p);
+       else
+           wca[i] = *(p++);
+ 
+     // Rule 1: Were any chars converted to lower?
+     p = compl_orig_text;
+     for (i = 0; i < min_len; ++i)
+     {
+       if (has_mbyte)
+           c = mb_ptr2char_adv(&p);
+       else
+           c = *(p++);
+       if (MB_ISLOWER(c))
+       {
+           has_lower = TRUE;
+           if (MB_ISUPPER(wca[i]))
+           {
+               // Rule 1 is satisfied.
+               for (i = actual_compl_length; i < actual_len; ++i)
+                   wca[i] = MB_TOLOWER(wca[i]);
+               break;
+           }
+       }
+     }
+ 
+     // Rule 2: No lower case, 2nd consecutive letter converted to
+     // upper case.
+     if (!has_lower)
+     {
+       p = compl_orig_text;
+       for (i = 0; i < min_len; ++i)
+       {
+           if (has_mbyte)
+               c = mb_ptr2char_adv(&p);
+           else
+               c = *(p++);
+           if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i]))
+           {
+               // Rule 2 is satisfied.
+               for (i = actual_compl_length; i < actual_len; ++i)
+                   wca[i] = MB_TOUPPER(wca[i]);
+               break;
+           }
+           was_letter = MB_ISLOWER(c) || MB_ISUPPER(c);
+       }
+     }
+ 
+     // Copy the original case of the part we typed.
+     p = compl_orig_text;
+     for (i = 0; i < min_len; ++i)
+     {
+       if (has_mbyte)
+           c = mb_ptr2char_adv(&p);
+       else
+           c = *(p++);
+       if (MB_ISLOWER(c))
+           wca[i] = MB_TOLOWER(wca[i]);
+       else if (MB_ISUPPER(c))
+           wca[i] = MB_TOUPPER(wca[i]);
+     }
+ 
+     // Generate encoding specific output from wide character array.
+     // Multi-byte characters can occupy up to five bytes more than
+     // ASCII characters, and we also need one byte for NUL, so stay
+     // six bytes away from the edge of IObuff.
+     p = IObuff;
+     i = 0;
+     while (i < actual_len && (p - IObuff + 6) < IOSIZE)
+       if (has_mbyte)
+           p += (*mb_char2bytes)(wca[i++], p);
+       else
+           *(p++) = wca[i++];
+     *p = NUL;
+ 
+     vim_free(wca);
+ 
+     return IObuff;
+ }
+ 
+ /*
   * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
   * case of the originally typed text is used, and the case of the completed
   * text is inferred, ie this tries to work out what case you probably wanted
***************
*** 442,454 ****
  {
      char_u    *str = str_arg;
      char_u    *p;
-     int               i, c;
      int               actual_len;             // Take multi-byte characters
      int               actual_compl_length;    // into account.
      int               min_len;
-     int               *wca;                   // Wide character array.
-     int               has_lower = FALSE;
-     int               was_letter = FALSE;
      int               flags = 0;
  
      if (p_ic && curbuf->b_p_inf && len > 0)
--- 557,565 ----
***************
*** 488,578 ****
        min_len = actual_len < actual_compl_length
                                           ? actual_len : actual_compl_length;
  
!       // Allocate wide character array for the completion and fill it.
!       wca = ALLOC_MULT(int, actual_len);
!       if (wca != NULL)
!       {
!           p = str;
!           for (i = 0; i < actual_len; ++i)
!               if (has_mbyte)
!                   wca[i] = mb_ptr2char_adv(&p);
!               else
!                   wca[i] = *(p++);
! 
!           // Rule 1: Were any chars converted to lower?
!           p = compl_orig_text;
!           for (i = 0; i < min_len; ++i)
!           {
!               if (has_mbyte)
!                   c = mb_ptr2char_adv(&p);
!               else
!                   c = *(p++);
!               if (MB_ISLOWER(c))
!               {
!                   has_lower = TRUE;
!                   if (MB_ISUPPER(wca[i]))
!                   {
!                       // Rule 1 is satisfied.
!                       for (i = actual_compl_length; i < actual_len; ++i)
!                           wca[i] = MB_TOLOWER(wca[i]);
!                       break;
!                   }
!               }
!           }
! 
!           // Rule 2: No lower case, 2nd consecutive letter converted to
!           // upper case.
!           if (!has_lower)
!           {
!               p = compl_orig_text;
!               for (i = 0; i < min_len; ++i)
!               {
!                   if (has_mbyte)
!                       c = mb_ptr2char_adv(&p);
!                   else
!                       c = *(p++);
!                   if (was_letter && MB_ISUPPER(c) && MB_ISLOWER(wca[i]))
!                   {
!                       // Rule 2 is satisfied.
!                       for (i = actual_compl_length; i < actual_len; ++i)
!                           wca[i] = MB_TOUPPER(wca[i]);
!                       break;
!                   }
!                   was_letter = MB_ISLOWER(c) || MB_ISUPPER(c);
!               }
!           }
! 
!           // Copy the original case of the part we typed.
!           p = compl_orig_text;
!           for (i = 0; i < min_len; ++i)
!           {
!               if (has_mbyte)
!                   c = mb_ptr2char_adv(&p);
!               else
!                   c = *(p++);
!               if (MB_ISLOWER(c))
!                   wca[i] = MB_TOLOWER(wca[i]);
!               else if (MB_ISUPPER(c))
!                   wca[i] = MB_TOUPPER(wca[i]);
!           }
! 
!           // Generate encoding specific output from wide character array.
!           // Multi-byte characters can occupy up to five bytes more than
!           // ASCII characters, and we also need one byte for NUL, so stay
!           // six bytes away from the edge of IObuff.
!           p = IObuff;
!           i = 0;
!           while (i < actual_len && (p - IObuff + 6) < IOSIZE)
!               if (has_mbyte)
!                   p += (*mb_char2bytes)(wca[i++], p);
!               else
!                   *(p++) = wca[i++];
!           *p = NUL;
! 
!           vim_free(wca);
!       }
! 
!       str = IObuff;
      }
      if (cont_s_ipos)
        flags |= CP_CONT_S_IPOS;
--- 599,606 ----
        min_len = actual_len < actual_compl_length
                                           ? actual_len : actual_compl_length;
  
!       str = ins_compl_infercase_gettext(str, actual_len, actual_compl_length,
!                                                               min_len);
      }
      if (cont_s_ipos)
        flags |= CP_CONT_S_IPOS;
***************
*** 618,624 ****
        match = compl_first_match;
        do
        {
!           if (    !(match->cp_flags & CP_ORIGINAL_TEXT)
                    && STRNCMP(match->cp_str, str, len) == 0
                    && match->cp_str[len] == NUL)
                return NOTDONE;
--- 646,652 ----
        match = compl_first_match;
        do
        {
!           if (!ins_compl_at_original_text(match)
                    && STRNCMP(match->cp_str, str, len) == 0
                    && match->cp_str[len] == NUL)
                return NOTDONE;
***************
*** 730,798 ****
      {
        // First match, use it as a whole.
        compl_leader = vim_strsave(match->cp_str);
!       if (compl_leader != NULL)
!       {
!           had_match = (curwin->w_cursor.col > compl_col);
            ins_compl_delete();
!           ins_bytes(compl_leader + ins_compl_len());
!           ins_redraw(FALSE);
  
!           // When the match isn't there (to avoid matching itself) remove it
!           // again after redrawing.
!           if (!had_match)
!               ins_compl_delete();
!           compl_used_match = FALSE;
!       }
      }
!     else
      {
!       // Reduce the text if this match differs from compl_leader.
!       p = compl_leader;
!       s = match->cp_str;
!       while (*p != NUL)
        {
!           if (has_mbyte)
!           {
!               c1 = mb_ptr2char(p);
!               c2 = mb_ptr2char(s);
!           }
!           else
!           {
!               c1 = *p;
!               c2 = *s;
!           }
!           if ((match->cp_flags & CP_ICASE)
!                            ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) : (c1 != c2))
!               break;
!           if (has_mbyte)
!           {
!               MB_PTR_ADV(p);
!               MB_PTR_ADV(s);
!           }
!           else
!           {
!               ++p;
!               ++s;
!           }
        }
! 
!       if (*p != NUL)
        {
!           // Leader was shortened, need to change the inserted text.
!           *p = NUL;
!           had_match = (curwin->w_cursor.col > compl_col);
!           ins_compl_delete();
!           ins_bytes(compl_leader + ins_compl_len());
!           ins_redraw(FALSE);
! 
!           // When the match isn't there (to avoid matching itself) remove it
!           // again after redrawing.
!           if (!had_match)
!               ins_compl_delete();
        }
  
!       compl_used_match = FALSE;
      }
  }
  
  /*
--- 758,826 ----
      {
        // First match, use it as a whole.
        compl_leader = vim_strsave(match->cp_str);
!       if (compl_leader == NULL)
!           return;
! 
!       had_match = (curwin->w_cursor.col > compl_col);
!       ins_compl_delete();
!       ins_bytes(compl_leader + ins_compl_len());
!       ins_redraw(FALSE);
! 
!       // When the match isn't there (to avoid matching itself) remove it
!       // again after redrawing.
!       if (!had_match)
            ins_compl_delete();
!       compl_used_match = FALSE;
  
!       return;
      }
! 
!     // Reduce the text if this match differs from compl_leader.
!     p = compl_leader;
!     s = match->cp_str;
!     while (*p != NUL)
      {
!       if (has_mbyte)
        {
!           c1 = mb_ptr2char(p);
!           c2 = mb_ptr2char(s);
        }
!       else
        {
!           c1 = *p;
!           c2 = *s;
        }
+       if ((match->cp_flags & CP_ICASE)
+               ? (MB_TOLOWER(c1) != MB_TOLOWER(c2)) : (c1 != c2))
+           break;
+       if (has_mbyte)
+       {
+           MB_PTR_ADV(p);
+           MB_PTR_ADV(s);
+       }
+       else
+       {
+           ++p;
+           ++s;
+       }
+     }
  
!     if (*p != NUL)
!     {
!       // Leader was shortened, need to change the inserted text.
!       *p = NUL;
!       had_match = (curwin->w_cursor.col > compl_col);
!       ins_compl_delete();
!       ins_bytes(compl_leader + ins_compl_len());
!       ins_redraw(FALSE);
! 
!       // When the match isn't there (to avoid matching itself) remove it
!       // again after redrawing.
!       if (!had_match)
!           ins_compl_delete();
      }
+ 
+     compl_used_match = FALSE;
  }
  
  /*
***************
*** 827,845 ****
      compl_T *match;
      int           count = 0;
  
!     if (compl_first_match != NULL)
      {
!       // Find the end of the list.
!       match = compl_first_match;
!       // there's always an entry for the compl_orig_text, it doesn't count.
!       while (match->cp_next != NULL && match->cp_next != compl_first_match)
!       {
!           match = match->cp_next;
!           ++count;
!       }
!       match->cp_next = compl_first_match;
!       compl_first_match->cp_prev = match;
      }
      return count;
  }
  
--- 855,874 ----
      compl_T *match;
      int           count = 0;
  
!     if (compl_first_match == NULL)
!       return 0;
! 
!     // Find the end of the list.
!     match = compl_first_match;
!     // there's always an entry for the compl_orig_text, it doesn't count.
!     while (match->cp_next != NULL && match->cp_next != compl_first_match)
      {
!       match = match->cp_next;
!       ++count;
      }
+     match->cp_next = compl_first_match;
+     compl_first_match->cp_prev = match;
+ 
      return count;
  }
  
***************
*** 892,905 ****
  {
      int               h;
  
!     if (compl_match_array != NULL)
!     {
!       h = curwin->w_cline_height;
!       // Update the screen later, before drawing the popup menu over it.
!       pum_call_update_screen();
!       if (h != curwin->w_cline_height)
!           ins_compl_del_pum();
!     }
  }
  
  /*
--- 921,934 ----
  {
      int               h;
  
!     if (compl_match_array == NULL)
!       return;
! 
!     h = curwin->w_cline_height;
!     // Update the screen later, before drawing the popup menu over it.
!     pum_call_update_screen();
!     if (h != curwin->w_cline_height)
!       ins_compl_del_pum();
  }
  
  /*
***************
*** 908,918 ****
      static void
  ins_compl_del_pum(void)
  {
!     if (compl_match_array != NULL)
!     {
!       pum_undisplay();
!       VIM_CLEAR(compl_match_array);
!     }
  }
  
  /*
--- 937,947 ----
      static void
  ins_compl_del_pum(void)
  {
!     if (compl_match_array == NULL)
!       return;
! 
!     pum_undisplay();
!     VIM_CLEAR(compl_match_array);
  }
  
  /*
***************
*** 952,958 ****
      do
      {
        if (compl == NULL
!                     || ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 
2))
            break;
        compl = compl->cp_next;
      } while (compl != compl_first_match);
--- 981,987 ----
      do
      {
        if (compl == NULL
!                     || (!ins_compl_at_original_text(compl) && ++i == 2))
            break;
        compl = compl->cp_next;
      } while (compl != compl_first_match);
***************
*** 972,989 ****
  {
      dict_T *dict = dict_alloc_lock(VAR_FIXED);
  
!     if (dict != NULL)
!     {
!       dict_add_string(dict, "word", match->cp_str);
!       dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
!       dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
!       dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
!       dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
!       if (match->cp_user_data.v_type == VAR_UNKNOWN)
!           dict_add_string(dict, "user_data", (char_u *)"");
!       else
!           dict_add_tv(dict, "user_data", &match->cp_user_data);
!     }
      return dict;
  }
  
--- 1001,1019 ----
  {
      dict_T *dict = dict_alloc_lock(VAR_FIXED);
  
!     if (dict == NULL)
!       return NULL;
! 
!     dict_add_string(dict, "word", match->cp_str);
!     dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]);
!     dict_add_string(dict, "menu", match->cp_text[CPT_MENU]);
!     dict_add_string(dict, "kind", match->cp_text[CPT_KIND]);
!     dict_add_string(dict, "info", match->cp_text[CPT_INFO]);
!     if (match->cp_user_data.v_type == VAR_UNKNOWN)
!       dict_add_string(dict, "user_data", (char_u *)"");
!     else
!       dict_add_tv(dict, "user_data", &match->cp_user_data);
! 
      return dict;
  }
  
***************
*** 1020,1030 ****
  #endif
  
  /*
!  * Show the popup menu for the list of matches.
!  * Also adjusts "compl_shown_match" to an entry that is actually displayed.
   */
!     void
! ins_compl_show_pum(void)
  {
      compl_T     *compl;
      compl_T     *shown_compl = NULL;
--- 1050,1061 ----
  #endif
  
  /*
!  * Build a popup menu to show the completion matches.
!  * Returns the popup menu entry that should be selected. Returns -1 if nothing
!  * should be selected.
   */
!     static int
! ins_compl_build_pum(void)
  {
      compl_T     *compl;
      compl_T     *shown_compl = NULL;
***************
*** 1032,1139 ****
      int               shown_match_ok = FALSE;
      int               i;
      int               cur = -1;
-     colnr_T   col;
      int               lead_len = 0;
  
!     if (!pum_wanted() || !pum_enough_matches())
!       return;
  
! #if defined(FEAT_EVAL)
!     // Dirty hard-coded hack: remove any matchparen highlighting.
!     do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|:3match 
none|endif");
! #endif
  
!     // Update the screen later, before drawing the popup menu over it.
!     pum_call_update_screen();
  
      if (compl_match_array == NULL)
      {
!       // Need to build the popup menu list.
!       compl_match_arraysize = 0;
!       compl = compl_first_match;
!       if (compl_leader != NULL)
!           lead_len = (int)STRLEN(compl_leader);
!       do
        {
!           if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
!                   && (compl_leader == NULL
!                       || ins_compl_equal(compl, compl_leader, lead_len)))
!               ++compl_match_arraysize;
!           compl = compl->cp_next;
!       } while (compl != NULL && compl != compl_first_match);
!       if (compl_match_arraysize == 0)
!           return;
!       compl_match_array = ALLOC_CLEAR_MULT(pumitem_T, compl_match_arraysize);
!       if (compl_match_array != NULL)
        {
!           // If the current match is the original text don't find the first
!           // match after it, don't highlight anything.
!           if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT)
                shown_match_ok = TRUE;
  
!           i = 0;
!           compl = compl_first_match;
!           do
            {
!               if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
!                       && (compl_leader == NULL
!                           || ins_compl_equal(compl, compl_leader, lead_len)))
!               {
!                   if (!shown_match_ok)
!                   {
!                       if (compl == compl_shown_match || did_find_shown_match)
!                       {
!                           // This item is the shown match or this is the
!                           // first displayed item after the shown match.
!                           compl_shown_match = compl;
!                           did_find_shown_match = TRUE;
!                           shown_match_ok = TRUE;
!                       }
!                       else
!                           // Remember this displayed match for when the
!                           // shown match is just below it.
!                           shown_compl = compl;
!                       cur = i;
!                   }
  
!                   if (compl->cp_text[CPT_ABBR] != NULL)
!                       compl_match_array[i].pum_text =
!                                                    compl->cp_text[CPT_ABBR];
!                   else
!                       compl_match_array[i].pum_text = compl->cp_str;
!                   compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
!                   compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
!                   if (compl->cp_text[CPT_MENU] != NULL)
!                       compl_match_array[i++].pum_extra =
!                                                    compl->cp_text[CPT_MENU];
!                   else
!                       compl_match_array[i++].pum_extra = compl->cp_fname;
!               }
  
!               if (compl == compl_shown_match)
!               {
!                   did_find_shown_match = TRUE;
  
!                   // When the original text is the shown match don't set
!                   // compl_shown_match.
!                   if (compl->cp_flags & CP_ORIGINAL_TEXT)
!                       shown_match_ok = TRUE;
  
!                   if (!shown_match_ok && shown_compl != NULL)
!                   {
!                       // The shown match isn't displayed, set it to the
!                       // previously displayed match.
!                       compl_shown_match = shown_compl;
!                       shown_match_ok = TRUE;
!                   }
!               }
!               compl = compl->cp_next;
!           } while (compl != NULL && compl != compl_first_match);
  
!           if (!shown_match_ok)    // no displayed match at all
!               cur = -1;
!       }
!     }
      else
      {
        // popup menu already exists, only need to find the current item.
--- 1063,1187 ----
      int               shown_match_ok = FALSE;
      int               i;
      int               cur = -1;
      int               lead_len = 0;
  
!     // Need to build the popup menu list.
!     compl_match_arraysize = 0;
!     compl = compl_first_match;
!     if (compl_leader != NULL)
!       lead_len = (int)STRLEN(compl_leader);
  
!     do
!     {
!       if (!ins_compl_at_original_text(compl)
!               && (compl_leader == NULL
!                   || ins_compl_equal(compl, compl_leader, lead_len)))
!           ++compl_match_arraysize;
!       compl = compl->cp_next;
!     } while (compl != NULL && compl != compl_first_match);
  
!     if (compl_match_arraysize == 0)
!       return -1;
  
+     compl_match_array = ALLOC_CLEAR_MULT(pumitem_T, compl_match_arraysize);
      if (compl_match_array == NULL)
+       return -1;
+ 
+     // If the current match is the original text don't find the first
+     // match after it, don't highlight anything.
+     if (ins_compl_at_original_text(compl_shown_match))
+       shown_match_ok = TRUE;
+ 
+     i = 0;
+     compl = compl_first_match;
+     do
      {
!       if (!ins_compl_at_original_text(compl)
!               && (compl_leader == NULL
!                   || ins_compl_equal(compl, compl_leader, lead_len)))
        {
!           if (!shown_match_ok)
!           {
!               if (compl == compl_shown_match || did_find_shown_match)
!               {
!                   // This item is the shown match or this is the
!                   // first displayed item after the shown match.
!                   compl_shown_match = compl;
!                   did_find_shown_match = TRUE;
!                   shown_match_ok = TRUE;
!               }
!               else
!                   // Remember this displayed match for when the
!                   // shown match is just below it.
!                   shown_compl = compl;
!               cur = i;
!           }
! 
!           if (compl->cp_text[CPT_ABBR] != NULL)
!               compl_match_array[i].pum_text =
!                   compl->cp_text[CPT_ABBR];
!           else
!               compl_match_array[i].pum_text = compl->cp_str;
!           compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
!           compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
!           if (compl->cp_text[CPT_MENU] != NULL)
!               compl_match_array[i++].pum_extra =
!                   compl->cp_text[CPT_MENU];
!           else
!               compl_match_array[i++].pum_extra = compl->cp_fname;
!       }
! 
!       if (compl == compl_shown_match)
        {
!           did_find_shown_match = TRUE;
! 
!           // When the original text is the shown match don't set
!           // compl_shown_match.
!           if (ins_compl_at_original_text(compl))
                shown_match_ok = TRUE;
  
!           if (!shown_match_ok && shown_compl != NULL)
            {
!               // The shown match isn't displayed, set it to the
!               // previously displayed match.
!               compl_shown_match = shown_compl;
!               shown_match_ok = TRUE;
!           }
!       }
!       compl = compl->cp_next;
!     } while (compl != NULL && compl != compl_first_match);
  
!     if (!shown_match_ok)    // no displayed match at all
!       cur = -1;
  
!     return cur;
! }
  
! /*
!  * Show the popup menu for the list of matches.
!  * Also adjusts "compl_shown_match" to an entry that is actually displayed.
!  */
!     void
! ins_compl_show_pum(void)
! {
!     int               i;
!     int               cur = -1;
!     colnr_T   col;
  
!     if (!pum_wanted() || !pum_enough_matches())
!       return;
  
! #if defined(FEAT_EVAL)
!     // Dirty hard-coded hack: remove any matchparen highlighting.
!     do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|:3match 
none|endif");
! #endif
! 
!     // Update the screen later, before drawing the popup menu over it.
!     pum_call_update_screen();
! 
!     if (compl_match_array == NULL)
!       // Need to build the popup menu list.
!       cur = ins_compl_build_pum();
      else
      {
        // popup menu already exists, only need to find the current item.
***************
*** 1147,1170 ****
            }
      }
  
!     if (compl_match_array != NULL)
!     {
!       // In Replace mode when a $ is displayed at the end of the line only
!       // part of the screen would be updated.  We do need to redraw here.
!       dollar_vcol = -1;
! 
!       // Compute the screen column of the start of the completed text.
!       // Use the cursor to get all wrapping and other settings right.
!       col = curwin->w_cursor.col;
!       curwin->w_cursor.col = compl_col;
!       pum_display(compl_match_array, compl_match_arraysize, cur);
!       curwin->w_cursor.col = col;
  
  #ifdef FEAT_EVAL
!       if (has_completechanged())
!           trigger_complete_changed_event(cur);
  #endif
-     }
  }
  
  #define DICT_FIRST    (1)     // use just first element in "dict"
--- 1195,1218 ----
            }
      }
  
!     if (compl_match_array == NULL)
!       return;
! 
!     // In Replace mode when a $ is displayed at the end of the line only
!     // part of the screen would be updated.  We do need to redraw here.
!     dollar_vcol = -1;
! 
!     // Compute the screen column of the start of the completed text.
!     // Use the cursor to get all wrapping and other settings right.
!     col = curwin->w_cursor.col;
!     curwin->w_cursor.col = compl_col;
!     pum_display(compl_match_array, compl_match_arraysize, cur);
!     curwin->w_cursor.col = col;
  
  #ifdef FEAT_EVAL
!     if (has_completechanged())
!       trigger_complete_changed_event(cur);
  #endif
  }
  
  #define DICT_FIRST    (1)     // use just first element in "dict"
***************
*** 1324,1400 ****
            (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
        }
  
!       if (fp != NULL)
        {
!           // Read dictionary file line by line.
!           // Check each line for a match.
!           while (!got_int && !compl_interrupted
!                                           && !vim_fgets(buf, LSIZE, fp))
            {
!               ptr = buf;
!               while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf)))
                {
!                   ptr = regmatch->startp[0];
!                   if (ctrl_x_mode_line_or_eval())
!                       ptr = find_line_end(ptr);
!                   else
!                       ptr = find_word_end(ptr);
!                   add_r = ins_compl_add_infercase(regmatch->startp[0],
!                                         (int)(ptr - regmatch->startp[0]),
!                                                 p_ic, files[i], *dir, FALSE);
!                   if (thesaurus)
                    {
!                       char_u *wstart;
  
!                       // Add the other matches on the line
!                       ptr = buf;
!                       while (!got_int)
!                       {
!                           // Find start of the next word.  Skip white
!                           // space and punctuation.
!                           ptr = find_word_start(ptr);
!                           if (*ptr == NUL || *ptr == NL)
!                               break;
!                           wstart = ptr;
! 
!                           // Find end of the word.
!                           if (has_mbyte)
!                               // Japanese words may have characters in
!                               // different classes, only separate words
!                               // with single-byte non-word characters.
!                               while (*ptr != NUL)
!                               {
!                                   int l = (*mb_ptr2len)(ptr);
! 
!                                   if (l < 2 && !vim_iswordc(*ptr))
!                                       break;
!                                   ptr += l;
!                               }
!                           else
!                               ptr = find_word_end(ptr);
! 
!                           // Add the word. Skip the regexp match.
!                           if (wstart != regmatch->startp[0])
!                               add_r = ins_compl_add_infercase(wstart,
!                                       (int)(ptr - wstart),
!                                       p_ic, files[i], *dir, FALSE);
!                       }
                    }
-                   if (add_r == OK)
-                       // if dir was BACKWARD then honor it just once
-                       *dir = FORWARD;
-                   else if (add_r == FAIL)
-                       break;
-                   // avoid expensive call to vim_regexec() when at end
-                   // of line
-                   if (*ptr == '\n' || got_int)
-                       break;
                }
!               line_breakcheck();
!               ins_compl_check_keys(50, FALSE);
            }
!           fclose(fp);
        }
      }
  }
  
--- 1372,1447 ----
            (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
        }
  
!       if (fp == NULL)
!           continue;
! 
!       // Read dictionary file line by line.
!       // Check each line for a match.
!       while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp))
        {
!           ptr = buf;
!           while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf)))
            {
!               ptr = regmatch->startp[0];
!               if (ctrl_x_mode_line_or_eval())
!                   ptr = find_line_end(ptr);
!               else
!                   ptr = find_word_end(ptr);
!               add_r = ins_compl_add_infercase(regmatch->startp[0],
!                       (int)(ptr - regmatch->startp[0]),
!                       p_ic, files[i], *dir, FALSE);
!               if (thesaurus)
                {
!                   char_u *wstart;
! 
!                   // Add the other matches on the line
!                   ptr = buf;
!                   while (!got_int)
                    {
!                       // Find start of the next word.  Skip white
!                       // space and punctuation.
!                       ptr = find_word_start(ptr);
!                       if (*ptr == NUL || *ptr == NL)
!                           break;
!                       wstart = ptr;
! 
!                       // Find end of the word.
!                       if (has_mbyte)
!                           // Japanese words may have characters in
!                           // different classes, only separate words
!                           // with single-byte non-word characters.
!                           while (*ptr != NUL)
!                           {
!                               int l = (*mb_ptr2len)(ptr);
! 
!                               if (l < 2 && !vim_iswordc(*ptr))
!                                   break;
!                               ptr += l;
!                           }
!                       else
!                           ptr = find_word_end(ptr);
  
!                       // Add the word. Skip the regexp match.
!                       if (wstart != regmatch->startp[0])
!                           add_r = ins_compl_add_infercase(wstart,
!                                   (int)(ptr - wstart),
!                                   p_ic, files[i], *dir, FALSE);
                    }
                }
!               if (add_r == OK)
!                   // if dir was BACKWARD then honor it just once
!                   *dir = FORWARD;
!               else if (add_r == FAIL)
!                   break;
!               // avoid expensive call to vim_regexec() when at end
!               // of line
!               if (*ptr == '\n' || got_int)
!                   break;
            }
!           line_breakcheck();
!           ins_compl_check_keys(50, FALSE);
        }
+       fclose(fp);
      }
  }
  
***************
*** 1757,1765 ****
      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 (compl_first_match->cp_flags & CP_ORIGINAL_TEXT)       // safety check
      {
        p = vim_strsave(str);
        if (p != NULL)
--- 1804,1812 ----
      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 (ins_compl_at_original_text(compl_first_match))        // safety check
      {
        p = vim_strsave(str);
        if (p != NULL)
***************
*** 1769,1775 ****
        }
      }
      else if (compl_first_match->cp_prev != NULL
!           && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT))
      {
         p = vim_strsave(str);
         if (p != NULL)
--- 1816,1822 ----
        }
      }
      else if (compl_first_match->cp_prev != NULL
!           && ins_compl_at_original_text(compl_first_match->cp_prev))
      {
         p = vim_strsave(str);
         if (p != NULL)
***************
*** 1797,1820 ****
      {
        // When still at the original match use the first entry that matches
        // the leader.
!       if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT)
!       {
!           p = NULL;
!           for (cp = compl_shown_match->cp_next; cp != NULL
!                                && cp != compl_first_match; cp = cp->cp_next)
            {
!               if (compl_leader == NULL
!                       || ins_compl_equal(cp, compl_leader,
!                                                  (int)STRLEN(compl_leader)))
!               {
!                   p = cp->cp_str;
!                   break;
!               }
            }
-           if (p == NULL || (int)STRLEN(p) <= len)
-               return;
        }
!       else
            return;
      }
      p += len;
--- 1844,1865 ----
      {
        // When still at the original match use the first entry that matches
        // the leader.
!       if (!ins_compl_at_original_text(compl_shown_match))
!           return;
! 
!       p = NULL;
!       for (cp = compl_shown_match->cp_next; cp != NULL
!               && cp != compl_first_match; cp = cp->cp_next)
!       {
!           if (compl_leader == NULL
!                   || ins_compl_equal(cp, compl_leader,
!                       (int)STRLEN(compl_leader)))
            {
!               p = cp->cp_str;
!               break;
            }
        }
!       if (p == NULL || (int)STRLEN(p) <= len)
            return;
      }
      p += len;
***************
*** 2860,2866 ****
            match = compl_first_match;
            do
            {
!               if (!(match->cp_flags & CP_ORIGINAL_TEXT))
                {
                    di = dict_alloc();
                    if (di == NULL)
--- 2905,2911 ----
            match = compl_first_match;
            do
            {
!               if (!ins_compl_at_original_text(match))
                {
                    di = dict_alloc();
                    if (di == NULL)
***************
*** 2949,3077 ****
  };
  
  /*
!  * Process the next 'complete' option value in "e_cpt_arg".
   *
   * If successful, the arguments are set as below:
!  *   e_cpt_arg - pointer to the next option value in 'e_cpt_arg'
   *   compl_type_arg - type of insert mode completion to use
!  *   found_all_arg - all matches of this type are found
!  *   buf_arg - search for completions in this buffer
!  *   first_match_pos - position of the first completion match
!  *   last_match_pos - position of the last completion match
!  *   set_match_pos - TRUE if the first match position should be saved to avoid
!  *                 loops after the search wraps around.
!  *   dict - name of the dictionary or thesaurus file to search
!  *   dict_f - flag specifying whether "dict" is an exact file name or not
   *
   * Returns INS_COMPL_CPT_OK if the next value is processed successfully.
   * Returns INS_COMPL_CPT_CONT to skip the current value and process the next
   * option value.
!  * Returns INS_COMPL_CPT_END if all the values in "e_cpt" are processed.
   */
      static int
  process_next_cpt_value(
!       char_u          **e_cpt_arg,
        int             *compl_type_arg,
!       int             *found_all_arg,
!       buf_T           **buf_arg,
!       pos_T           *start_match_pos,
!       pos_T           *first_match_pos,
!       pos_T           *last_match_pos,
!       int             *set_match_pos,
!       char_u          **dict_arg,
!       int             *dict_flag)
  {
-     char_u  *e_cpt = *e_cpt_arg;
      int           compl_type = -1;
      int           status = INS_COMPL_CPT_OK;
-     buf_T   *buf = *buf_arg;
-     int           found_all = FALSE;
-     char_u  *dict = NULL;
-     int           dict_f = 0;
  
!     while (*e_cpt == ',' || *e_cpt == ' ')
!       e_cpt++;
  
!     if (*e_cpt == '.' && !curbuf->b_scanned)
      {
!       buf = curbuf;
!       *first_match_pos = *start_match_pos;
        // Move the cursor back one character so that ^N can match the
        // word immediately after the cursor.
!       if (ctrl_x_mode == CTRL_X_NORMAL && dec(first_match_pos) < 0)
        {
            // Move the cursor to after the last character in the
            // buffer, so that word at start of buffer is found
            // correctly.
!           first_match_pos->lnum = buf->b_ml.ml_line_count;
!           first_match_pos->col =
!               (colnr_T)STRLEN(ml_get(first_match_pos->lnum));
        }
!       *last_match_pos = *first_match_pos;
        compl_type = 0;
  
        // Remember the first match so that the loop stops when we
        // wrap and come back there a second time.
!       *set_match_pos = TRUE;
      }
!     else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL
!           && (buf = ins_compl_next_buf(buf, *e_cpt)) != curbuf)
      {
        // Scan a buffer, but not the current one.
!       if (buf->b_ml.ml_mfp != NULL)   // loaded buffer
        {
            compl_started = TRUE;
!           first_match_pos->col = last_match_pos->col = 0;
!           first_match_pos->lnum = buf->b_ml.ml_line_count + 1;
!           last_match_pos->lnum = 0;
            compl_type = 0;
        }
        else    // unloaded buffer, scan like dictionary
        {
!           found_all = TRUE;
!           if (buf->b_fname == NULL)
            {
                status = INS_COMPL_CPT_CONT;
                goto done;
            }
            compl_type = CTRL_X_DICTIONARY;
!           dict = buf->b_fname;
!           dict_f = DICT_EXACT;
        }
        msg_hist_off = TRUE;    // reset in msg_trunc_attr()
        vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
!               buf->b_fname == NULL
!                   ? buf_spname(buf)
!                   : buf->b_sfname == NULL
!                       ? buf->b_fname
!                       : buf->b_sfname);
        (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
      }
!     else if (*e_cpt == NUL)
        status = INS_COMPL_CPT_END;
      else
      {
        if (ctrl_x_mode_line_or_eval())
            compl_type = -1;
!       else if (*e_cpt == 'k' || *e_cpt == 's')
        {
!           if (*e_cpt == 'k')
                compl_type = CTRL_X_DICTIONARY;
            else
                compl_type = CTRL_X_THESAURUS;
!           if (*++e_cpt != ',' && *e_cpt != NUL)
            {
!               dict = e_cpt;
!               dict_f = DICT_FIRST;
            }
        }
  #ifdef FEAT_FIND_ID
!       else if (*e_cpt == 'i')
            compl_type = CTRL_X_PATH_PATTERNS;
!       else if (*e_cpt == 'd')
            compl_type = CTRL_X_PATH_DEFINES;
  #endif
!       else if (*e_cpt == ']' || *e_cpt == 't')
        {
            msg_hist_off = TRUE;        // reset in msg_trunc_attr()
            compl_type = CTRL_X_TAGS;
--- 2994,3130 ----
  };
  
  /*
!  * state information used for getting the next set of insert completion
!  * matches.
!  */
! typedef struct
! {
!     char_u    *e_cpt;                 // current entry in 'complete'
!     buf_T     *ins_buf;               // buffer being scanned
!     pos_T     *cur_match_pos;                 // current match position
!     pos_T     prev_match_pos;         // previous match position
!     int               set_match_pos;          // save 
first_match_pos/last_match_pos
!     pos_T     first_match_pos;        // first match position
!     pos_T     last_match_pos;         // last match position
!     int               found_all;              // found all matches of a 
certain type.
!     char_u    *dict;                  // dictionary file to search
!     int               dict_f;                 // "dict" is an exact file name 
or not
! } ins_compl_next_state_T;
! 
! /*
!  * Process the next 'complete' option value in st->e_cpt.
   *
   * If successful, the arguments are set as below:
!  *   st->cpt - pointer to the next option value in "st->cpt"
   *   compl_type_arg - type of insert mode completion to use
!  *   st->found_all - all matches of this type are found
!  *   st->ins_buf - search for completions in this buffer
!  *   st->first_match_pos - position of the first completion match
!  *   st->last_match_pos - position of the last completion match
!  *   st->set_match_pos - TRUE if the first match position should be saved to
!  *                     avoid loops after the search wraps around.
!  *   st->dict - name of the dictionary or thesaurus file to search
!  *   st->dict_f - flag specifying whether "dict" is an exact file name or not
   *
   * Returns INS_COMPL_CPT_OK if the next value is processed successfully.
   * Returns INS_COMPL_CPT_CONT to skip the current value and process the next
   * option value.
!  * Returns INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
   */
      static int
  process_next_cpt_value(
!       ins_compl_next_state_T *st,
        int             *compl_type_arg,
!       pos_T           *start_match_pos)
  {
      int           compl_type = -1;
      int           status = INS_COMPL_CPT_OK;
  
!     st->found_all = FALSE;
  
!     while (*st->e_cpt == ',' || *st->e_cpt == ' ')
!       st->e_cpt++;
! 
!     if (*st->e_cpt == '.' && !curbuf->b_scanned)
      {
!       st->ins_buf = curbuf;
!       st->first_match_pos = *start_match_pos;
        // Move the cursor back one character so that ^N can match the
        // word immediately after the cursor.
!       if (ctrl_x_mode == CTRL_X_NORMAL && dec(&st->first_match_pos) < 0)
        {
            // Move the cursor to after the last character in the
            // buffer, so that word at start of buffer is found
            // correctly.
!           st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
!           st->first_match_pos.col =
!               (colnr_T)STRLEN(ml_get(st->first_match_pos.lnum));
        }
!       st->last_match_pos = st->first_match_pos;
        compl_type = 0;
  
        // Remember the first match so that the loop stops when we
        // wrap and come back there a second time.
!       st->set_match_pos = TRUE;
      }
!     else if (vim_strchr((char_u *)"buwU", *st->e_cpt) != NULL
!           && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != 
curbuf)
      {
        // Scan a buffer, but not the current one.
!       if (st->ins_buf->b_ml.ml_mfp != NULL)   // loaded buffer
        {
            compl_started = TRUE;
!           st->first_match_pos.col = st->last_match_pos.col = 0;
!           st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
!           st->last_match_pos.lnum = 0;
            compl_type = 0;
        }
        else    // unloaded buffer, scan like dictionary
        {
!           st->found_all = TRUE;
!           if (st->ins_buf->b_fname == NULL)
            {
                status = INS_COMPL_CPT_CONT;
                goto done;
            }
            compl_type = CTRL_X_DICTIONARY;
!           st->dict = st->ins_buf->b_fname;
!           st->dict_f = DICT_EXACT;
        }
        msg_hist_off = TRUE;    // reset in msg_trunc_attr()
        vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
!               st->ins_buf->b_fname == NULL
!                   ? buf_spname(st->ins_buf)
!                   : st->ins_buf->b_sfname == NULL
!                       ? st->ins_buf->b_fname
!                       : st->ins_buf->b_sfname);
        (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
      }
!     else if (*st->e_cpt == NUL)
        status = INS_COMPL_CPT_END;
      else
      {
        if (ctrl_x_mode_line_or_eval())
            compl_type = -1;
!       else if (*st->e_cpt == 'k' || *st->e_cpt == 's')
        {
!           if (*st->e_cpt == 'k')
                compl_type = CTRL_X_DICTIONARY;
            else
                compl_type = CTRL_X_THESAURUS;
!           if (*++st->e_cpt != ',' && *st->e_cpt != NUL)
            {
!               st->dict = st->e_cpt;
!               st->dict_f = DICT_FIRST;
            }
        }
  #ifdef FEAT_FIND_ID
!       else if (*st->e_cpt == 'i')
            compl_type = CTRL_X_PATH_PATTERNS;
!       else if (*st->e_cpt == 'd')
            compl_type = CTRL_X_PATH_DEFINES;
  #endif
!       else if (*st->e_cpt == ']' || *st->e_cpt == 't')
        {
            msg_hist_off = TRUE;        // reset in msg_trunc_attr()
            compl_type = CTRL_X_TAGS;
***************
*** 3082,3101 ****
            compl_type = -1;
  
        // in any case e_cpt is advanced to the next entry
!       (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ",");
  
!       found_all = TRUE;
        if (compl_type == -1)
            status = INS_COMPL_CPT_CONT;
      }
  
  done:
-     *e_cpt_arg = e_cpt;
      *compl_type_arg = compl_type;
-     *found_all_arg = found_all;
-     *buf_arg = buf;
-     *dict_arg = dict;
-     *dict_flag = dict_f;
      return status;
  }
  
--- 3135,3149 ----
            compl_type = -1;
  
        // in any case e_cpt is advanced to the next entry
!       (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ",");
  
!       st->found_all = TRUE;
        if (compl_type == -1)
            status = INS_COMPL_CPT_CONT;
      }
  
  done:
      *compl_type_arg = compl_type;
      return status;
  }
  
***************
*** 3121,3127 ****
   * thesaurus files.
   */
      static void
! get_next_dict_tsr_completion(int compl_type, char_u **dict, int dict_f)
  {
  #ifdef FEAT_COMPL_FUNC
      if (thesaurus_func_complete(compl_type))
--- 3169,3175 ----
   * thesaurus files.
   */
      static void
! get_next_dict_tsr_completion(int compl_type, char_u *dict, int dict_f)
  {
  #ifdef FEAT_COMPL_FUNC
      if (thesaurus_func_complete(compl_type))
***************
*** 3129,3142 ****
      else
  #endif
        ins_compl_dictionaries(
!               *dict != NULL ? *dict
                : (compl_type == CTRL_X_THESAURUS
                    ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
                    : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
                compl_pattern,
!               *dict != NULL ? dict_f : 0,
                compl_type == CTRL_X_THESAURUS);
-     *dict = NULL;
  }
  
  /*
--- 3177,3189 ----
      else
  #endif
        ins_compl_dictionaries(
!               dict != NULL ? dict
                : (compl_type == CTRL_X_THESAURUS
                    ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
                    : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
                compl_pattern,
!               dict != NULL ? dict_f : 0,
                compl_type == CTRL_X_THESAURUS);
  }
  
  /*
***************
*** 3335,3355 ****
  /*
   * Get the next set of words matching "compl_pattern" for default 
completion(s)
   * (normal ^P/^N and ^X^L).
!  * Search for "compl_pattern" in the buffer "ins_buf" starting from the
!  * position "start_pos" in the "compl_direction" direction. If 
"save_match_pos"
!  * is TRUE, then set the "first_match_pos" and "last_match_pos".
   * Returns OK if a new next match is found, otherwise returns FAIL.
   */
      static int
! get_next_default_completion(
!       buf_T   *ins_buf,               // buffer being scanned
!       pos_T   *start_pos,             // search start position
!       pos_T   *cur_match_pos,         // current match position
!       pos_T   *prev_match_pos,        // previous match position
!       int     *save_match_pos,        // set first_match_pos/last_match_pos
!       pos_T   *first_match_pos,       // first match position
!       pos_T   *last_match_pos,        // last match position
!       int     scan_curbuf)            // scan current buffer for completion
  {
      int               found_new_match = FAIL;
      int               save_p_scs;
--- 3382,3395 ----
  /*
   * Get the next set of words matching "compl_pattern" for default 
completion(s)
   * (normal ^P/^N and ^X^L).
!  * Search for "compl_pattern" in the buffer "st->ins_buf" starting from the
!  * position "st->start_pos" in the "compl_direction" direction. If
!  * "st->set_match_pos" is TRUE, then set the "st->first_match_pos" and
!  * "st->last_match_pos".
   * Returns OK if a new next match is found, otherwise returns FAIL.
   */
      static int
! get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
  {
      int               found_new_match = FAIL;
      int               save_p_scs;
***************
*** 3360,3366 ****
  
      // If 'infercase' is set, don't use 'smartcase' here
      save_p_scs = p_scs;
!     if (ins_buf->b_p_inf)
        p_scs = FALSE;
  
      //        Buffers other than curbuf are scanned from the beginning or the
--- 3400,3406 ----
  
      // If 'infercase' is set, don't use 'smartcase' here
      save_p_scs = p_scs;
!     if (st->ins_buf->b_p_inf)
        p_scs = FALSE;
  
      //        Buffers other than curbuf are scanned from the beginning or the
***************
*** 3368,3376 ****
      //        buffer is a good idea, on the other hand, we always set
      //        wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
      save_p_ws = p_ws;
!     if (ins_buf != curbuf)
        p_ws = FALSE;
!     else if (scan_curbuf)
        p_ws = TRUE;
      looped_around = FALSE;
      for (;;)
--- 3408,3416 ----
      //        buffer is a good idea, on the other hand, we always set
      //        wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
      save_p_ws = p_ws;
!     if (st->ins_buf != curbuf)
        p_ws = FALSE;
!     else if (*st->e_cpt == '.')
        p_ws = TRUE;
      looped_around = FALSE;
      for (;;)
***************
*** 3383,3413 ****
        // has added a word that was at the beginning of the line
        if (ctrl_x_mode_line_or_eval()
                || (compl_cont_status & CONT_SOL))
!           found_new_match = search_for_exact_line(ins_buf, cur_match_pos,
!                   compl_direction, compl_pattern);
        else
!           found_new_match = searchit(NULL, ins_buf, cur_match_pos, NULL,
!                   compl_direction,
!                   compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG,
!                   RE_LAST, NULL);
        --msg_silent;
!       if (!compl_started || *save_match_pos)
        {
            // set "compl_started" even on fail
            compl_started = TRUE;
!           *first_match_pos = *cur_match_pos;
!           *last_match_pos = *cur_match_pos;
!           *save_match_pos = FALSE;
        }
!       else if (first_match_pos->lnum == last_match_pos->lnum
!               && first_match_pos->col == last_match_pos->col)
        {
            found_new_match = FAIL;
        }
        else if ((compl_direction == FORWARD)
!               && (prev_match_pos->lnum > cur_match_pos->lnum
!                   || (prev_match_pos->lnum == cur_match_pos->lnum
!                       && prev_match_pos->col >= cur_match_pos->col)))
        {
            if (looped_around)
                found_new_match = FAIL;
--- 3423,3452 ----
        // has added a word that was at the beginning of the line
        if (ctrl_x_mode_line_or_eval()
                || (compl_cont_status & CONT_SOL))
!           found_new_match = search_for_exact_line(st->ins_buf,
!                           st->cur_match_pos, compl_direction, compl_pattern);
        else
!           found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
!                               NULL, compl_direction, compl_pattern, 1L,
!                               SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
        --msg_silent;
!       if (!compl_started || st->set_match_pos)
        {
            // set "compl_started" even on fail
            compl_started = TRUE;
!           st->first_match_pos = *st->cur_match_pos;
!           st->last_match_pos = *st->cur_match_pos;
!           st->set_match_pos = FALSE;
        }
!       else if (st->first_match_pos.lnum == st->last_match_pos.lnum
!               && st->first_match_pos.col == st->last_match_pos.col)
        {
            found_new_match = FAIL;
        }
        else if ((compl_direction == FORWARD)
!               && (st->prev_match_pos.lnum > st->cur_match_pos->lnum
!                   || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
!                       && st->prev_match_pos.col >= st->cur_match_pos->col)))
        {
            if (looped_around)
                found_new_match = FAIL;
***************
*** 3415,3446 ****
                looped_around = TRUE;
        }
        else if ((compl_direction != FORWARD)
!               && (prev_match_pos->lnum < cur_match_pos->lnum
!                   || (prev_match_pos->lnum == cur_match_pos->lnum
!                       && prev_match_pos->col <= cur_match_pos->col)))
        {
            if (looped_around)
                found_new_match = FAIL;
            else
                looped_around = TRUE;
        }
!       *prev_match_pos = *cur_match_pos;
        if (found_new_match == FAIL)
            break;
  
        // when ADDING, the text before the cursor matches, skip it
!       if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf
!               && start_pos->lnum == cur_match_pos->lnum
!               && start_pos->col  == cur_match_pos->col)
            continue;
  
!       ptr = ins_comp_get_next_word_or_line(ins_buf, cur_match_pos, &len,
!                                                       &cont_s_ipos);
        if (ptr == NULL)
            continue;
  
        if (ins_compl_add_infercase(ptr, len, p_ic,
!                   ins_buf == curbuf ? NULL : ins_buf->b_sfname,
                    0, cont_s_ipos) != NOTDONE)
        {
            found_new_match = OK;
--- 3454,3485 ----
                looped_around = TRUE;
        }
        else if ((compl_direction != FORWARD)
!               && (st->prev_match_pos.lnum < st->cur_match_pos->lnum
!                   || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
!                       && st->prev_match_pos.col <= st->cur_match_pos->col)))
        {
            if (looped_around)
                found_new_match = FAIL;
            else
                looped_around = TRUE;
        }
!       st->prev_match_pos = *st->cur_match_pos;
        if (found_new_match == FAIL)
            break;
  
        // when ADDING, the text before the cursor matches, skip it
!       if ((compl_cont_status & CONT_ADDING) && st->ins_buf == curbuf
!               && start_pos->lnum == st->cur_match_pos->lnum
!               && start_pos->col  == st->cur_match_pos->col)
            continue;
  
!       ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
!                                                       &len, &cont_s_ipos);
        if (ptr == NULL)
            continue;
  
        if (ins_compl_add_infercase(ptr, len, p_ic,
!                   st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
                    0, cont_s_ipos) != NOTDONE)
        {
            found_new_match = OK;
***************
*** 3454,3459 ****
--- 3493,3562 ----
  }
  
  /*
+  * get the next set of completion matches for 'type'.
+  * Returns TRUE if a new match is found. Otherwise returns FALSE.
+  */
+     static int
+ get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
+ {
+     int       found_new_match = FALSE;
+ 
+     switch (type)
+     {
+       case -1:
+           break;
+ #ifdef FEAT_FIND_ID
+       case CTRL_X_PATH_PATTERNS:
+       case CTRL_X_PATH_DEFINES:
+           get_next_include_file_completion(type);
+           break;
+ #endif
+ 
+       case CTRL_X_DICTIONARY:
+       case CTRL_X_THESAURUS:
+           get_next_dict_tsr_completion(type, st->dict, st->dict_f);
+           st->dict = NULL;
+           break;
+ 
+       case CTRL_X_TAGS:
+           get_next_tag_completion();
+           break;
+ 
+       case CTRL_X_FILES:
+           get_next_filename_completion();
+           break;
+ 
+       case CTRL_X_CMDLINE:
+       case CTRL_X_CMDLINE_CTRL_X:
+           get_next_cmdline_completion();
+           break;
+ 
+ #ifdef FEAT_COMPL_FUNC
+       case CTRL_X_FUNCTION:
+       case CTRL_X_OMNI:
+           expand_by_function(type, compl_pattern);
+           break;
+ #endif
+ 
+       case CTRL_X_SPELL:
+           get_next_spell_completion(st->first_match_pos.lnum);
+           break;
+ 
+       default:        // normal ^P/^N and ^X^L
+           found_new_match = get_next_default_completion(st, ini);
+           if (found_new_match == FAIL && st->ins_buf == curbuf)
+               st->found_all = TRUE;
+     }
+ 
+     // check if compl_curr_match has changed, (e.g. other type of
+     // expansion added something)
+     if (type != 0 && compl_curr_match != compl_old_match)
+       found_new_match = OK;
+ 
+     return found_new_match;
+ }
+ 
+ /*
   * Get the next expansion(s), using "compl_pattern".
   * The search starts at position "ini" in curbuf and in the direction
   * compl_direction.
***************
*** 3465,3518 ****
      static int
  ins_compl_get_exp(pos_T *ini)
  {
!     static pos_T      first_match_pos;
!     static pos_T      last_match_pos;
!     static char_u     *e_cpt = (char_u *)"";  // curr. entry in 'complete'
!     static int                found_all = FALSE;      // Found all matches of 
a
!                                               // certain type.
!     static buf_T      *ins_buf = NULL;        // buffer being scanned
! 
!     pos_T     *pos;
      int               i;
      int               found_new_match;
      int               type = ctrl_x_mode;
-     char_u    *dict = NULL;
-     int               dict_f = 0;
-     int               set_match_pos;
-     pos_T     prev_pos = {0, 0, 0};
  
      if (!compl_started)
      {
!       FOR_ALL_BUFFERS(ins_buf)
!           ins_buf->b_scanned = 0;
!       found_all = FALSE;
!       ins_buf = curbuf;
!       e_cpt = (compl_cont_status & CONT_LOCAL)
                                            ? (char_u *)"." : curbuf->b_p_cpt;
!       last_match_pos = first_match_pos = *ini;
      }
!     else if (ins_buf != curbuf && !buf_valid(ins_buf))
!       ins_buf = curbuf;  // In case the buffer was wiped out.
  
      compl_old_match = compl_curr_match;       // remember the last current 
match
!     pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos;
  
      // For ^N/^P loop over all the flags/windows/buffers in 'complete'.
      for (;;)
      {
        found_new_match = FAIL;
!       set_match_pos = FALSE;
  
        // For ^N/^P pick a new entry from e_cpt if compl_started is off,
        // or if found_all says this entry is done.  For ^X^L only use the
        // entries from 'complete' that look in loaded buffers.
        if ((ctrl_x_mode == CTRL_X_NORMAL
                    || ctrl_x_mode_line_or_eval())
!                                       && (!compl_started || found_all))
        {
!           int status = process_next_cpt_value(&e_cpt, &type, &found_all,
!                   &ins_buf, ini, &first_match_pos, &last_match_pos,
!                   &set_match_pos, &dict, &dict_f);
  
            if (status == INS_COMPL_CPT_END)
                break;
--- 3568,3609 ----
      static int
  ins_compl_get_exp(pos_T *ini)
  {
!     static ins_compl_next_state_T st;
      int               i;
      int               found_new_match;
      int               type = ctrl_x_mode;
  
      if (!compl_started)
      {
!       FOR_ALL_BUFFERS(st.ins_buf)
!           st.ins_buf->b_scanned = 0;
!       st.found_all = FALSE;
!       st.ins_buf = curbuf;
!       st.e_cpt = (compl_cont_status & CONT_LOCAL)
                                            ? (char_u *)"." : curbuf->b_p_cpt;
!       st.last_match_pos = st.first_match_pos = *ini;
      }
!     else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
!       st.ins_buf = curbuf;  // In case the buffer was wiped out.
  
      compl_old_match = compl_curr_match;       // remember the last current 
match
!     st.cur_match_pos = (compl_direction == FORWARD)
!                               ? &st.last_match_pos : &st.first_match_pos;
  
      // For ^N/^P loop over all the flags/windows/buffers in 'complete'.
      for (;;)
      {
        found_new_match = FAIL;
!       st.set_match_pos = FALSE;
  
        // For ^N/^P pick a new entry from e_cpt if compl_started is off,
        // or if found_all says this entry is done.  For ^X^L only use the
        // entries from 'complete' that look in loaded buffers.
        if ((ctrl_x_mode == CTRL_X_NORMAL
                    || ctrl_x_mode_line_or_eval())
!                                       && (!compl_started || st.found_all))
        {
!           int status = process_next_cpt_value(&st, &type, ini);
  
            if (status == INS_COMPL_CPT_END)
                break;
***************
*** 3525,3582 ****
        if (compl_pattern == NULL)
            break;
  
!       switch (type)
!       {
!       case -1:
!           break;
! #ifdef FEAT_FIND_ID
!       case CTRL_X_PATH_PATTERNS:
!       case CTRL_X_PATH_DEFINES:
!           get_next_include_file_completion(type);
!           break;
! #endif
! 
!       case CTRL_X_DICTIONARY:
!       case CTRL_X_THESAURUS:
!           get_next_dict_tsr_completion(type, &dict, dict_f);
!           break;
! 
!       case CTRL_X_TAGS:
!           get_next_tag_completion();
!           break;
! 
!       case CTRL_X_FILES:
!           get_next_filename_completion();
!           break;
! 
!       case CTRL_X_CMDLINE:
!       case CTRL_X_CMDLINE_CTRL_X:
!           get_next_cmdline_completion();
!           break;
! 
! #ifdef FEAT_COMPL_FUNC
!       case CTRL_X_FUNCTION:
!       case CTRL_X_OMNI:
!           expand_by_function(type, compl_pattern);
!           break;
! #endif
! 
!       case CTRL_X_SPELL:
!           get_next_spell_completion(first_match_pos.lnum);
!           break;
! 
!       default:        // normal ^P/^N and ^X^L
!           found_new_match = get_next_default_completion(ins_buf, ini, pos,
!                   &prev_pos, &set_match_pos, &first_match_pos,
!                   &last_match_pos, (*e_cpt == '.'));
!           if (found_new_match == FAIL && ins_buf == curbuf)
!               found_all = TRUE;
!       }
! 
!       // check if compl_curr_match has changed, (e.g. other type of
!       // expansion added something)
!       if (type != 0 && compl_curr_match != compl_old_match)
!           found_new_match = OK;
  
        // break the loop for specialized modes (use 'complete' just for the
        // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new
--- 3616,3623 ----
        if (compl_pattern == NULL)
            break;
  
!       // get the next set of completion matches
!       found_new_match = get_next_completion_match(type, &st, ini);
  
        // break the loop for specialized modes (use 'complete' just for the
        // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new
***************
*** 3599,3605 ****
        {
            // Mark a buffer scanned when it has been scanned completely
            if (type == 0 || type == CTRL_X_PATH_PATTERNS)
!               ins_buf->b_scanned = TRUE;
  
            compl_started = FALSE;
        }
--- 3640,3646 ----
        {
            // Mark a buffer scanned when it has been scanned completely
            if (type == 0 || type == CTRL_X_PATH_PATTERNS)
!               st.ins_buf->b_scanned = TRUE;
  
            compl_started = FALSE;
        }
***************
*** 3607,3613 ****
      compl_started = TRUE;
  
      if ((ctrl_x_mode == CTRL_X_NORMAL || ctrl_x_mode_line_or_eval())
!           && *e_cpt == NUL)           // Got to end of 'complete'
        found_new_match = FAIL;
  
      i = -1;           // total of matches, unknown
--- 3648,3654 ----
      compl_started = TRUE;
  
      if ((ctrl_x_mode == CTRL_X_NORMAL || ctrl_x_mode_line_or_eval())
!           && *st.e_cpt == NUL)                // Got to end of 'complete'
        found_new_match = FAIL;
  
      i = -1;           // total of matches, unknown
***************
*** 3699,3705 ****
      // illegal bytes.
      if (compl_len < (int)STRLEN(compl_shown_match->cp_str))
        ins_bytes(compl_shown_match->cp_str + compl_len);
!     if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT)
        compl_used_match = FALSE;
      else
        compl_used_match = TRUE;
--- 3740,3746 ----
      // illegal bytes.
      if (compl_len < (int)STRLEN(compl_shown_match->cp_str))
        ins_bytes(compl_shown_match->cp_str + compl_len);
!     if (ins_compl_at_original_text(compl_shown_match))
        compl_used_match = FALSE;
      else
        compl_used_match = TRUE;
***************
*** 3832,3838 ****
            }
            found_end = FALSE;
        }
!       if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0
                && compl_leader != NULL
                && !ins_compl_equal(compl_shown_match,
                    compl_leader, (int)STRLEN(compl_leader)))
--- 3873,3879 ----
            }
            found_end = FALSE;
        }
!       if (!ins_compl_at_original_text(compl_shown_match)
                && compl_leader != NULL
                && !ins_compl_equal(compl_shown_match,
                    compl_leader, (int)STRLEN(compl_leader)))
***************
*** 3891,3897 ****
        return -1;
  
      if (compl_leader != NULL
!                     && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0)
        // Update "compl_shown_match" to the actually shown match
        ins_compl_update_shown_match();
  
--- 3932,3938 ----
        return -1;
  
      if (compl_leader != NULL
!                     && !ins_compl_at_original_text(compl_shown_match))
        // Update "compl_shown_match" to the actually shown match
        ins_compl_update_shown_match();
  
***************
*** 4661,4667 ****
  
      if (edit_submode_extra == NULL)
      {
!       if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT)
        {
            edit_submode_extra = (char_u *)_("Back at original");
            edit_submode_highl = HLF_W;
--- 4702,4708 ----
  
      if (edit_submode_extra == NULL)
      {
!       if (ins_compl_at_original_text(compl_curr_match))
        {
            edit_submode_extra = (char_u *)_("Back at original");
            edit_submode_highl = HLF_W;
*** ../vim-8.2.3952/src/version.c       2021-12-31 12:19:18.739789858 +0000
--- src/version.c       2021-12-31 12:58:08.704766015 +0000
***************
*** 751,752 ****
--- 751,754 ----
  {   /* Add new patch number below this line */
+ /**/
+     3953,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
168. You have your own domain name.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
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 on the web visit 
https://groups.google.com/d/msgid/vim_dev/20211231130045.3D9841C0A5B%40moolenaar.net.

Raspunde prin e-mail lui