diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 6ec418a93b..0cbd92854f 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -422,16 +422,16 @@ bool cmdline_pum_active(void) } /// Remove the cmdline completion popup menu (if present), free the list of items. -void cmdline_pum_remove(void) +void cmdline_pum_remove(bool defer_redraw) { - pum_undisplay(true); + pum_undisplay(!defer_redraw); XFREE_CLEAR(compl_match_array); compl_match_arraysize = 0; } void cmdline_pum_cleanup(CmdlineInfo *cclp) { - cmdline_pum_remove(); + cmdline_pum_remove(false); wildmenu_cleanup(cclp); } @@ -936,7 +936,7 @@ char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode) // The entries from xp_files may be used in the PUM, remove it. if (compl_match_array != NULL) { - cmdline_pum_remove(); + cmdline_pum_remove(false); } } xp->xp_selected = (options & WILD_NOSELECT) ? -1 : 0; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e93698be94..d345990ac6 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -136,6 +136,7 @@ typedef struct { int prev_cmdpos; char *prev_cmdbuff; char *save_p_icm; + bool skip_pum_redraw; bool some_key_typed; // one of the keys was typed // mouse drag and release events are ignored, unless they are // preceded with a mouse down event @@ -929,7 +930,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear // if certain special keys like or were used as wildchar. Make // sure to still clean up to avoid memory corruption. if (cmdline_pum_active()) { - cmdline_pum_remove(); + cmdline_pum_remove(false); } wildmenu_cleanup(&ccline); s->did_wild_list = false; @@ -1035,12 +1036,17 @@ static int command_line_check(VimState *state) // that occurs while typing a command should // cause the command not to be executed. + // Trigger SafeState if nothing is pending. + may_trigger_safestate(s->xpc.xp_numfiles <= 0); + if (ccline.cmdbuff != NULL) { s->prev_cmdbuff = xstrdup(ccline.cmdbuff); } - // Trigger SafeState if nothing is pending. - may_trigger_safestate(s->xpc.xp_numfiles <= 0); + // Defer screen update to avoid pum flicker during wildtrigger() + if (s->c == K_WILD && s->firstc != '@') { + s->skip_pum_redraw = true; + } cursorcmd(); // set the cursor on the right spot ui_cursor_shape(); @@ -1124,6 +1130,7 @@ static int command_line_wildchar_complete(CommandLineState *s) int res; int options = WILD_NO_BEEP; bool escape = s->firstc != '@'; + bool redraw_if_menu_empty = s->c == K_WILD; bool wim_noselect = p_wmnu && (wim_flags[0] & kOptWimFlagNoselect) != 0; if (wim_flags[s->wim_index] & kOptWimFlagLastused) { @@ -1171,6 +1178,11 @@ static int command_line_wildchar_complete(CommandLineState *s) res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, escape); } + // Remove popup menu if no completion items are available + if (redraw_if_menu_empty && s->xpc.xp_numfiles <= 0) { + pum_check_clear(); + } + // if interrupted while completing, behave like it failed if (got_int) { vpeekc(); // remove from input stream @@ -1232,7 +1244,11 @@ static int command_line_wildchar_complete(CommandLineState *s) static void command_line_end_wildmenu(CommandLineState *s) { if (cmdline_pum_active()) { - cmdline_pum_remove(); + s->skip_pum_redraw = (s->skip_pum_redraw + && (vim_isprintc(s->c) + || s->c == K_BS || s->c == Ctrl_H || s->c == K_DEL + || s->c == K_KDEL || s->c == Ctrl_W || s->c == Ctrl_U)); + cmdline_pum_remove(s->skip_pum_redraw); } if (s->xpc.xp_numfiles != -1) { ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua index 75eb234c6d..44576ce7df 100644 --- a/test/functional/legacy/cmdline_spec.lua +++ b/test/functional/legacy/cmdline_spec.lua @@ -531,6 +531,62 @@ describe('cmdline', function() ]]) end) + -- oldtest: Test_wildtrigger_update_screen() + it('pum by wildtrigger() avoids flicker', function() + local screen = Screen.new(40, 10) + exec([[ + command! -nargs=* -complete=customlist,TestFn TestCmd echo + func TestFn(cmdarg, b, c) + if a:cmdarg == 'ax' + return [] + else + return map(range(1, 5), 'printf("abc%d", v:val)') + endif + endfunc + set wildmode=noselect,full + set wildoptions=pum + set wildmenu + cnoremap =wildtrigger()[-1] + ]]) + + feed(':TestCmd a') + screen:expect([[ + | + {1:~ }|*3 + {1:~ }{4: abc1 }{1: }| + {1:~ }{4: abc2 }{1: }| + {1:~ }{4: abc3 }{1: }| + {1:~ }{4: abc4 }{1: }| + {1:~ }{4: abc5 }{1: }| + :TestCmd a^ | + ]]) + + -- Typing a character when pum is open does not close the pum window + -- This is needed to prevent pum window from flickering during + -- ':h cmdline-autocompletion'. + feed('x') + screen:expect([[ + | + {1:~ }|*3 + {1:~ }{4: abc1 }{1: }| + {1:~ }{4: abc2 }{1: }| + {1:~ }{4: abc3 }{1: }| + {1:~ }{4: abc4 }{1: }| + {1:~ }{4: abc5 }{1: }| + :TestCmd ax^ | + ]]) + + -- pum window is closed when no completion candidates are available + feed('') + screen:expect([[ + | + {1:~ }|*8 + :TestCmd ax^ | + ]]) + + feed('') + end) + -- oldtest: Test_long_line_noselect() it("long line is shown properly with noselect in 'wildmode'", function() local screen = Screen.new(60, 8) diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index e4a5200f10..69923129f5 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -4939,6 +4939,43 @@ func Test_cmdline_changed() call Ntest_override("char_avail", 0) endfunc +func Test_wildtrigger_update_screen() + CheckScreendump + + let lines =<< trim [SCRIPT] + command! -nargs=* -complete=customlist,TestFn TestCmd echo + func TestFn(cmdarg, b, c) + if a:cmdarg == 'ax' + return [] + else + return map(range(1, 5), 'printf("abc%d", v:val)') + endif + endfunc + set wildmode=noselect,full + set wildoptions=pum + set wildmenu + cnoremap =wildtrigger()[-1] + [SCRIPT] + call writefile(lines, 'XTest_wildtrigger', 'D') + let buf = RunVimInTerminal('-S XTest_wildtrigger', {'rows': 10}) + + call term_sendkeys(buf, ":TestCmd a\") + call VerifyScreenDump(buf, 'Test_wildtrigger_update_screen_1', {}) + + " Typing a character when pum is open does not close the pum window + " This is needed to prevent pum window from flickering during + " ':h cmdline-autocompletion'. + call term_sendkeys(buf, "x") + call VerifyScreenDump(buf, 'Test_wildtrigger_update_screen_2', {}) + + " pum window is closed when no completion candidates are available + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_wildtrigger_update_screen_3', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + " Issue #18035: long lines should not get listed twice in the menu when " 'wildmode' contains 'noselect' func Test_long_line_noselect()