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)
This commit is contained in:
zeertzjq
2021-08-31 19:12:51 +02:00
committed by Bram Moolenaar
parent 4eaef9979f
commit dca29d9cf4
5 changed files with 119 additions and 7 deletions

View File

@ -168,6 +168,7 @@ commands in CTRL-X submode *i_CTRL-X_index*
|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

View File

@ -640,6 +640,8 @@ Completion can be done for:
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 that start with the
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'.

View File

@ -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 of located bad word
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)

View File

@ -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

View File

@ -755,6 +755,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
3389,
/**/
3388,
/**/