diff options
-rw-r--r-- | runtime/doc/index.txt | 1 | ||||
-rw-r--r-- | runtime/doc/insert.txt | 8 | ||||
-rw-r--r-- | src/nvim/edit.c | 58 | ||||
-rw-r--r-- | src/nvim/testdir/test_ins_complete.vim | 127 |
4 files changed, 183 insertions, 11 deletions
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index b69b294820..baa7bc1992 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -155,6 +155,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 diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index bb00c77ca8..ef2fe23d91 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -622,6 +622,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 @@ -1022,6 +1024,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'. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 8216987635..f39a6a281b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -84,6 +84,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] #define CTRL_X_MODE_LINE_OR_EVAL(m) \ @@ -109,6 +110,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)"), }; static char *ctrl_x_mode_names[] = { @@ -128,7 +130,8 @@ static char *ctrl_x_mode_names[] = { "omni", "spell", NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" - "eval" + "eval", + "cmdline", }; static char e_hitend[] = N_("Hit end of paragraph"); @@ -762,7 +765,8 @@ static int insert_execute(VimState *state, int key) s->c = do_digraph(s->c); - if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) { + if ((s->c == Ctrl_V || s->c == Ctrl_Q) + && (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X)) { insert_do_complete(s); return 1; } @@ -2028,11 +2032,8 @@ static bool del_char_after_col(int limit_col) */ static 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 the next ^X<> won't ADD nothing, then reset - * compl_cont_status */ + if (ctrl_x_mode != CTRL_X_CMDLINE && ctrl_x_mode != CTRL_X_CMDLINE_CTRL_X) { + // if the next ^X<> won't ADD nothing, then reset compl_cont_status if (compl_cont_status & CONT_N_ADDS) { compl_cont_status |= CONT_INTRPT; } else { @@ -2043,6 +2044,10 @@ static void ins_ctrl_x(void) edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); 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; } } @@ -2052,7 +2057,8 @@ bool ctrl_x_mode_not_default(void) return ctrl_x_mode != CTRL_X_NORMAL; } -// 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. bool ctrl_x_mode_not_defined_yet(void) { return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; @@ -2104,12 +2110,14 @@ bool 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: @@ -2163,6 +2171,7 @@ static bool 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. @@ -3567,6 +3576,26 @@ static bool ins_compl_prep(int c) return retval; } + 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)) { @@ -3633,6 +3662,12 @@ static bool 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 @@ -4292,10 +4327,11 @@ static int 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) { - ins_compl_add_matches(num_matches, matches, FALSE); + ins_compl_add_matches(num_matches, matches, false); } break; @@ -5135,7 +5171,7 @@ static int ins_complete(int c, bool enable_pum) compl_col += startcol; compl_length = (int)curs_col - startcol; compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES); - } else if (ctrl_x_mode == CTRL_X_CMDLINE) { + } else if (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X) { compl_pattern = vim_strnsave(line, curs_col); set_cmd_context(&compl_xp, compl_pattern, (int)STRLEN(compl_pattern), curs_col, false); diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 0fb026f6b0..ce75799551 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -497,6 +497,133 @@ func Test_ins_compl_tag_sft() %bwipe! endfunc +" Test for completing special characters +func Test_complete_special_chars() + new + call setline(1, 'int .*[-\^$ func float') + call feedkeys("oin\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>", 'xt') + call assert_equal('int .*[-\^$ func float', getline(2)) + close! +endfunc + +" Test for completion when text is wrapped across lines. +func Test_complete_across_line() + new + call setline(1, ['red green blue', 'one two three']) + setlocal textwidth=20 + exe "normal 2G$a re\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>" + call assert_equal(['one two three red', 'green blue one'], getline(2, '$')) + close! +endfunc + +" Test for using CTRL-L to add one character when completing matching +func Test_complete_add_onechar() + new + call setline(1, ['wool', 'woodwork']) + call feedkeys("Gowoo\<C-P>\<C-P>\<C-P>\<C-L>f", 'xt') + call assert_equal('woof', getline(3)) + + " use 'ignorecase' and backspace to erase characters from the prefix string + " and then add letters using CTRL-L + %d + set ignorecase backspace=2 + setlocal complete=. + call setline(1, ['workhorse', 'workload']) + normal Go + exe "normal aWOR\<C-P>\<bs>\<bs>\<bs>\<bs>\<bs>\<bs>\<C-L>r\<C-L>\<C-L>" + call assert_equal('workh', getline(3)) + set ignorecase& backspace& + close! +endfunc + +" Test insert completion with 'cindent' (adjust the indent) +func Test_complete_with_cindent() + new + setlocal cindent + call setline(1, ['if (i == 1)', " j = 2;"]) + exe "normal Go{\<CR>i\<C-X>\<C-L>\<C-X>\<C-L>\<CR>}" + call assert_equal(['{', "\tif (i == 1)", "\t\tj = 2;", '}'], getline(3, '$')) + + %d + call setline(1, ['when while', '{', '']) + setlocal cinkeys+==while + exe "normal Giwh\<C-P> " + call assert_equal("\twhile ", getline('$')) + close! +endfunc + +" Test for <CTRL-X> <CTRL-V> completion. Complete commands and functions +func Test_complete_cmdline() + new + exe "normal icaddb\<C-X>\<C-V>" + call assert_equal('caddbuffer', getline(1)) + exe "normal ocall getqf\<C-X>\<C-V>" + 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 + " Test to ensure 'Scanning...' messages are not recorded in messages history func Test_z1_complete_no_history() new |