# HG changeset patch # User Bram Moolenaar # Date 1630430105 -7200 # Node ID 31db9c6df4e31fe4330e1a2813862edfc12e2bde # Parent db2130b7a83f1e5edcf7a4aebe0b22d441708d9f patch 8.2.3389: cannot stop insert mode completion without side effects Commit: https://github.com/vim/vim/commit/dca29d9cf46cd1d4d4519211c7af78b6b1c56960 Author: zeertzjq 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) diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt --- 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 diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt --- 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'. diff --git a/src/insexpand.c b/src/insexpand.c --- 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) diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim --- 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(\\" call assert_equal('abcxyz(', getline(3)) + com! -buffer TestCommand1 echo 'TestCommand1' + com! -buffer TestCommand2 echo 'TestCommand2' + write TestCommand1Test + write TestCommand2Test + " Test repeating and switching to another CTRL-X mode + exe "normal oT\\\\\\\" + call assert_equal('TestCommand2Test', getline(4)) + call delete('TestCommand1Test') + call delete('TestCommand2Test') + delcom TestCommand1 + delcom TestCommand2 + close! +endfunc + +" Test for 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 =Save_mode1() + inoremap =Save_mode2() + call setline(1, ['aaa bbb ccc ']) + exe "normal A\\\\\\\" + call assert_equal('ic', g:mode1) + call assert_equal('i', g:mode2) + call assert_equal('aaa bbb ccc ', getline(1)) + exe "normal A\\\\\\\" + 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 \\\\\\\\\\" + 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\\\\\\" + 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\\\\\\\\\" + 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 + iunmap + delfunc Save_mode1 + delfunc Save_mode2 close! endfunc diff --git a/src/version.c b/src/version.c --- 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,