changeset 25707:31db9c6df4e3 v8.2.3389

patch 8.2.3389: cannot stop insert mode completion without side effects Commit: https://github.com/vim/vim/commit/dca29d9cf46cd1d4d4519211c7af78b6b1c56960 Author: zeertzjq <zeertzjq@outlook.com> Date: Tue Aug 31 19:12:51 2021 +0200 patch 8.2.3389: cannot stop insert mode completion without side effects Problem: Cannot stop insert mode completion without side effects. Solution: Add CTRL-X CTRL-Z. (closes https://github.com/vim/vim/issues/8821)
author Bram Moolenaar <Bram@vim.org>
date Tue, 31 Aug 2021 19:15:05 +0200
parents db2130b7a83f
children a1f90f486bf7
files runtime/doc/index.txt runtime/doc/insert.txt src/insexpand.c src/testdir/test_ins_complete.vim src/version.c
diffstat 5 files changed, 119 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -168,6 +168,7 @@ commands in CTRL-X submode				*i_CTRL-X_
 |i_CTRL-X_CTRL-Y|	CTRL-X CTRL-Y	scroll down
 |i_CTRL-X_CTRL-U|	CTRL-X CTRL-U	complete with 'completefunc'
 |i_CTRL-X_CTRL-V|	CTRL-X CTRL-V	complete like in : command line
+|i_CTRL-X_CTRL-Z|	CTRL-X CTRL-Z	stop completion, keeping the text as-is
 |i_CTRL-X_CTRL-]|	CTRL-X CTRL-]	complete tags
 |i_CTRL-X_s|		CTRL-X s	spelling suggestions
 
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -640,6 +640,8 @@ 11. omni completion					|i_CTRL-X_CTRL-O
 12. Spelling suggestions				|i_CTRL-X_s|
 13. keywords in 'complete'				|i_CTRL-N| |i_CTRL-P|
 
+Additionally, |i_CTRL-X_CTRL-Z| stops completion without changing the text.
+
 All these, except CTRL-N and CTRL-P, are done in CTRL-X mode.  This is a
 sub-mode of Insert and Replace modes.  You enter CTRL-X mode by typing CTRL-X
 and one of the CTRL-X commands.  You exit CTRL-X mode by typing a key that is
@@ -1042,6 +1044,12 @@ CTRL-P			Find previous match for words t
 			other contexts unless a double CTRL-X is used.
 
 
+Stop completion						*compl-stop*
+
+							*i_CTRL-X_CTRL-Z*
+CTRL-X CTRL-Z		Stop completion without changing the text.
+
+
 FUNCTIONS FOR FINDING COMPLETIONS			*complete-functions*
 
 This applies to 'completefunc' and 'omnifunc'.
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -37,6 +37,7 @@
 # define CTRL_X_SPELL		14
 # define CTRL_X_LOCAL_MSG	15	// only used in "ctrl_x_msgs"
 # define CTRL_X_EVAL		16	// for builtin function complete()
+# define CTRL_X_CMDLINE_CTRL_X	17	// CTRL-X typed in CTRL_X_CMDLINE
 
 # define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
 
@@ -60,6 +61,7 @@ static char *ctrl_x_msgs[] =
     N_(" Spelling suggestion (s^N^P)"),
     N_(" Keyword Local completion (^N^P)"),
     NULL,   // CTRL_X_EVAL doesn't use msg.
+    N_(" Command-line completion (^V^N^P)"),
 };
 
 #if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
@@ -80,7 +82,8 @@ static char *ctrl_x_mode_names[] = {
 	"omni",
 	"spell",
 	NULL,		    // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
-	"eval"
+	"eval",
+	"cmdline",
 };
 #endif
 
@@ -222,9 +225,7 @@ static int  spell_bad_len = 0;	// length
     void
 ins_ctrl_x(void)
 {
-    // CTRL-X after CTRL-X CTRL-V doesn't do anything, so that CTRL-X
-    // CTRL-V works like CTRL-N
-    if (ctrl_x_mode != CTRL_X_CMDLINE)
+    if (!ctrl_x_mode_cmdline())
     {
 	// if the next ^X<> won't ADD nothing, then reset
 	// compl_cont_status
@@ -238,6 +239,10 @@ ins_ctrl_x(void)
 	edit_submode_pre = NULL;
 	showmode();
     }
+    else
+	// CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X
+	// CTRL-V look like CTRL-N
+	ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
 }
 
 /*
@@ -255,7 +260,9 @@ int ctrl_x_mode_path_defines(void) {
 				   return ctrl_x_mode == CTRL_X_PATH_DEFINES; }
 int ctrl_x_mode_dictionary(void) { return ctrl_x_mode == CTRL_X_DICTIONARY; }
 int ctrl_x_mode_thesaurus(void) { return ctrl_x_mode == CTRL_X_THESAURUS; }
-int ctrl_x_mode_cmdline(void) { return ctrl_x_mode == CTRL_X_CMDLINE; }
+int ctrl_x_mode_cmdline(void) {
+	return ctrl_x_mode == CTRL_X_CMDLINE
+		|| ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X; }
 int ctrl_x_mode_function(void) { return ctrl_x_mode == CTRL_X_FUNCTION; }
 int ctrl_x_mode_omni(void) { return ctrl_x_mode == CTRL_X_OMNI; }
 int ctrl_x_mode_spell(void) { return ctrl_x_mode == CTRL_X_SPELL; }
@@ -272,7 +279,8 @@ ctrl_x_mode_not_default(void)
 }
 
 /*
- * Whether CTRL-X was typed without a following character.
+ * Whether CTRL-X was typed without a following character,
+ * not including when in CTRL-X CTRL-V mode.
  */
     int
 ctrl_x_mode_not_defined_yet(void)
@@ -333,12 +341,14 @@ vim_is_ctrl_x_key(int c)
 	case 0:		    // Not in any CTRL-X mode
 	    return (c == Ctrl_N || c == Ctrl_P || c == Ctrl_X);
 	case CTRL_X_NOT_DEFINED_YET:
+	case CTRL_X_CMDLINE_CTRL_X:
 	    return (   c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E
 		    || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB
 		    || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P
 		    || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V
 		    || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O
-		    || c == Ctrl_S || c == Ctrl_K || c == 's');
+		    || c == Ctrl_S || c == Ctrl_K || c == 's'
+		    || c == Ctrl_Z);
 	case CTRL_X_SCROLL:
 	    return (c == Ctrl_Y || c == Ctrl_E);
 	case CTRL_X_WHOLE_LINE:
@@ -396,6 +406,7 @@ ins_compl_accept_char(int c)
 	    return vim_isfilec(c) && !vim_ispathsep(c);
 
 	case CTRL_X_CMDLINE:
+	case CTRL_X_CMDLINE_CTRL_X:
 	case CTRL_X_OMNI:
 	    // Command line and Omni completion can work with just about any
 	    // printable character, but do stop at white space.
@@ -1860,6 +1871,29 @@ ins_compl_prep(int c)
     }
 #endif
 
+    if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X)
+    {
+	if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c)
+		|| !vim_is_ctrl_x_key(c))
+	{
+	    // Not starting another completion mode.
+	    ctrl_x_mode = CTRL_X_CMDLINE;
+
+	    // CTRL-X CTRL-Z should stop completion without inserting anything
+	    if (c == Ctrl_Z)
+		retval = TRUE;
+	}
+	else
+	{
+	    ctrl_x_mode = CTRL_X_CMDLINE;
+
+	    // Other CTRL-X keys first stop completion, then start another
+	    // completion mode.
+	    ins_compl_prep(' ');
+	    ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
+	}
+    }
+
     // Set "compl_get_longest" when finding the first matches.
     if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET
 			   || (ctrl_x_mode == CTRL_X_NORMAL && !compl_started))
@@ -1933,6 +1967,12 @@ ins_compl_prep(int c)
 	    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
@@ -2929,6 +2969,7 @@ ins_compl_get_exp(pos_T *ini)
 	    break;
 
 	case CTRL_X_CMDLINE:
+	case CTRL_X_CMDLINE_CTRL_X:
 	    if (expand_cmdline(&compl_xp, compl_pattern,
 			(int)STRLEN(compl_pattern),
 					 &num_matches, &matches) == EXPAND_OK)
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -730,6 +730,66 @@ func Test_complete_cmdline()
   call assert_equal('call getqflist(', getline(2))
   exe "normal oabcxyz(\<C-X>\<C-V>"
   call assert_equal('abcxyz(', getline(3))
+  com! -buffer TestCommand1 echo 'TestCommand1'
+  com! -buffer TestCommand2 echo 'TestCommand2'
+  write TestCommand1Test
+  write TestCommand2Test
+  " Test repeating <CTRL-X> <CTRL-V> and switching to another CTRL-X mode
+  exe "normal oT\<C-X>\<C-V>\<C-X>\<C-V>\<C-X>\<C-F>\<Esc>"
+  call assert_equal('TestCommand2Test', getline(4))
+  call delete('TestCommand1Test')
+  call delete('TestCommand2Test')
+  delcom TestCommand1
+  delcom TestCommand2
+  close!
+endfunc
+
+" Test for <CTRL-X> <CTRL-Z> stopping completion without changing the match
+func Test_complete_stop()
+  new
+  func Save_mode1()
+    let g:mode1 = mode(1)
+    return ''
+  endfunc
+  func Save_mode2()
+    let g:mode2 = mode(1)
+    return ''
+  endfunc
+  inoremap <F1> <C-R>=Save_mode1()<CR>
+  inoremap <F2> <C-R>=Save_mode2()<CR>
+  call setline(1, ['aaa bbb ccc '])
+  exe "normal A\<C-N>\<C-P>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
+  call assert_equal('ic', g:mode1)
+  call assert_equal('i', g:mode2)
+  call assert_equal('aaa bbb ccc ', getline(1))
+  exe "normal A\<C-N>\<Down>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
+  call assert_equal('ic', g:mode1)
+  call assert_equal('i', g:mode2)
+  call assert_equal('aaa bbb ccc aaa', getline(1))
+  set completeopt+=noselect
+  exe "normal A \<C-N>\<Down>\<Down>\<C-L>\<C-L>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
+  call assert_equal('ic', g:mode1)
+  call assert_equal('i', g:mode2)
+  call assert_equal('aaa bbb ccc aaa bb', getline(1))
+  set completeopt&
+  exe "normal A d\<C-N>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
+  call assert_equal('ic', g:mode1)
+  call assert_equal('i', g:mode2)
+  call assert_equal('aaa bbb ccc aaa bb d', getline(1))
+  com! -buffer TestCommand1 echo 'TestCommand1'
+  com! -buffer TestCommand2 echo 'TestCommand2'
+  exe "normal oT\<C-X>\<C-V>\<C-X>\<C-V>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
+  call assert_equal('ic', g:mode1)
+  call assert_equal('i', g:mode2)
+  call assert_equal('TestCommand2', getline(2))
+  delcom TestCommand1
+  delcom TestCommand2
+  unlet g:mode1
+  unlet g:mode2
+  iunmap <F1>
+  iunmap <F2>
+  delfunc Save_mode1
+  delfunc Save_mode2
   close!
 endfunc
 
--- a/src/version.c
+++ b/src/version.c
@@ -756,6 +756,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3389,
+/**/
     3388,
 /**/
     3387,