changeset 26831:537fdb6653a9 v8.2.3944

patch 8.2.3944: insert mode completion functions are too long Commit: https://github.com/vim/vim/commit/5d2e007ccbfbd749a1f201d06965b8811ff50e6e Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Thu Dec 30 11:40:53 2021 +0000 patch 8.2.3944: insert mode completion functions are too long Problem: Insert mode completion functions are too long. Solution: Split up into multiple functions. (Yegappan Lakshmanan, closes #9431)
author Bram Moolenaar <Bram@vim.org>
date Thu, 30 Dec 2021 12:45:03 +0100
parents 13ecf040b431
children eb188d6e2e41
files src/insexpand.c src/testdir/test_ins_complete.vim src/version.c
diffstat 3 files changed, 773 insertions(+), 610 deletions(-) [+]
line wrap: on
line diff
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -1823,6 +1823,257 @@ ins_compl_addfrommatch(void)
 }
 
 /*
+ * 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,10 +2081,6 @@ ins_compl_addfrommatch(void)
     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;
 
@@ -1910,113 +2157,9 @@ ins_compl_prep(int c)
     }
 
     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;
-	}
-    }
+	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,112 +2183,7 @@ ins_compl_prep(int c)
 	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);
-	}
+	    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,6 +3237,102 @@ get_next_spell_completion(linenr_T lnum 
 }
 
 /*
+ * 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,82 +3433,12 @@ get_next_default_completion(
 		&& 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;
-	    }
-	}
+
+	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,6 +3631,35 @@ ins_compl_get_exp(pos_T *ini)
 }
 
 /*
+ * 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,97 +3715,74 @@ ins_compl_insert(int in_compl_func)
 }
 
 /*
- * 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.
+ * 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'.
  *
- * compl_curr_match is currently being used by ins_compl_get_exp(), so we use
- * compl_shown_match here.
+ * 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.
  *
- * 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.
+ * 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
-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()
+find_next_completion_match(
+	int	allow_get_expansion,
+	int	todo,		// repeat completion this many times
+	int	advance,
+	int	*num_matches)
 {
-    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.
+    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));
+		    && (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)
+		&& compl_shown_match->cp_prev != NULL)
 	{
 	    found_end = (compl_shown_match == compl_first_match);
 	    compl_shown_match = compl_shown_match->cp_prev;
@@ -3741,11 +3811,11 @@ ins_compl_next(
 	    }
 
 	    // Find matches.
-	    num_matches = ins_compl_get_exp(&compl_startpos);
+	    *num_matches = ins_compl_get_exp(&compl_startpos);
 
 	    // handle any pending completions
 	    while (compl_pending != 0 && compl_direction == compl_shows_dir
-								   && advance)
+		    && advance)
 	    {
 		if (compl_pending > 0 && compl_shown_match->cp_next != NULL)
 		{
@@ -3765,7 +3835,7 @@ ins_compl_next(
 	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)))
+		    compl_leader, (int)STRLEN(compl_leader)))
 	    ++todo;
 	else
 	    // Remember a matching item.
@@ -3783,6 +3853,70 @@ ins_compl_next(
 	}
     }
 
+    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,36 +3970,8 @@ ins_compl_next(
 	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!
-	}
-    }
+	ins_compl_show_filename();
 
     return num_matches;
 }
@@ -4357,213 +4463,193 @@ compl_get_info(char_u *line, int startco
 }
 
 /*
- * 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).
+ * 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.
  */
-    int
-ins_complete(int c, int enable_pum)
+    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		n;
-    int		save_w_wrow;
-    int		save_w_leftcol;
-    int		insert_match;
+    int		line_invalid = FALSE;
     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;
+
+    // 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;
+    did_si = FALSE;
+    can_si = FALSE;
+    can_si_back = FALSE;
 #endif
-	if (stop_arrow() == FAIL)
-	    return FAIL;
-
+    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);
-	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)
+
+    if (compl_cont_status & CONT_ADDING)
+    {
+	edit_submode_pre = (char_u *)_(" Adding");
+	if (ctrl_x_mode_line_or_eval())
 	{
-	    // 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;
+	    // 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;
 	}
-
-	// 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
+    {
+	edit_submode_pre = NULL;
+	compl_startpos.col = compl_col;
     }
-    else if (insert_match && stop_arrow() == FAIL)
+
+    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;
-
-    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;
     }
 
+    // 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,23 +4657,8 @@ ins_complete(int c, int enable_pum)
 			&& 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)
@@ -4623,12 +4694,12 @@ ins_complete(int c, int enable_pum)
 
 		if (compl_matches > 0)
 		    vim_snprintf((char *)match_ref, sizeof(match_ref),
-				_("match %d of %d"),
-				compl_curr_match->cp_number, compl_matches);
+			    _("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);
+			    _("match %d"),
+			    compl_curr_match->cp_number);
 		edit_submode_extra = match_ref;
 		edit_submode_highl = HLF_R;
 		if (dollar_vcol >= 0)
@@ -4658,6 +4729,77 @@ ins_complete(int c, int enable_pum)
 		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)
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -900,6 +900,25 @@ func Test_complete_stop()
   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
 
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3944,
+/**/
     3943,
 /**/
     3942,