diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 2bfc5850cf..3382e7df89 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -381,6 +381,12 @@ static int insert_check(VimState *state) did_cursorhold = false; } + // Check if we need to cancel completion mode because the window + // or tab page was changed + if (ins_compl_active() && !ins_compl_win_active(curwin)) { + ins_compl_cancel(); + } + // If the cursor was moved we didn't just insert a space if (arrow_used) { s->inserted_space = false; diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index caf0cce681..0b7b5c37c2 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -265,6 +265,9 @@ static extmark_undo_vec_t compl_orig_extmarks; static int compl_cont_mode = 0; static expand_T compl_xp; +static win_T *compl_curr_win = NULL; ///< win where completion is active +static buf_T *compl_curr_buf = NULL; ///< buf where completion is active + // List of flags for method of completion. static int compl_cont_status = 0; #define CONT_ADDING 1 ///< "normal" or "adding" expansion @@ -1753,6 +1756,8 @@ void ins_compl_clear(void) compl_matches = 0; compl_selected_item = -1; compl_ins_end_col = 0; + compl_curr_win = NULL; + compl_curr_buf = NULL; API_CLEAR_STRING(compl_pattern); API_CLEAR_STRING(compl_leader); edit_submode_extra = NULL; @@ -1773,7 +1778,7 @@ bool ins_compl_active(void) /// Return true when wp is the actual completion window bool ins_compl_win_active(win_T *wp) { - return ins_compl_active() && !(wp->w_p_pvw || wp->w_float_is_info); + return ins_compl_active() && wp == compl_curr_win && wp->w_buffer == compl_curr_buf; } /// Selected one of the matches. When false the match was edited or using the @@ -2300,6 +2305,12 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) return retval; } +/// Cancel completion. +bool ins_compl_cancel(void) +{ + return ins_compl_stop(' ', ctrl_x_mode, true); +} + /// Prepare for Insert mode completion, or stop it. /// Called just after typing a character in Insert mode. /// @@ -4851,6 +4862,8 @@ int ins_complete(int c, bool enable_pum) return FAIL; } + compl_curr_win = curwin; + compl_curr_buf = curwin->w_buffer; compl_shown_match = compl_curr_match; compl_shows_dir = compl_direction; diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 964eb7e708..46f4f79789 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1164,6 +1164,7 @@ describe('builtin popupmenu', function() [8] = { foreground = Screen.colors.Red }, [9] = { foreground = Screen.colors.Yellow, background = Screen.colors.Green }, [10] = { foreground = Screen.colors.White, background = Screen.colors.Green }, + [11] = { background = Screen.colors.LightGrey, underline = true }, ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey }, kn = { foreground = Screen.colors.Red, background = Screen.colors.Plum1 }, xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey }, @@ -4343,12 +4344,32 @@ describe('builtin popupmenu', function() end end) - if not multigrid then - it('with multiline messages', function() - screen:try_resize(40, 8) - feed('ixx') - command('imap echoerr "very"\\|echoerr "much"\\|echoerr "error"') - fn.complete(1, { 'word', 'choice', 'text', 'thing' }) + it('with multiline messages', function() + screen:try_resize(40, 8) + feed('ixx') + command('imap echoerr "very"\\|echoerr "much"\\|echoerr "error"') + fn.complete(1, { 'word', 'choice', 'text', 'thing' }) + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:----------------------------------------]|*7 + [3:----------------------------------------]| + ## grid 2 + xx | + word^ | + {1:~ }|*5 + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + {s:word }| + {n:choice }| + {n:text }| + {n:thing }| + ]], + float_pos = { [4] = { -1, 'NW', 2, 2, 0, false, 100 } }, + }) + else screen:expect([[ xx | word^ | @@ -4359,8 +4380,33 @@ describe('builtin popupmenu', function() {1:~ }| {2:-- INSERT --} | ]]) + end - feed('') + feed('') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:----------------------------------------]|*4 + [3:----------------------------------------]|*4 + ## grid 2 + xx | + word | + {1:~ }|*5 + ## grid 3 + {6:very} | + {6:much} | + {6:error} | + {5:Press ENTER or type command to continue}^ | + ## grid 4 + {s:word }| + {n:choice }| + {n:text }| + {n:thing }| + ]], + float_pos = { [4] = { -1, 'NW', 2, 2, 0, false, 100 } }, + }) + else screen:expect([[ xx | word | @@ -4371,8 +4417,30 @@ describe('builtin popupmenu', function() {6:error} | {5:Press ENTER or type command to continue}^ | ]]) + end - feed('') + feed('') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:----------------------------------------]|*7 + [3:----------------------------------------]| + ## grid 2 + xx | + word^ | + {1:~ }|*5 + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + {s:word }| + {n:choice }| + {n:text }| + {n:thing }| + ]], + float_pos = { [4] = { -1, 'NW', 2, 2, 0, false, 100 } }, + }) + else screen:expect([[ xx | word^ | @@ -4383,8 +4451,30 @@ describe('builtin popupmenu', function() {1:~ }| {2:-- INSERT --} | ]]) + end - feed('') + feed('') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:----------------------------------------]|*7 + [3:----------------------------------------]| + ## grid 2 + xx | + choice^ | + {1:~ }|*5 + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + {n:word }| + {s:choice }| + {n:text }| + {n:thing }| + ]], + float_pos = { [4] = { -1, 'NW', 2, 2, 0, false, 100 } }, + }) + else screen:expect([[ xx | choice^ | @@ -4395,35 +4485,92 @@ describe('builtin popupmenu', function() {1:~ }| {2:-- INSERT --} | ]]) + end - command('split') + command('split') + feed('') + fn.complete(1, { 'word', 'choice', 'text', 'thing' }) + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [5:----------------------------------------]|*3 + {4:[No Name] [+] }| + [2:----------------------------------------]|*2 + {3:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + xx | + word | + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + {s:word }| + {n:choice }| + {n:text }| + {n:thing }| + ## grid 5 + xx | + word^ | + {1:~ }| + ]], + float_pos = { [4] = { -1, 'NW', 5, 2, 0, false, 100 } }, + }) + else screen:expect([[ xx | - choice^ | - {n:word }{1: }| - {s:choice }{4: }| + word^ | + {s:word }{1: }| + {n:choice }{4: }| {n:text } | {n:thing } | {3:[No Name] [+] }| {2:-- INSERT --} | ]]) + end - api.nvim_input_mouse('wheel', 'down', '', 0, 6, 15) - screen:expect { + api.nvim_input_mouse('wheel', 'down', '', 0, 6, 15) + if multigrid then + screen:expect({ grid = [[ + ## grid 1 + [5:----------------------------------------]|*3 + {4:[No Name] [+] }| + [2:----------------------------------------]|*2 + {3:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + word | + {1:~ }| + ## grid 3 + {2:-- INSERT --} | + ## grid 4 + {s:word }| + {n:choice }| + {n:text }| + {n:thing }| + ## grid 5 xx | - choice^ | - {n:word }{1: }| - {s:choice }{4: }| + word^ | + {1:~ }| + ]], + float_pos = { [4] = { -1, 'NW', 5, 2, 0, false, 100 } }, + }) + else + screen:expect([[ + xx | + word^ | + {s:word }{1: }| + {n:choice }{4: }| {n:text } | {n:thing }{1: }| {3:[No Name] [+] }| {2:-- INSERT --} | - ]], - unchanged = true, - } - end) + ]]) + end + end) + if not multigrid then it('with kind, menu and abbr attributes', function() screen:try_resize(40, 8) feed('ixx ') @@ -7839,6 +7986,53 @@ describe('builtin popupmenu', function() {2:-- INSERT --} | ]]) end) + + -- oldtest: Test_pum_clear_when_switch_tab_or_win() + it('is cleared when switching tab or win', function() + screen:try_resize(55, 20) + exec([[ + inoremap wincmd w + inoremap tabnext + ]]) + + command('tabe') + feed('Aaa aaa ') + screen:expect([[ + {11: [No Name] }{2: + [No Name] }{3: }{11:X}| + aa aaa aa^ | + {1:~ }{s: aa }{1: }| + {1:~ }{n: aaa }{1: }| + {1:~ }|*15 + {2:-- Keyword Local completion (^N^P) }{5:match 1 of 2} | + ]]) + feed('') + screen:expect([[ + {2: [No Name] }{11: + [No Name] }{3: }{11:X}| + ^ | + {1:~ }|*17 + {2:-- INSERT --} | + ]]) + feed('') + command('tabclose!') + + command('vnew win_b') + feed('Abb bbb ') + screen:expect([[ + bb bbb bb^ │aa aaa aa | + {1:~ }{s: bb }{1: }│{1:~ }| + {1:~ }{n: bbb }{1: }│{1:~ }| + {1:~ }│{1:~ }|*15 + {4:win_b [+] }{3:[No Name] [+] }| + {2:-- Keyword Local completion (^N^P) }{5:match 1 of 2} | + ]]) + feed('') + screen:expect([[ + bb bbb bb │aa aaa a^a | + {1:~ }│{1:~ }|*17 + {3:win_b [+] }{4:[No Name] [+] }| + {2:-- INSERT --} | + ]]) + end) end end diff --git a/test/old/testdir/test_popup.vim b/test/old/testdir/test_popup.vim index 988d60916c..3ef14513d7 100644 --- a/test/old/testdir/test_popup.vim +++ b/test/old/testdir/test_popup.vim @@ -1966,4 +1966,36 @@ func Test_pum_complete_with_special_characters() call StopVimInTerminal(buf) endfunc +func Test_pum_clear_when_switch_tab_or_win() + CheckScreendump + + let lines =<< trim END + inoremap wincmd w + inoremap tabnext + END + + call writefile(lines, 'Xtest', 'D') + let buf = RunVimInTerminal('-S Xtest', {}) + + call term_sendkeys(buf, ":tabe\") + call TermWait(buf, 50) + call term_sendkeys(buf, "Aaa aaa \") + call VerifyScreenDump(buf, 'Test_tabnext_clear_pum_01', {}) + call term_sendkeys(buf, "\") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_tabnext_clear_pum_02', {}) + call term_sendkeys(buf, "\:tabclose!\") + + call term_sendkeys(buf, ":vnew win_b\") + call TermWait(buf, 50) + call term_sendkeys(buf, "Abb bbb \") + call VerifyScreenDump(buf, 'Test_switchwin_clear_pum_01', {}) + call term_sendkeys(buf, "\") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_switchwin_clear_pum_02', {}) + + call StopVimInTerminal(buf) +endfunc + + " vim: shiftwidth=2 sts=2 expandtab