Skip to content

Commit dca29d9

Browse files
zeertzjqbrammool
authored andcommitted
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 #8821)
1 parent 4eaef99 commit dca29d9

5 files changed

Lines changed: 119 additions & 7 deletions

File tree

runtime/doc/index.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ commands in CTRL-X submode *i_CTRL-X_index*
168168
|i_CTRL-X_CTRL-Y| CTRL-X CTRL-Y scroll down
169169
|i_CTRL-X_CTRL-U| CTRL-X CTRL-U complete with 'completefunc'
170170
|i_CTRL-X_CTRL-V| CTRL-X CTRL-V complete like in : command line
171+
|i_CTRL-X_CTRL-Z| CTRL-X CTRL-Z stop completion, keeping the text as-is
171172
|i_CTRL-X_CTRL-]| CTRL-X CTRL-] complete tags
172173
|i_CTRL-X_s| CTRL-X s spelling suggestions
173174

runtime/doc/insert.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,8 @@ Completion can be done for:
640640
12. Spelling suggestions |i_CTRL-X_s|
641641
13. keywords in 'complete' |i_CTRL-N| |i_CTRL-P|
642642

643+
Additionally, |i_CTRL-X_CTRL-Z| stops completion without changing the text.
644+
643645
All these, except CTRL-N and CTRL-P, are done in CTRL-X mode. This is a
644646
sub-mode of Insert and Replace modes. You enter CTRL-X mode by typing CTRL-X
645647
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 that start with the
10421044
other contexts unless a double CTRL-X is used.
10431045

10441046

1047+
Stop completion *compl-stop*
1048+
1049+
*i_CTRL-X_CTRL-Z*
1050+
CTRL-X CTRL-Z Stop completion without changing the text.
1051+
1052+
10451053
FUNCTIONS FOR FINDING COMPLETIONS *complete-functions*
10461054

10471055
This applies to 'completefunc' and 'omnifunc'.

src/insexpand.c

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
# define CTRL_X_SPELL 14
3838
# define CTRL_X_LOCAL_MSG 15 // only used in "ctrl_x_msgs"
3939
# define CTRL_X_EVAL 16 // for builtin function complete()
40+
# define CTRL_X_CMDLINE_CTRL_X 17 // CTRL-X typed in CTRL_X_CMDLINE
4041

4142
# define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT]
4243

@@ -60,6 +61,7 @@ static char *ctrl_x_msgs[] =
6061
N_(" Spelling suggestion (s^N^P)"),
6162
N_(" Keyword Local completion (^N^P)"),
6263
NULL, // CTRL_X_EVAL doesn't use msg.
64+
N_(" Command-line completion (^V^N^P)"),
6365
};
6466

6567
#if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
@@ -80,7 +82,8 @@ static char *ctrl_x_mode_names[] = {
8082
"omni",
8183
"spell",
8284
NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs"
83-
"eval"
85+
"eval",
86+
"cmdline",
8487
};
8588
#endif
8689

@@ -222,9 +225,7 @@ static int spell_bad_len = 0; // length of located bad word
222225
void
223226
ins_ctrl_x(void)
224227
{
225-
// CTRL-X after CTRL-X CTRL-V doesn't do anything, so that CTRL-X
226-
// CTRL-V works like CTRL-N
227-
if (ctrl_x_mode != CTRL_X_CMDLINE)
228+
if (!ctrl_x_mode_cmdline())
228229
{
229230
// if the next ^X<> won't ADD nothing, then reset
230231
// compl_cont_status
@@ -238,6 +239,10 @@ ins_ctrl_x(void)
238239
edit_submode_pre = NULL;
239240
showmode();
240241
}
242+
else
243+
// CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X
244+
// CTRL-V look like CTRL-N
245+
ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
241246
}
242247

243248
/*
@@ -255,7 +260,9 @@ int ctrl_x_mode_path_defines(void) {
255260
return ctrl_x_mode == CTRL_X_PATH_DEFINES; }
256261
int ctrl_x_mode_dictionary(void) { return ctrl_x_mode == CTRL_X_DICTIONARY; }
257262
int ctrl_x_mode_thesaurus(void) { return ctrl_x_mode == CTRL_X_THESAURUS; }
258-
int ctrl_x_mode_cmdline(void) { return ctrl_x_mode == CTRL_X_CMDLINE; }
263+
int ctrl_x_mode_cmdline(void) {
264+
return ctrl_x_mode == CTRL_X_CMDLINE
265+
|| ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X; }
259266
int ctrl_x_mode_function(void) { return ctrl_x_mode == CTRL_X_FUNCTION; }
260267
int ctrl_x_mode_omni(void) { return ctrl_x_mode == CTRL_X_OMNI; }
261268
int ctrl_x_mode_spell(void) { return ctrl_x_mode == CTRL_X_SPELL; }
@@ -272,7 +279,8 @@ ctrl_x_mode_not_default(void)
272279
}
273280

274281
/*
275-
* Whether CTRL-X was typed without a following character.
282+
* Whether CTRL-X was typed without a following character,
283+
* not including when in CTRL-X CTRL-V mode.
276284
*/
277285
int
278286
ctrl_x_mode_not_defined_yet(void)
@@ -333,12 +341,14 @@ vim_is_ctrl_x_key(int c)
333341
case 0: // Not in any CTRL-X mode
334342
return (c == Ctrl_N || c == Ctrl_P || c == Ctrl_X);
335343
case CTRL_X_NOT_DEFINED_YET:
344+
case CTRL_X_CMDLINE_CTRL_X:
336345
return ( c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E
337346
|| c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB
338347
|| c == Ctrl_I || c == Ctrl_D || c == Ctrl_P
339348
|| c == Ctrl_N || c == Ctrl_T || c == Ctrl_V
340349
|| c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O
341-
|| c == Ctrl_S || c == Ctrl_K || c == 's');
350+
|| c == Ctrl_S || c == Ctrl_K || c == 's'
351+
|| c == Ctrl_Z);
342352
case CTRL_X_SCROLL:
343353
return (c == Ctrl_Y || c == Ctrl_E);
344354
case CTRL_X_WHOLE_LINE:
@@ -396,6 +406,7 @@ ins_compl_accept_char(int c)
396406
return vim_isfilec(c) && !vim_ispathsep(c);
397407

398408
case CTRL_X_CMDLINE:
409+
case CTRL_X_CMDLINE_CTRL_X:
399410
case CTRL_X_OMNI:
400411
// Command line and Omni completion can work with just about any
401412
// printable character, but do stop at white space.
@@ -1860,6 +1871,29 @@ ins_compl_prep(int c)
18601871
}
18611872
#endif
18621873

1874+
if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X)
1875+
{
1876+
if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c)
1877+
|| !vim_is_ctrl_x_key(c))
1878+
{
1879+
// Not starting another completion mode.
1880+
ctrl_x_mode = CTRL_X_CMDLINE;
1881+
1882+
// CTRL-X CTRL-Z should stop completion without inserting anything
1883+
if (c == Ctrl_Z)
1884+
retval = TRUE;
1885+
}
1886+
else
1887+
{
1888+
ctrl_x_mode = CTRL_X_CMDLINE;
1889+
1890+
// Other CTRL-X keys first stop completion, then start another
1891+
// completion mode.
1892+
ins_compl_prep(' ');
1893+
ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
1894+
}
1895+
}
1896+
18631897
// Set "compl_get_longest" when finding the first matches.
18641898
if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET
18651899
|| (ctrl_x_mode == CTRL_X_NORMAL && !compl_started))
@@ -1933,6 +1967,12 @@ ins_compl_prep(int c)
19331967
case Ctrl_Q:
19341968
ctrl_x_mode = CTRL_X_CMDLINE;
19351969
break;
1970+
case Ctrl_Z:
1971+
ctrl_x_mode = CTRL_X_NORMAL;
1972+
edit_submode = NULL;
1973+
showmode();
1974+
retval = TRUE;
1975+
break;
19361976
case Ctrl_P:
19371977
case Ctrl_N:
19381978
// ^X^P means LOCAL expansion if nothing interrupted (eg we
@@ -2929,6 +2969,7 @@ ins_compl_get_exp(pos_T *ini)
29292969
break;
29302970

29312971
case CTRL_X_CMDLINE:
2972+
case CTRL_X_CMDLINE_CTRL_X:
29322973
if (expand_cmdline(&compl_xp, compl_pattern,
29332974
(int)STRLEN(compl_pattern),
29342975
&num_matches, &matches) == EXPAND_OK)

src/testdir/test_ins_complete.vim

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,66 @@ func Test_complete_cmdline()
730730
call assert_equal('call getqflist(', getline(2))
731731
exe "normal oabcxyz(\<C-X>\<C-V>"
732732
call assert_equal('abcxyz(', getline(3))
733+
com! -buffer TestCommand1 echo 'TestCommand1'
734+
com! -buffer TestCommand2 echo 'TestCommand2'
735+
write TestCommand1Test
736+
write TestCommand2Test
737+
" Test repeating <CTRL-X> <CTRL-V> and switching to another CTRL-X mode
738+
exe "normal oT\<C-X>\<C-V>\<C-X>\<C-V>\<C-X>\<C-F>\<Esc>"
739+
call assert_equal('TestCommand2Test', getline(4))
740+
call delete('TestCommand1Test')
741+
call delete('TestCommand2Test')
742+
delcom TestCommand1
743+
delcom TestCommand2
744+
close!
745+
endfunc
746+
747+
" Test for <CTRL-X> <CTRL-Z> stopping completion without changing the match
748+
func Test_complete_stop()
749+
new
750+
func Save_mode1()
751+
let g:mode1 = mode(1)
752+
return ''
753+
endfunc
754+
func Save_mode2()
755+
let g:mode2 = mode(1)
756+
return ''
757+
endfunc
758+
inoremap <F1> <C-R>=Save_mode1()<CR>
759+
inoremap <F2> <C-R>=Save_mode2()<CR>
760+
call setline(1, ['aaa bbb ccc '])
761+
exe "normal A\<C-N>\<C-P>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
762+
call assert_equal('ic', g:mode1)
763+
call assert_equal('i', g:mode2)
764+
call assert_equal('aaa bbb ccc ', getline(1))
765+
exe "normal A\<C-N>\<Down>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
766+
call assert_equal('ic', g:mode1)
767+
call assert_equal('i', g:mode2)
768+
call assert_equal('aaa bbb ccc aaa', getline(1))
769+
set completeopt+=noselect
770+
exe "normal A \<C-N>\<Down>\<Down>\<C-L>\<C-L>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
771+
call assert_equal('ic', g:mode1)
772+
call assert_equal('i', g:mode2)
773+
call assert_equal('aaa bbb ccc aaa bb', getline(1))
774+
set completeopt&
775+
exe "normal A d\<C-N>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
776+
call assert_equal('ic', g:mode1)
777+
call assert_equal('i', g:mode2)
778+
call assert_equal('aaa bbb ccc aaa bb d', getline(1))
779+
com! -buffer TestCommand1 echo 'TestCommand1'
780+
com! -buffer TestCommand2 echo 'TestCommand2'
781+
exe "normal oT\<C-X>\<C-V>\<C-X>\<C-V>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>"
782+
call assert_equal('ic', g:mode1)
783+
call assert_equal('i', g:mode2)
784+
call assert_equal('TestCommand2', getline(2))
785+
delcom TestCommand1
786+
delcom TestCommand2
787+
unlet g:mode1
788+
unlet g:mode2
789+
iunmap <F1>
790+
iunmap <F2>
791+
delfunc Save_mode1
792+
delfunc Save_mode2
733793
close!
734794
endfunc
735795

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ static char *(features[]) =
755755

756756
static int included_patches[] =
757757
{ /* Add new patch number below this line */
758+
/**/
759+
3389,
758760
/**/
759761
3388,
760762
/**/

0 commit comments

Comments
 (0)