Patch 8.2.3944
Problem:    Insert mode completion functions are too long.
Solution:   Split up into multiple functions. (Yegappan Lakshmanan,
            closes #9431)
Files:      src/insexpand.c, src/testdir/test_ins_complete.vim


*** ../vim-8.2.3943/src/insexpand.c     2021-12-30 10:32:21.156298119 +0000
--- src/insexpand.c     2021-12-30 11:37:46.568928853 +0000
***************
*** 1823,1828 ****
--- 1823,2079 ----
  }
  
  /*
+  * Set the CTRL-X completion mode based on the key 'c' typed after a CTRL-X.
+  * Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
+  * compl_cont_mode and compl_cont_status.
+  * Returns TRUE when the character is not to be inserted.
+  */
+     static int
+ set_ctrl_x_mode(int c)
+ {
+     int retval = FALSE;
+ 
+     switch (c)
+     {
+       case Ctrl_E:
+       case Ctrl_Y:
+           // scroll the window one line up or down
+           ctrl_x_mode = CTRL_X_SCROLL;
+           if (!(State & REPLACE_FLAG))
+               edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
+           else
+               edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
+           edit_submode_pre = NULL;
+           showmode();
+           break;
+       case Ctrl_L:
+           // complete whole line
+           ctrl_x_mode = CTRL_X_WHOLE_LINE;
+           break;
+       case Ctrl_F:
+           // complete filenames
+           ctrl_x_mode = CTRL_X_FILES;
+           break;
+       case Ctrl_K:
+           // complete words from a dictinoary
+           ctrl_x_mode = CTRL_X_DICTIONARY;
+           break;
+       case Ctrl_R:
+           // Register insertion without exiting CTRL-X mode
+           // Simply allow ^R to happen without affecting ^X mode
+           break;
+       case Ctrl_T:
+           // complete words from a thesaurus
+           ctrl_x_mode = CTRL_X_THESAURUS;
+           break;
+ #ifdef FEAT_COMPL_FUNC
+       case Ctrl_U:
+           // user defined completion
+           ctrl_x_mode = CTRL_X_FUNCTION;
+           break;
+       case Ctrl_O:
+           // omni completion
+           ctrl_x_mode = CTRL_X_OMNI;
+           break;
+ #endif
+       case 's':
+       case Ctrl_S:
+           // complete spelling suggestions
+           ctrl_x_mode = CTRL_X_SPELL;
+ #ifdef FEAT_SPELL
+           ++emsg_off; // Avoid getting the E756 error twice.
+           spell_back_to_badword();
+           --emsg_off;
+ #endif
+           break;
+       case Ctrl_RSB:
+           // complete tag names
+           ctrl_x_mode = CTRL_X_TAGS;
+           break;
+ #ifdef FEAT_FIND_ID
+       case Ctrl_I:
+       case K_S_TAB:
+           // complete keywords from included files
+           ctrl_x_mode = CTRL_X_PATH_PATTERNS;
+           break;
+       case Ctrl_D:
+           // complete definitions from included files
+           ctrl_x_mode = CTRL_X_PATH_DEFINES;
+           break;
+ #endif
+       case Ctrl_V:
+       case Ctrl_Q:
+           // complete vim commands
+           ctrl_x_mode = CTRL_X_CMDLINE;
+           break;
+       case Ctrl_Z:
+           // stop completion
+           ctrl_x_mode = CTRL_X_NORMAL;
+           edit_submode = NULL;
+           showmode();
+           retval = TRUE;
+           break;
+       case Ctrl_P:
+       case Ctrl_N:
+           // ^X^P means LOCAL expansion if nothing interrupted (eg we
+           // just started ^X mode, or there were enough ^X's to cancel
+           // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
+           // do normal expansion when interrupting a different mode (say
+           // ^X^F^X^P or ^P^X^X^P, see below)
+           // nothing changes if interrupting mode 0, (eg, the flag
+           // doesn't change when going to ADDING mode  -- Acevedo
+           if (!(compl_cont_status & CONT_INTRPT))
+               compl_cont_status |= CONT_LOCAL;
+           else if (compl_cont_mode != 0)
+               compl_cont_status &= ~CONT_LOCAL;
+           // FALLTHROUGH
+       default:
+           // If we have typed at least 2 ^X's... for modes != 0, we set
+           // compl_cont_status = 0 (eg, as if we had just started ^X
+           // mode).
+           // For mode 0, we set "compl_cont_mode" to an impossible
+           // value, in both cases ^X^X can be used to restart the same
+           // mode (avoiding ADDING mode).
+           // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
+           // 'complete' and local ^P expansions respectively.
+           // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
+           // mode  -- Acevedo
+           if (c == Ctrl_X)
+           {
+               if (compl_cont_mode != 0)
+                   compl_cont_status = 0;
+               else
+                   compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
+           }
+           ctrl_x_mode = CTRL_X_NORMAL;
+           edit_submode = NULL;
+           showmode();
+           break;
+     }
+ 
+     return retval;
+ }
+ 
+ /*
+  * Stop insert completion mode
+  */
+     static int
+ ins_compl_stop(int c, int prev_mode, int retval)
+ {
+     char_u    *ptr;
+ #ifdef FEAT_CINDENT
+     int               want_cindent;
+ #endif
+ 
+     // Get here when we have finished typing a sequence of ^N and
+     // ^P or other completion characters in CTRL-X mode.  Free up
+     // memory that was used, and make sure we can redo the insert.
+     if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E)
+     {
+       // If any of the original typed text has been changed, eg when
+       // ignorecase is set, we must add back-spaces to the redo
+       // buffer.  We add as few as necessary to delete just the part
+       // of the original text that has changed.
+       // When using the longest match, edited the match or used
+       // CTRL-E then don't use the current match.
+       if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E)
+           ptr = compl_curr_match->cp_str;
+       else
+           ptr = NULL;
+       ins_compl_fixRedoBufForLeader(ptr);
+     }
+ 
+ #ifdef FEAT_CINDENT
+     want_cindent = (get_can_cindent() && cindent_on());
+ #endif
+     // When completing whole lines: fix indent for 'cindent'.
+     // Otherwise, break line if it's too long.
+     if (compl_cont_mode == CTRL_X_WHOLE_LINE)
+     {
+ #ifdef FEAT_CINDENT
+       // re-indent the current line
+       if (want_cindent)
+       {
+           do_c_expr_indent();
+           want_cindent = FALSE;       // don't do it again
+       }
+ #endif
+     }
+     else
+     {
+       int prev_col = curwin->w_cursor.col;
+ 
+       // put the cursor on the last char, for 'tw' formatting
+       if (prev_col > 0)
+           dec_cursor();
+       // only format when something was inserted
+       if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E)
+           insertchar(NUL, 0, -1);
+       if (prev_col > 0
+               && ml_get_curline()[curwin->w_cursor.col] != NUL)
+           inc_cursor();
+     }
+ 
+     // If the popup menu is displayed pressing CTRL-Y means accepting
+     // the selection without inserting anything.  When
+     // compl_enter_selects is set the Enter key does the same.
+     if ((c == Ctrl_Y || (compl_enter_selects
+                   && (c == CAR || c == K_KENTER || c == NL)))
+           && pum_visible())
+       retval = TRUE;
+ 
+     // CTRL-E means completion is Ended, go back to the typed text.
+     // but only do this, if the Popup is still visible
+     if (c == Ctrl_E)
+     {
+       ins_compl_delete();
+       if (compl_leader != NULL)
+           ins_bytes(compl_leader + ins_compl_len());
+       else if (compl_first_match != NULL)
+           ins_bytes(compl_orig_text + ins_compl_len());
+       retval = TRUE;
+     }
+ 
+     auto_format(FALSE, TRUE);
+ 
+     // Trigger the CompleteDonePre event to give scripts a chance to
+     // act upon the completion before clearing the info, and restore
+     // ctrl_x_mode, so that complete_info() can be used.
+     ctrl_x_mode = prev_mode;
+     ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
+ 
+     ins_compl_free();
+     compl_started = FALSE;
+     compl_matches = 0;
+     if (!shortmess(SHM_COMPLETIONMENU))
+       msg_clr_cmdline();      // necessary for "noshowmode"
+     ctrl_x_mode = CTRL_X_NORMAL;
+     compl_enter_selects = FALSE;
+     if (edit_submode != NULL)
+     {
+       edit_submode = NULL;
+       showmode();
+     }
+ 
+ #ifdef FEAT_CMDWIN
+     if (c == Ctrl_C && cmdwin_type != 0)
+       // Avoid the popup menu remains displayed when leaving the
+       // command line window.
+       update_screen(0);
+ #endif
+ #ifdef FEAT_CINDENT
+     // Indent now if a key was typed that is in 'cinkeys'.
+     if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0)))
+       do_c_expr_indent();
+ #endif
+     // Trigger the CompleteDone event to give scripts a chance to act
+     // upon the end of completion.
+     ins_apply_autocmds(EVENT_COMPLETEDONE);
+ 
+     return retval;
+ }
+ 
+ /*
   * Prepare for Insert mode completion, or stop it.
   * Called just after typing a character in Insert mode.
   * Returns TRUE when the character is not to be inserted;
***************
*** 1830,1839 ****
      int
  ins_compl_prep(int c)
  {
-     char_u    *ptr;
- #ifdef FEAT_CINDENT
-     int               want_cindent;
- #endif
      int               retval = FALSE;
      int               prev_mode = ctrl_x_mode;
  
--- 2081,2086 ----
***************
*** 1910,2022 ****
      }
  
      if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET)
-     {
        // We have just typed CTRL-X and aren't quite sure which CTRL-X mode
        // it will be yet.  Now we decide.
!       switch (c)
!       {
!           case Ctrl_E:
!           case Ctrl_Y:
!               ctrl_x_mode = CTRL_X_SCROLL;
!               if (!(State & REPLACE_FLAG))
!                   edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
!               else
!                   edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
!               edit_submode_pre = NULL;
!               showmode();
!               break;
!           case Ctrl_L:
!               ctrl_x_mode = CTRL_X_WHOLE_LINE;
!               break;
!           case Ctrl_F:
!               ctrl_x_mode = CTRL_X_FILES;
!               break;
!           case Ctrl_K:
!               ctrl_x_mode = CTRL_X_DICTIONARY;
!               break;
!           case Ctrl_R:
!               // Simply allow ^R to happen without affecting ^X mode
!               break;
!           case Ctrl_T:
!               ctrl_x_mode = CTRL_X_THESAURUS;
!               break;
! #ifdef FEAT_COMPL_FUNC
!           case Ctrl_U:
!               ctrl_x_mode = CTRL_X_FUNCTION;
!               break;
!           case Ctrl_O:
!               ctrl_x_mode = CTRL_X_OMNI;
!               break;
! #endif
!           case 's':
!           case Ctrl_S:
!               ctrl_x_mode = CTRL_X_SPELL;
! #ifdef FEAT_SPELL
!               ++emsg_off;     // Avoid getting the E756 error twice.
!               spell_back_to_badword();
!               --emsg_off;
! #endif
!               break;
!           case Ctrl_RSB:
!               ctrl_x_mode = CTRL_X_TAGS;
!               break;
! #ifdef FEAT_FIND_ID
!           case Ctrl_I:
!           case K_S_TAB:
!               ctrl_x_mode = CTRL_X_PATH_PATTERNS;
!               break;
!           case Ctrl_D:
!               ctrl_x_mode = CTRL_X_PATH_DEFINES;
!               break;
! #endif
!           case Ctrl_V:
!           case Ctrl_Q:
!               ctrl_x_mode = CTRL_X_CMDLINE;
!               break;
!           case Ctrl_Z:
!               ctrl_x_mode = CTRL_X_NORMAL;
!               edit_submode = NULL;
!               showmode();
!               retval = TRUE;
!               break;
!           case Ctrl_P:
!           case Ctrl_N:
!               // ^X^P means LOCAL expansion if nothing interrupted (eg we
!               // just started ^X mode, or there were enough ^X's to cancel
!               // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
!               // do normal expansion when interrupting a different mode (say
!               // ^X^F^X^P or ^P^X^X^P, see below)
!               // nothing changes if interrupting mode 0, (eg, the flag
!               // doesn't change when going to ADDING mode  -- Acevedo
!               if (!(compl_cont_status & CONT_INTRPT))
!                   compl_cont_status |= CONT_LOCAL;
!               else if (compl_cont_mode != 0)
!                   compl_cont_status &= ~CONT_LOCAL;
!               // FALLTHROUGH
!           default:
!               // If we have typed at least 2 ^X's... for modes != 0, we set
!               // compl_cont_status = 0 (eg, as if we had just started ^X
!               // mode).
!               // For mode 0, we set "compl_cont_mode" to an impossible
!               // value, in both cases ^X^X can be used to restart the same
!               // mode (avoiding ADDING mode).
!               // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
!               // 'complete' and local ^P expansions respectively.
!               // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
!               // mode  -- Acevedo
!               if (c == Ctrl_X)
!               {
!                   if (compl_cont_mode != 0)
!                       compl_cont_status = 0;
!                   else
!                       compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
!               }
!               ctrl_x_mode = CTRL_X_NORMAL;
!               edit_submode = NULL;
!               showmode();
!               break;
!       }
!     }
      else if (ctrl_x_mode != CTRL_X_NORMAL)
      {
        // We're already in CTRL-X mode, do we stay in it?
--- 2157,2165 ----
      }
  
      if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET)
        // We have just typed CTRL-X and aren't quite sure which CTRL-X mode
        // it will be yet.  Now we decide.
!       retval = set_ctrl_x_mode(c);
      else if (ctrl_x_mode != CTRL_X_NORMAL)
      {
        // We're already in CTRL-X mode, do we stay in it?
***************
*** 2040,2151 ****
        if ((ctrl_x_mode == CTRL_X_NORMAL && c != Ctrl_N && c != Ctrl_P
                                       && c != Ctrl_R && !ins_compl_pum_key(c))
                || ctrl_x_mode == CTRL_X_FINISHED)
!       {
!           // Get here when we have finished typing a sequence of ^N and
!           // ^P or other completion characters in CTRL-X mode.  Free up
!           // memory that was used, and make sure we can redo the insert.
!           if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E)
!           {
!               // If any of the original typed text has been changed, eg when
!               // ignorecase is set, we must add back-spaces to the redo
!               // buffer.  We add as few as necessary to delete just the part
!               // of the original text that has changed.
!               // When using the longest match, edited the match or used
!               // CTRL-E then don't use the current match.
!               if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E)
!                   ptr = compl_curr_match->cp_str;
!               else
!                   ptr = NULL;
!               ins_compl_fixRedoBufForLeader(ptr);
!           }
! 
! #ifdef FEAT_CINDENT
!           want_cindent = (get_can_cindent() && cindent_on());
! #endif
!           // When completing whole lines: fix indent for 'cindent'.
!           // Otherwise, break line if it's too long.
!           if (compl_cont_mode == CTRL_X_WHOLE_LINE)
!           {
! #ifdef FEAT_CINDENT
!               // re-indent the current line
!               if (want_cindent)
!               {
!                   do_c_expr_indent();
!                   want_cindent = FALSE;       // don't do it again
!               }
! #endif
!           }
!           else
!           {
!               int prev_col = curwin->w_cursor.col;
! 
!               // put the cursor on the last char, for 'tw' formatting
!               if (prev_col > 0)
!                   dec_cursor();
!               // only format when something was inserted
!               if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E)
!                   insertchar(NUL, 0, -1);
!               if (prev_col > 0
!                            && ml_get_curline()[curwin->w_cursor.col] != NUL)
!                   inc_cursor();
!           }
! 
!           // If the popup menu is displayed pressing CTRL-Y means accepting
!           // the selection without inserting anything.  When
!           // compl_enter_selects is set the Enter key does the same.
!           if ((c == Ctrl_Y || (compl_enter_selects
!                                  && (c == CAR || c == K_KENTER || c == NL)))
!                   && pum_visible())
!               retval = TRUE;
! 
!           // CTRL-E means completion is Ended, go back to the typed text.
!           // but only do this, if the Popup is still visible
!           if (c == Ctrl_E)
!           {
!               ins_compl_delete();
!               if (compl_leader != NULL)
!                   ins_bytes(compl_leader + ins_compl_len());
!               else if (compl_first_match != NULL)
!                   ins_bytes(compl_orig_text + ins_compl_len());
!               retval = TRUE;
!           }
! 
!           auto_format(FALSE, TRUE);
! 
!           // Trigger the CompleteDonePre event to give scripts a chance to
!           // act upon the completion before clearing the info, and restore
!           // ctrl_x_mode, so that complete_info() can be used.
!           ctrl_x_mode = prev_mode;
!           ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
! 
!           ins_compl_free();
!           compl_started = FALSE;
!           compl_matches = 0;
!           if (!shortmess(SHM_COMPLETIONMENU))
!               msg_clr_cmdline();      // necessary for "noshowmode"
!           ctrl_x_mode = CTRL_X_NORMAL;
!           compl_enter_selects = FALSE;
!           if (edit_submode != NULL)
!           {
!               edit_submode = NULL;
!               showmode();
!           }
! 
! #ifdef FEAT_CMDWIN
!           if (c == Ctrl_C && cmdwin_type != 0)
!               // Avoid the popup menu remains displayed when leaving the
!               // command line window.
!               update_screen(0);
! #endif
! #ifdef FEAT_CINDENT
!           // Indent now if a key was typed that is in 'cinkeys'.
!           if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0)))
!               do_c_expr_indent();
! #endif
!           // Trigger the CompleteDone event to give scripts a chance to act
!           // upon the end of completion.
!           ins_apply_autocmds(EVENT_COMPLETEDONE);
!       }
      }
      else if (ctrl_x_mode == CTRL_X_LOCAL_MSG)
        // Trigger the CompleteDone event to give scripts a chance to act
--- 2183,2189 ----
        if ((ctrl_x_mode == CTRL_X_NORMAL && c != Ctrl_N && c != Ctrl_P
                                       && c != Ctrl_R && !ins_compl_pum_key(c))
                || ctrl_x_mode == CTRL_X_FINISHED)
!           retval = ins_compl_stop(c, prev_mode, retval);
      }
      else if (ctrl_x_mode == CTRL_X_LOCAL_MSG)
        // Trigger the CompleteDone event to give scripts a chance to act
***************
*** 3199,3204 ****
--- 3237,3338 ----
  }
  
  /*
+  * Return the next word or line from buffer "ins_buf" at position
+  * "cur_match_pos" for completion.  The length of the match is set in "len".
+  */
+     static char_u *
+ ins_comp_get_next_word_or_line(
+       buf_T   *ins_buf,               // buffer being scanned
+       pos_T   *cur_match_pos,         // current match position
+       int     *match_len,
+       int     *cont_s_ipos)           // next ^X<> will set initial_pos
+ {
+     char_u    *ptr;
+     int               len;
+ 
+     *match_len = 0;
+     ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
+       cur_match_pos->col;
+     if (ctrl_x_mode_line_or_eval())
+     {
+       if (compl_cont_status & CONT_ADDING)
+       {
+           if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
+               return NULL;
+           ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
+           if (!p_paste)
+               ptr = skipwhite(ptr);
+       }
+       len = (int)STRLEN(ptr);
+     }
+     else
+     {
+       char_u  *tmp_ptr = ptr;
+ 
+       if (compl_cont_status & CONT_ADDING)
+       {
+           tmp_ptr += compl_length;
+           // Skip if already inside a word.
+           if (vim_iswordp(tmp_ptr))
+               return NULL;
+           // Find start of next word.
+           tmp_ptr = find_word_start(tmp_ptr);
+       }
+       // Find end of this word.
+       tmp_ptr = find_word_end(tmp_ptr);
+       len = (int)(tmp_ptr - ptr);
+ 
+       if ((compl_cont_status & CONT_ADDING) && len == compl_length)
+       {
+           if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count)
+           {
+               // Try next line, if any. the new word will be
+               // "join" as if the normal command "J" was used.
+               // IOSIZE is always greater than
+               // compl_length, so the next STRNCPY always
+               // works -- Acevedo
+               STRNCPY(IObuff, ptr, len);
+               ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
+               tmp_ptr = ptr = skipwhite(ptr);
+               // Find start of next word.
+               tmp_ptr = find_word_start(tmp_ptr);
+               // Find end of next word.
+               tmp_ptr = find_word_end(tmp_ptr);
+               if (tmp_ptr > ptr)
+               {
+                   if (*ptr != ')' && IObuff[len - 1] != TAB)
+                   {
+                       if (IObuff[len - 1] != ' ')
+                           IObuff[len++] = ' ';
+                       // IObuf =~ "\k.* ", thus len >= 2
+                       if (p_js
+                               && (IObuff[len - 2] == '.'
+                                   || (vim_strchr(p_cpo, CPO_JOINSP)
+                                       == NULL
+                                       && (IObuff[len - 2] == '?'
+                                           || IObuff[len - 2] == '!'))))
+                           IObuff[len++] = ' ';
+                   }
+                   // copy as much as possible of the new word
+                   if (tmp_ptr - ptr >= IOSIZE - len)
+                       tmp_ptr = ptr + IOSIZE - len - 1;
+                   STRNCPY(IObuff + len, ptr, tmp_ptr - ptr);
+                   len += (int)(tmp_ptr - ptr);
+                   *cont_s_ipos = TRUE;
+               }
+               IObuff[len] = NUL;
+               ptr = IObuff;
+           }
+           if (len == compl_length)
+               return NULL;
+       }
+     }
+ 
+     *match_len = len;
+     return ptr;
+ }
+ 
+ /*
   * 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
***************
*** 3299,3380 ****
                && start_pos->lnum == cur_match_pos->lnum
                && start_pos->col  == cur_match_pos->col)
            continue;
-       ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
-                                                       cur_match_pos->col;
-       if (ctrl_x_mode_line_or_eval())
-       {
-           if (compl_cont_status & CONT_ADDING)
-           {
-               if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
-                   continue;
-               ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
-               if (!p_paste)
-                   ptr = skipwhite(ptr);
-           }
-           len = (int)STRLEN(ptr);
-       }
-       else
-       {
-           char_u      *tmp_ptr = ptr;
  
!           if (compl_cont_status & CONT_ADDING)
!           {
!               tmp_ptr += compl_length;
!               // Skip if already inside a word.
!               if (vim_iswordp(tmp_ptr))
!                   continue;
!               // Find start of next word.
!               tmp_ptr = find_word_start(tmp_ptr);
!           }
!           // Find end of this word.
!           tmp_ptr = find_word_end(tmp_ptr);
!           len = (int)(tmp_ptr - ptr);
  
-           if ((compl_cont_status & CONT_ADDING) && len == compl_length)
-           {
-               if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count)
-               {
-                   // Try next line, if any. the new word will be
-                   // "join" as if the normal command "J" was used.
-                   // IOSIZE is always greater than
-                   // compl_length, so the next STRNCPY always
-                   // works -- Acevedo
-                   STRNCPY(IObuff, ptr, len);
-                   ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
-                   tmp_ptr = ptr = skipwhite(ptr);
-                   // Find start of next word.
-                   tmp_ptr = find_word_start(tmp_ptr);
-                   // Find end of next word.
-                   tmp_ptr = find_word_end(tmp_ptr);
-                   if (tmp_ptr > ptr)
-                   {
-                       if (*ptr != ')' && IObuff[len - 1] != TAB)
-                       {
-                           if (IObuff[len - 1] != ' ')
-                               IObuff[len++] = ' ';
-                           // IObuf =~ "\k.* ", thus len >= 2
-                           if (p_js
-                                   && (IObuff[len - 2] == '.'
-                                       || (vim_strchr(p_cpo, CPO_JOINSP)
-                                           == NULL
-                                           && (IObuff[len - 2] == '?'
-                                               || IObuff[len - 2] == '!'))))
-                               IObuff[len++] = ' ';
-                       }
-                       // copy as much as possible of the new word
-                       if (tmp_ptr - ptr >= IOSIZE - len)
-                           tmp_ptr = ptr + IOSIZE - len - 1;
-                       STRNCPY(IObuff + len, ptr, tmp_ptr - ptr);
-                       len += (int)(tmp_ptr - ptr);
-                       cont_s_ipos = TRUE;
-                   }
-                   IObuff[len] = NUL;
-                   ptr = IObuff;
-               }
-               if (len == compl_length)
-                   continue;
-           }
-       }
        if (ins_compl_add_infercase(ptr, len, p_ic,
                    ins_buf == curbuf ? NULL : ins_buf->b_sfname,
                    0, cont_s_ipos) != NOTDONE)
--- 3433,3444 ----
                && 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)
***************
*** 3567,3572 ****
--- 3631,3665 ----
  }
  
  /*
+  * Update "compl_shown_match" to the actually shown match, it may differ when
+  * "compl_leader" is used to omit some of the matches.
+  */
+     static void
+ ins_compl_update_shown_match(void)
+ {
+     while (!ins_compl_equal(compl_shown_match,
+               compl_leader, (int)STRLEN(compl_leader))
+           && compl_shown_match->cp_next != NULL
+           && compl_shown_match->cp_next != compl_first_match)
+       compl_shown_match = compl_shown_match->cp_next;
+ 
+     // If we didn't find it searching forward, and compl_shows_dir is
+     // backward, find the last match.
+     if (compl_shows_dir == BACKWARD
+           && !ins_compl_equal(compl_shown_match,
+               compl_leader, (int)STRLEN(compl_leader))
+           && (compl_shown_match->cp_next == NULL
+               || compl_shown_match->cp_next == compl_first_match))
+     {
+       while (!ins_compl_equal(compl_shown_match,
+                   compl_leader, (int)STRLEN(compl_leader))
+               && compl_shown_match->cp_prev != NULL
+               && compl_shown_match->cp_prev != compl_first_match)
+           compl_shown_match = compl_shown_match->cp_prev;
+     }
+ }
+ 
+ /*
   * Delete the old text being completed.
   */
      void
***************
*** 3622,3718 ****
  }
  
  /*
!  * Fill in the next completion in the current direction.
!  * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to
!  * get more completions.  If it is FALSE, then we just do nothing when there
!  * are no more completions in a given direction.  The latter case is used when
!  * we are still in the middle of finding completions, to allow browsing
!  * through the ones found so far.
!  * Return the total number of matches, or -1 if still unknown -- webb.
!  *
!  * compl_curr_match is currently being used by ins_compl_get_exp(), so we use
!  * compl_shown_match here.
!  *
!  * Note that this function may be called recursively once only.  First with
!  * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn
!  * calls this function with "allow_get_expansion" FALSE.
   */
!     static int
! ins_compl_next(
!     int           allow_get_expansion,
!     int           count,              // repeat completion this many times; 
should
!                               // be at least 1
!     int           insert_match,       // Insert the newly selected match
!     int           in_compl_func)      // called from complete_check()
  {
!     int           num_matches = -1;
!     int           todo = count;
!     compl_T *found_compl = NULL;
!     int           found_end = FALSE;
!     int           advance;
!     int           started = compl_started;
  
!     // When user complete function return -1 for findstart which is next
!     // time of 'always', compl_shown_match become NULL.
!     if (compl_shown_match == NULL)
!       return -1;
  
!     if (compl_leader != NULL
!                     && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0)
      {
!       // Set "compl_shown_match" to the actually shown match, it may differ
!       // when "compl_leader" is used to omit some of the matches.
!       while (!ins_compl_equal(compl_shown_match,
!                                     compl_leader, (int)STRLEN(compl_leader))
!               && compl_shown_match->cp_next != NULL
!               && compl_shown_match->cp_next != compl_first_match)
!           compl_shown_match = compl_shown_match->cp_next;
! 
!       // If we didn't find it searching forward, and compl_shows_dir is
!       // backward, find the last match.
!       if (compl_shows_dir == BACKWARD
!               && !ins_compl_equal(compl_shown_match,
!                                     compl_leader, (int)STRLEN(compl_leader))
!               && (compl_shown_match->cp_next == NULL
!                   || compl_shown_match->cp_next == compl_first_match))
!       {
!           while (!ins_compl_equal(compl_shown_match,
!                                     compl_leader, (int)STRLEN(compl_leader))
!                   && compl_shown_match->cp_prev != NULL
!                   && compl_shown_match->cp_prev != compl_first_match)
!               compl_shown_match = compl_shown_match->cp_prev;
        }
      }
  
!     if (allow_get_expansion && insert_match
!           && (!(compl_get_longest || compl_restarting) || compl_used_match))
!       // Delete old text to be replaced
!       ins_compl_delete();
! 
!     // When finding the longest common text we stick at the original text,
!     // don't let CTRL-N or CTRL-P move to the first match.
!     advance = count != 1 || !allow_get_expansion || !compl_get_longest;
! 
!     // When restarting the search don't insert the first match either.
!     if (compl_restarting)
!     {
!       advance = FALSE;
!       compl_restarting = FALSE;
!     }
  
-     // Repeat this for when <PageUp> or <PageDown> is typed.  But don't wrap
-     // around.
      while (--todo >= 0)
      {
        if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL)
        {
            compl_shown_match = compl_shown_match->cp_next;
            found_end = (compl_first_match != NULL
!                          && (compl_shown_match->cp_next == compl_first_match
!                              || compl_shown_match == compl_first_match));
        }
        else if (compl_shows_dir == BACKWARD
!                                       && compl_shown_match->cp_prev != NULL)
        {
            found_end = (compl_shown_match == compl_first_match);
            compl_shown_match = compl_shown_match->cp_prev;
--- 3715,3788 ----
  }
  
  /*
!  * show the file name for the completion match (if any).  Truncate the file
!  * name to avoid a wait for return.
   */
!     static void
! ins_compl_show_filename(void)
  {
!     char      *lead = _("match in file");
!     int               space = sc_col - vim_strsize((char_u *)lead) - 2;
!     char_u    *s;
!     char_u    *e;
  
!     if (space <= 0)
!       return;
  
!     // We need the tail that fits.  With double-byte encoding going
!     // back from the end is very slow, thus go from the start and keep
!     // the text that fits in "space" between "s" and "e".
!     for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e))
      {
!       space -= ptr2cells(e);
!       while (space < 0)
!       {
!           space += ptr2cells(s);
!           MB_PTR_ADV(s);
        }
      }
+     msg_hist_off = TRUE;
+     vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
+           s > compl_shown_match->cp_fname ? "<" : "", s);
+     msg((char *)IObuff);
+     msg_hist_off = FALSE;
+     redraw_cmdline = FALSE;       // don't overwrite!
+ }
  
! /*
!  * Find the next set of matches for completion. Repeat the completion 'todo'
!  * times.  The number of matches found is returned in 'num_matches'.
!  *
!  * If "allow_get_expansion" is TRUE, then ins_compl_get_exp() may be called to
!  * get more completions. If it is FALSE, then do nothing when there are no 
more
!  * completions in the given direction.
!  *
!  * If "advance" is TRUE, then completion will move to the first match.
!  * Otherwise, the original text will be shown.
!  *
!  * Returns OK on success and -1 if the number of matches are unknown.
!  */
!     static int
! find_next_completion_match(
!       int     allow_get_expansion,
!       int     todo,           // repeat completion this many times
!       int     advance,
!       int     *num_matches)
! {
!     int           found_end = FALSE;
!     compl_T *found_compl = NULL;
  
      while (--todo >= 0)
      {
        if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL)
        {
            compl_shown_match = compl_shown_match->cp_next;
            found_end = (compl_first_match != NULL
!                   && (compl_shown_match->cp_next == compl_first_match
!                       || compl_shown_match == compl_first_match));
        }
        else if (compl_shows_dir == BACKWARD
!               && compl_shown_match->cp_prev != NULL)
        {
            found_end = (compl_shown_match == compl_first_match);
            compl_shown_match = compl_shown_match->cp_prev;
***************
*** 3741,3751 ****
            }
  
            // Find matches.
!           num_matches = ins_compl_get_exp(&compl_startpos);
  
            // handle any pending completions
            while (compl_pending != 0 && compl_direction == compl_shows_dir
!                                                                  && advance)
            {
                if (compl_pending > 0 && compl_shown_match->cp_next != NULL)
                {
--- 3811,3821 ----
            }
  
            // Find matches.
!           *num_matches = ins_compl_get_exp(&compl_startpos);
  
            // handle any pending completions
            while (compl_pending != 0 && compl_direction == compl_shows_dir
!                   && advance)
            {
                if (compl_pending > 0 && compl_shown_match->cp_next != NULL)
                {
***************
*** 3765,3771 ****
        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)))
            ++todo;
        else
            // Remember a matching item.
--- 3835,3841 ----
        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)))
            ++todo;
        else
            // Remember a matching item.
***************
*** 3783,3788 ****
--- 3853,3922 ----
        }
      }
  
+     return OK;
+ }
+ 
+ /*
+  * Fill in the next completion in the current direction.
+  * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to
+  * get more completions.  If it is FALSE, then we just do nothing when there
+  * are no more completions in a given direction.  The latter case is used when
+  * we are still in the middle of finding completions, to allow browsing
+  * through the ones found so far.
+  * Return the total number of matches, or -1 if still unknown -- webb.
+  *
+  * compl_curr_match is currently being used by ins_compl_get_exp(), so we use
+  * compl_shown_match here.
+  *
+  * Note that this function may be called recursively once only.  First with
+  * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn
+  * calls this function with "allow_get_expansion" FALSE.
+  */
+     static int
+ ins_compl_next(
+     int           allow_get_expansion,
+     int           count,              // repeat completion this many times; 
should
+                               // be at least 1
+     int           insert_match,       // Insert the newly selected match
+     int           in_compl_func)      // called from complete_check()
+ {
+     int           num_matches = -1;
+     int           todo = count;
+     int           advance;
+     int           started = compl_started;
+ 
+     // When user complete function return -1 for findstart which is next
+     // time of 'always', compl_shown_match become NULL.
+     if (compl_shown_match == NULL)
+       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();
+ 
+     if (allow_get_expansion && insert_match
+           && (!(compl_get_longest || compl_restarting) || compl_used_match))
+       // Delete old text to be replaced
+       ins_compl_delete();
+ 
+     // When finding the longest common text we stick at the original text,
+     // don't let CTRL-N or CTRL-P move to the first match.
+     advance = count != 1 || !allow_get_expansion || !compl_get_longest;
+ 
+     // When restarting the search don't insert the first match either.
+     if (compl_restarting)
+     {
+       advance = FALSE;
+       compl_restarting = FALSE;
+     }
+ 
+     // Repeat this for when <PageUp> or <PageDown> is typed.  But don't wrap
+     // around.
+     if (find_next_completion_match(allow_get_expansion, todo, advance,
+                                                       &num_matches) == -1)
+       return -1;
+ 
      // Insert the text of the new completion, or the compl_leader.
      if (compl_no_insert && !started)
      {
***************
*** 3836,3871 ****
        compl_enter_selects = !insert_match && compl_match_array != NULL;
  
      // Show the file name for the match (if any)
-     // Truncate the file name to avoid a wait for return.
      if (compl_shown_match->cp_fname != NULL)
!     {
!       char    *lead = _("match in file");
!       int     space = sc_col - vim_strsize((char_u *)lead) - 2;
!       char_u  *s;
!       char_u  *e;
! 
!       if (space > 0)
!       {
!           // We need the tail that fits.  With double-byte encoding going
!           // back from the end is very slow, thus go from the start and keep
!           // the text that fits in "space" between "s" and "e".
!           for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e))
!           {
!               space -= ptr2cells(e);
!               while (space < 0)
!               {
!                   space += ptr2cells(s);
!                   MB_PTR_ADV(s);
!               }
!           }
!           msg_hist_off = TRUE;
!           vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
!                               s > compl_shown_match->cp_fname ? "<" : "", s);
!           msg((char *)IObuff);
!           msg_hist_off = FALSE;
!           redraw_cmdline = FALSE;         // don't overwrite!
!       }
!     }
  
      return num_matches;
  }
--- 3970,3977 ----
        compl_enter_selects = !insert_match && compl_match_array != NULL;
  
      // Show the file name for the match (if any)
      if (compl_shown_match->cp_fname != NULL)
!       ins_compl_show_filename();
  
      return num_matches;
  }
***************
*** 4357,4569 ****
  }
  
  /*
!  * Do Insert mode completion.
!  * Called when character "c" was typed, which has a meaning for completion.
!  * Returns OK if completion was done, FAIL if something failed (out of mem).
   */
!     int
! ins_complete(int c, int enable_pum)
  {
      char_u    *line;
      int               startcol = 0;       // column where searched text starts
      colnr_T   curs_col;           // cursor column
!     int               n;
!     int               save_w_wrow;
!     int               save_w_leftcol;
!     int               insert_match;
      int               save_did_ai = did_ai;
      int               flags = CP_ORIGINAL_TEXT;
-     int               line_invalid = FALSE;
  
!     compl_direction = ins_compl_key2dir(c);
!     insert_match = ins_compl_use_match(c);
! 
!     if (!compl_started)
!     {
!       // First time we hit ^N or ^P (in a row, I mean)
  
!       did_ai = FALSE;
  #ifdef FEAT_SMARTINDENT
!       did_si = FALSE;
!       can_si = FALSE;
!       can_si_back = FALSE;
  #endif
!       if (stop_arrow() == FAIL)
!           return FAIL;
  
!       line = ml_get(curwin->w_cursor.lnum);
!       curs_col = curwin->w_cursor.col;
!       compl_pending = 0;
  
!       // If this same ctrl_x_mode has been interrupted use the text from
!       // "compl_startpos" to the cursor as a pattern to add a new word
!       // instead of expand the one before the cursor, in word-wise if
!       // "compl_startpos" is not in the same line as the cursor then fix it
!       // (the line has been split because it was longer than 'tw').  if SOL
!       // is set then skip the previous pattern, a word at the beginning of
!       // the line has been inserted, we'll look for that  -- Acevedo.
!       if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
!                                           && compl_cont_mode == ctrl_x_mode)
!       {
!           // it is a continued search
!           compl_cont_status &= ~CONT_INTRPT;  // remove INTRPT
!           if (ctrl_x_mode == CTRL_X_NORMAL
!                   || ctrl_x_mode == CTRL_X_PATH_PATTERNS
!                   || ctrl_x_mode == CTRL_X_PATH_DEFINES)
!           {
!               if (compl_startpos.lnum != curwin->w_cursor.lnum)
!               {
!                   // line (probably) wrapped, set compl_startpos to the
!                   // first non_blank in the line, if it is not a wordchar
!                   // include it to get a better pattern, but then we don't
!                   // want the "\\<" prefix, check it below
!                   compl_col = (colnr_T)getwhitecols(line);
!                   compl_startpos.col = compl_col;
!                   compl_startpos.lnum = curwin->w_cursor.lnum;
!                   compl_cont_status &= ~CONT_SOL;   // clear SOL if present
!               }
!               else
!               {
!                   // S_IPOS was set when we inserted a word that was at the
!                   // beginning of the line, which means that we'll go to SOL
!                   // mode but first we need to redefine compl_startpos
!                   if (compl_cont_status & CONT_S_IPOS)
!                   {
!                       compl_cont_status |= CONT_SOL;
!                       compl_startpos.col = (colnr_T)(skipwhite(
!                                               line + compl_length
!                                               + compl_startpos.col) - line);
!                   }
!                   compl_col = compl_startpos.col;
!               }
!               compl_length = curwin->w_cursor.col - (int)compl_col;
!               // IObuff is used to add a "word from the next line" would we
!               // have enough space?  just being paranoid
! #define       MIN_SPACE 75
!               if (compl_length > (IOSIZE - MIN_SPACE))
!               {
!                   compl_cont_status &= ~CONT_SOL;
!                   compl_length = (IOSIZE - MIN_SPACE);
!                   compl_col = curwin->w_cursor.col - compl_length;
!               }
!               compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
!               if (compl_length < 1)
!                   compl_cont_status &= CONT_LOCAL;
!           }
!           else if (ctrl_x_mode_line_or_eval())
!               compl_cont_status = CONT_ADDING | CONT_N_ADDS;
!           else
!               compl_cont_status = 0;
!       }
!       else
!           compl_cont_status &= CONT_LOCAL;
  
!       if (!(compl_cont_status & CONT_ADDING)) // normal expansion
!       {
!           compl_cont_mode = ctrl_x_mode;
!           if (ctrl_x_mode != CTRL_X_NORMAL)
!               // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
!               compl_cont_status = 0;
!           compl_cont_status |= CONT_N_ADDS;
!           compl_startpos = curwin->w_cursor;
!           startcol = (int)curs_col;
!           compl_col = 0;
!       }
! 
!       // Work out completion pattern and original text -- webb
!       if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL)
!       {
!           if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
!                                      || thesaurus_func_complete(ctrl_x_mode))
!               // restore did_ai, so that adding comment leader works
!               did_ai = save_did_ai;
!           return FAIL;
!       }
!       // If "line" was changed while getting completion info get it again.
!       if (line_invalid)
!           line = ml_get(curwin->w_cursor.lnum);
  
!       if (compl_cont_status & CONT_ADDING)
        {
!           edit_submode_pre = (char_u *)_(" Adding");
!           if (ctrl_x_mode_line_or_eval())
!           {
!               // Insert a new line, keep indentation but ignore 'comments'.
!               char_u *old = curbuf->b_p_com;
  
!               curbuf->b_p_com = (char_u *)"";
!               compl_startpos.lnum = curwin->w_cursor.lnum;
!               compl_startpos.col = compl_col;
!               ins_eol('\r');
!               curbuf->b_p_com = old;
!               compl_length = 0;
!               compl_col = curwin->w_cursor.col;
!           }
!       }
!       else
!       {
!           edit_submode_pre = NULL;
            compl_startpos.col = compl_col;
        }
- 
-       if (compl_cont_status & CONT_LOCAL)
-           edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
-       else
-           edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
- 
-       // If any of the original typed text has been changed we need to fix
-       // the redo buffer.
-       ins_compl_fixRedoBufForLeader(NULL);
- 
-       // Always add completion for the original text.
-       vim_free(compl_orig_text);
-       compl_orig_text = vim_strnsave(line + compl_col, compl_length);
-       if (p_ic)
-           flags |= CP_ICASE;
-       if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
-                                 -1, NULL, NULL, NULL, 0, flags, FALSE) != OK)
-       {
-           VIM_CLEAR(compl_pattern);
-           VIM_CLEAR(compl_orig_text);
-           return FAIL;
-       }
- 
-       // showmode might reset the internal line pointers, so it must
-       // be called before line = ml_get(), or when this address is no
-       // longer needed.  -- Acevedo.
-       edit_submode_extra = (char_u *)_("-- Searching...");
-       edit_submode_highl = HLF_COUNT;
-       showmode();
-       edit_submode_extra = NULL;
-       out_flush();
      }
!     else if (insert_match && stop_arrow() == FAIL)
!       return FAIL;
! 
!     compl_shown_match = compl_curr_match;
!     compl_shows_dir = compl_direction;
! 
!     // Find next match (and following matches).
!     save_w_wrow = curwin->w_wrow;
!     save_w_leftcol = curwin->w_leftcol;
!     n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE);
! 
!     // may undisplay the popup menu
!     ins_compl_upd_pum();
  
!     if (n > 1)                // all matches have been found
!       compl_matches = n;
!     compl_curr_match = compl_shown_match;
!     compl_direction = compl_shows_dir;
  
!     // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
!     // mode.
!     if (got_int && !global_busy)
      {
!       (void)vgetc();
!       got_int = FALSE;
      }
  
      // we found no match if the list has only the "compl_orig_text"-entry
      if (compl_first_match == compl_first_match->cp_next)
      {
--- 4463,4655 ----
  }
  
  /*
!  * Continue an interrupted completion mode search in "line".
!  *
!  * If this same ctrl_x_mode has been interrupted use the text from
!  * "compl_startpos" to the cursor as a pattern to add a new word instead of
!  * expand the one before the cursor, in word-wise if "compl_startpos" is not 
in
!  * the same line as the cursor then fix it (the line has been split because it
!  * was longer than 'tw').  if SOL is set then skip the previous pattern, a 
word
!  * at the beginning of the line has been inserted, we'll look for that.
   */
!     static void
! ins_compl_continue_search(char_u *line)
! {
!     // it is a continued search
!     compl_cont_status &= ~CONT_INTRPT;        // remove INTRPT
!     if (ctrl_x_mode == CTRL_X_NORMAL
!           || ctrl_x_mode == CTRL_X_PATH_PATTERNS
!           || ctrl_x_mode == CTRL_X_PATH_DEFINES)
!     {
!       if (compl_startpos.lnum != curwin->w_cursor.lnum)
!       {
!           // line (probably) wrapped, set compl_startpos to the
!           // first non_blank in the line, if it is not a wordchar
!           // include it to get a better pattern, but then we don't
!           // want the "\\<" prefix, check it below
!           compl_col = (colnr_T)getwhitecols(line);
!           compl_startpos.col = compl_col;
!           compl_startpos.lnum = curwin->w_cursor.lnum;
!           compl_cont_status &= ~CONT_SOL;   // clear SOL if present
!       }
!       else
!       {
!           // S_IPOS was set when we inserted a word that was at the
!           // beginning of the line, which means that we'll go to SOL
!           // mode but first we need to redefine compl_startpos
!           if (compl_cont_status & CONT_S_IPOS)
!           {
!               compl_cont_status |= CONT_SOL;
!               compl_startpos.col = (colnr_T)(skipwhite(
!                           line + compl_length
!                           + compl_startpos.col) - line);
!           }
!           compl_col = compl_startpos.col;
!       }
!       compl_length = curwin->w_cursor.col - (int)compl_col;
!       // IObuff is used to add a "word from the next line" would we
!       // have enough space?  just being paranoid
! #define       MIN_SPACE 75
!       if (compl_length > (IOSIZE - MIN_SPACE))
!       {
!           compl_cont_status &= ~CONT_SOL;
!           compl_length = (IOSIZE - MIN_SPACE);
!           compl_col = curwin->w_cursor.col - compl_length;
!       }
!       compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
!       if (compl_length < 1)
!           compl_cont_status &= CONT_LOCAL;
!     }
!     else if (ctrl_x_mode_line_or_eval())
!       compl_cont_status = CONT_ADDING | CONT_N_ADDS;
!     else
!       compl_cont_status = 0;
! }
! 
! /*
!  * start insert mode completion
!  */
!     static int
! ins_compl_start(void)
  {
      char_u    *line;
      int               startcol = 0;       // column where searched text starts
      colnr_T   curs_col;           // cursor column
!     int               line_invalid = FALSE;
      int               save_did_ai = did_ai;
      int               flags = CP_ORIGINAL_TEXT;
  
!     // First time we hit ^N or ^P (in a row, I mean)
  
!     did_ai = FALSE;
  #ifdef FEAT_SMARTINDENT
!     did_si = FALSE;
!     can_si = FALSE;
!     can_si_back = FALSE;
  #endif
!     if (stop_arrow() == FAIL)
!       return FAIL;
  
!     line = ml_get(curwin->w_cursor.lnum);
!     curs_col = curwin->w_cursor.col;
!     compl_pending = 0;
  
!     if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
!           && compl_cont_mode == ctrl_x_mode)
!       // this same ctrl-x_mode was interrupted previously. Continue the
!       // completion.
!       ins_compl_continue_search(line);
!     else
!       compl_cont_status &= CONT_LOCAL;
  
!     if (!(compl_cont_status & CONT_ADDING))   // normal expansion
!     {
!       compl_cont_mode = ctrl_x_mode;
!       if (ctrl_x_mode != CTRL_X_NORMAL)
!           // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
!           compl_cont_status = 0;
!       compl_cont_status |= CONT_N_ADDS;
!       compl_startpos = curwin->w_cursor;
!       startcol = (int)curs_col;
!       compl_col = 0;
!     }
! 
!     // Work out completion pattern and original text -- webb
!     if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL)
!     {
!       if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI
!               || thesaurus_func_complete(ctrl_x_mode))
!           // restore did_ai, so that adding comment leader works
!           did_ai = save_did_ai;
!       return FAIL;
!     }
!     // If "line" was changed while getting completion info get it again.
!     if (line_invalid)
!       line = ml_get(curwin->w_cursor.lnum);
  
!     if (compl_cont_status & CONT_ADDING)
!     {
!       edit_submode_pre = (char_u *)_(" Adding");
!       if (ctrl_x_mode_line_or_eval())
        {
!           // Insert a new line, keep indentation but ignore 'comments'.
!           char_u *old = curbuf->b_p_com;
  
!           curbuf->b_p_com = (char_u *)"";
!           compl_startpos.lnum = curwin->w_cursor.lnum;
            compl_startpos.col = compl_col;
+           ins_eol('\r');
+           curbuf->b_p_com = old;
+           compl_length = 0;
+           compl_col = curwin->w_cursor.col;
        }
      }
!     else
!     {
!       edit_submode_pre = NULL;
!       compl_startpos.col = compl_col;
!     }
  
!     if (compl_cont_status & CONT_LOCAL)
!       edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
!     else
!       edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
  
!     // If any of the original typed text has been changed we need to fix
!     // the redo buffer.
!     ins_compl_fixRedoBufForLeader(NULL);
! 
!     // Always add completion for the original text.
!     vim_free(compl_orig_text);
!     compl_orig_text = vim_strnsave(line + compl_col, compl_length);
!     if (p_ic)
!       flags |= CP_ICASE;
!     if (compl_orig_text == NULL || ins_compl_add(compl_orig_text,
!               -1, NULL, NULL, NULL, 0, flags, FALSE) != OK)
      {
!       VIM_CLEAR(compl_pattern);
!       VIM_CLEAR(compl_orig_text);
!       return FAIL;
      }
  
+     // showmode might reset the internal line pointers, so it must
+     // be called before line = ml_get(), or when this address is no
+     // longer needed.  -- Acevedo.
+     edit_submode_extra = (char_u *)_("-- Searching...");
+     edit_submode_highl = HLF_COUNT;
+     showmode();
+     edit_submode_extra = NULL;
+     out_flush();
+ 
+     return OK;
+ }
+ 
+ /*
+  * display the completion status message
+  */
+     static void
+ ins_compl_show_statusmsg(void)
+ {
      // we found no match if the list has only the "compl_orig_text"-entry
      if (compl_first_match == compl_first_match->cp_next)
      {
***************
*** 4571,4593 ****
                        && compl_length > 1
                             ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf);
        edit_submode_highl = HLF_E;
-       // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
-       // because we couldn't expand anything at first place, but if we used
-       // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
-       // (such as M in M'exico) if not tried already.  -- Acevedo
-       if (       compl_length > 1
-               || (compl_cont_status & CONT_ADDING)
-               || (ctrl_x_mode != CTRL_X_NORMAL
-                   && ctrl_x_mode != CTRL_X_PATH_PATTERNS
-                   && ctrl_x_mode != CTRL_X_PATH_DEFINES))
-           compl_cont_status &= ~CONT_N_ADDS;
      }
  
-     if (compl_curr_match->cp_flags & CP_CONT_S_IPOS)
-       compl_cont_status |= CONT_S_IPOS;
-     else
-       compl_cont_status &= ~CONT_S_IPOS;
- 
      if (edit_submode_extra == NULL)
      {
        if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT)
--- 4657,4664 ----
***************
*** 4623,4634 ****
  
                if (compl_matches > 0)
                    vim_snprintf((char *)match_ref, sizeof(match_ref),
!                               _("match %d of %d"),
!                               compl_curr_match->cp_number, compl_matches);
                else
                    vim_snprintf((char *)match_ref, sizeof(match_ref),
!                               _("match %d"),
!                               compl_curr_match->cp_number);
                edit_submode_extra = match_ref;
                edit_submode_highl = HLF_R;
                if (dollar_vcol >= 0)
--- 4694,4705 ----
  
                if (compl_matches > 0)
                    vim_snprintf((char *)match_ref, sizeof(match_ref),
!                           _("match %d of %d"),
!                           compl_curr_match->cp_number, compl_matches);
                else
                    vim_snprintf((char *)match_ref, sizeof(match_ref),
!                           _("match %d"),
!                           compl_curr_match->cp_number);
                edit_submode_extra = match_ref;
                edit_submode_highl = HLF_R;
                if (dollar_vcol >= 0)
***************
*** 4658,4663 ****
--- 4729,4805 ----
                msg_clr_cmdline();      // necessary for "noshowmode"
        }
      }
+ }
+ 
+ /*
+  * Do Insert mode completion.
+  * Called when character "c" was typed, which has a meaning for completion.
+  * Returns OK if completion was done, FAIL if something failed (out of mem).
+  */
+     int
+ ins_complete(int c, int enable_pum)
+ {
+     int               n;
+     int               save_w_wrow;
+     int               save_w_leftcol;
+     int               insert_match;
+ 
+     compl_direction = ins_compl_key2dir(c);
+     insert_match = ins_compl_use_match(c);
+ 
+     if (!compl_started)
+     {
+       if (ins_compl_start() == FAIL)
+           return FAIL;
+     }
+     else if (insert_match && stop_arrow() == FAIL)
+       return FAIL;
+ 
+     compl_shown_match = compl_curr_match;
+     compl_shows_dir = compl_direction;
+ 
+     // Find next match (and following matches).
+     save_w_wrow = curwin->w_wrow;
+     save_w_leftcol = curwin->w_leftcol;
+     n = ins_compl_next(TRUE, ins_compl_key2count(c), insert_match, FALSE);
+ 
+     // may undisplay the popup menu
+     ins_compl_upd_pum();
+ 
+     if (n > 1)                // all matches have been found
+       compl_matches = n;
+     compl_curr_match = compl_shown_match;
+     compl_direction = compl_shows_dir;
+ 
+     // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
+     // mode.
+     if (got_int && !global_busy)
+     {
+       (void)vgetc();
+       got_int = FALSE;
+     }
+ 
+     // we found no match if the list has only the "compl_orig_text"-entry
+     if (compl_first_match == compl_first_match->cp_next)
+     {
+       // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
+       // because we couldn't expand anything at first place, but if we used
+       // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
+       // (such as M in M'exico) if not tried already.  -- Acevedo
+       if (compl_length > 1
+               || (compl_cont_status & CONT_ADDING)
+               || (ctrl_x_mode != CTRL_X_NORMAL
+                   && ctrl_x_mode != CTRL_X_PATH_PATTERNS
+                   && ctrl_x_mode != CTRL_X_PATH_DEFINES))
+           compl_cont_status &= ~CONT_N_ADDS;
+     }
+ 
+     if (compl_curr_match->cp_flags & CP_CONT_S_IPOS)
+       compl_cont_status |= CONT_S_IPOS;
+     else
+       compl_cont_status &= ~CONT_S_IPOS;
+ 
+     ins_compl_show_statusmsg();
  
      // Show the popup menu, unless we got interrupted.
      if (enable_pum && !compl_interrupted)
*** ../vim-8.2.3943/src/testdir/test_ins_complete.vim   2021-12-29 
17:38:42.301517624 +0000
--- src/testdir/test_ins_complete.vim   2021-12-30 11:37:46.568928853 +0000
***************
*** 900,905 ****
--- 900,924 ----
    close!
  endfunc
  
+ " Test for typing CTRL-R in insert completion mode to insert a register
+ " content.
+ func Test_complete_reginsert()
+   new
+   call setline(1, ['a1', 'a12', 'a123', 'a1234'])
+ 
+   " if a valid CTRL-X mode key is returned from <C-R>=, then it should be
+   " processed. Otherwise, CTRL-X mode should be stopped and the key should be
+   " inserted.
+   exe "normal Goa\<C-P>\<C-R>=\"\\<C-P>\"\<CR>"
+   call assert_equal('a123', getline(5))
+   let @r = "\<C-P>\<C-P>"
+   exe "normal GCa\<C-P>\<C-R>r"
+   call assert_equal('a12', getline(5))
+   exe "normal GCa\<C-P>\<C-R>=\"x\"\<CR>"
+   call assert_equal('a1234x', getline(5))
+   bw!
+ endfunc
+ 
  func Test_issue_7021()
    CheckMSWindows
  
*** ../vim-8.2.3943/src/version.c       2021-12-30 10:51:41.168860630 +0000
--- src/version.c       2021-12-30 11:39:10.808802978 +0000
***************
*** 751,752 ****
--- 751,754 ----
  {   /* Add new patch number below this line */
+ /**/
+     3944,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
150. You find yourself counting emoticons to get to sleep.

 /// 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/20211230114129.E078B1C0A5B%40moolenaar.net.

Raspunde prin e-mail lui