patch 9.1.1621: flicker in popup menu during cmdline autocompletion Commit: https://github.com/vim/vim/commit/da9c9668931e5c8ee4dc0e430898ad2d80f56862 Author: Girish Palya <giris...@gmail.com> Date: Fri Aug 8 08:26:03 2025 +0200
patch 9.1.1621: flicker in popup menu during cmdline autocompletion Problem: When the popup menu (PUM) occupies more than half the screen height, it flickers whenever a character is typed or erased. This happens because the PUM is cleared and the screen is redrawn before a new PUM is rendered. The extra redraw between menu updates causes visible flicker. Solution: A complete, non-hacky fix would require removing the CmdlineChanged event from the loop and letting autocompletion manage the process end-to-end. This is because screen redraws after any cmdline change are necessary for other features to work. This change modifies wildtrigger() so that the next typed character defers the screen update instead of redrawing immediately. This removes the intermediate redraw, eliminating flicker and making cmdline autocompletion feel smooth (Girish Palya). Trade-offs: This behavior change in wildtrigger() is tailored specifically for :h cmdline-autocompletion. wildtrigger() now has no general-purpose use outside this scenario. closes: #17932 Signed-off-by: Girish Palya <giris...@gmail.com> Signed-off-by: Christian Brabandt <c...@256bit.org> diff --git a/src/cmdexpand.c b/src/cmdexpand.c index ab849e296..4ef187911 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -455,9 +455,8 @@ cmdline_pum_active(void) * items and refresh the screen. */ void -cmdline_pum_remove(cmdline_info_T *cclp UNUSED) +cmdline_pum_remove(cmdline_info_T *cclp UNUSED, int defer_redraw) { - int save_p_lz = p_lz; int save_KeyTyped = KeyTyped; #ifdef FEAT_EVAL int save_RedrawingDisabled = RedrawingDisabled; @@ -468,9 +467,15 @@ cmdline_pum_remove(cmdline_info_T *cclp UNUSED) pum_undisplay(); VIM_CLEAR(compl_match_array); compl_match_arraysize = 0; - p_lz = FALSE; // avoid the popup menu hanging around - update_screen(0); - p_lz = save_p_lz; + if (!defer_redraw) + { + int save_p_lz = p_lz; + p_lz = FALSE; // avoid the popup menu hanging around + update_screen(0); + p_lz = save_p_lz; + } + else + pum_call_update_screen(); redrawcmd(); // When a function is called (e.g. for 'foldtext') KeyTyped might be reset @@ -485,7 +490,7 @@ cmdline_pum_remove(cmdline_info_T *cclp UNUSED) void cmdline_pum_cleanup(cmdline_info_T *cclp) { - cmdline_pum_remove(cclp); + cmdline_pum_remove(cclp, FALSE); wildmenu_cleanup(cclp); } @@ -1068,7 +1073,7 @@ ExpandOne( // The entries from xp_files may be used in the PUM, remove it. if (compl_match_array != NULL) - cmdline_pum_remove(get_cmdline_info()); + cmdline_pum_remove(get_cmdline_info(), FALSE); } xp->xp_selected = 0; diff --git a/src/ex_getln.c b/src/ex_getln.c index 93ce07062..a96ece70c 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -939,6 +939,7 @@ cmdline_wildchar_complete( int *wim_index_p, expand_T *xp, int *gotesc, + int redraw_if_menu_empty, pos_T *pre_incsearch_pos) { int wim_index = *wim_index_p; @@ -991,6 +992,10 @@ cmdline_wildchar_complete( else res = nextwild(xp, WILD_EXPAND_KEEP, options, escape); + // Remove popup window if no completion items are available + if (redraw_if_menu_empty && xp->xp_numfiles <= 0) + update_screen(0); + // if interrupted while completing, behave like it failed if (got_int) { @@ -1633,7 +1638,7 @@ getcmdline_int( int clear_ccline) // clear ccline first { static int depth = 0; // call depth - int c; + int c = 0; int i; int j; int gotesc = FALSE; // TRUE when <ESC> just typed @@ -1838,6 +1843,7 @@ getcmdline_int( int trigger_cmdlinechanged = TRUE; int end_wildmenu; int prev_cmdpos = ccline.cmdpos; + int skip_pum_redraw = FALSE; VIM_CLEAR(prev_cmdbuff); @@ -1863,6 +1869,10 @@ getcmdline_int( goto returncmd; } + // Defer screen update to avoid pum flicker during wildtrigger() + if (c == K_WILD && firstc != '@') + skip_pum_redraw = TRUE; + // Get a character. Ignore K_IGNORE and K_NOP, they should not do // anything, such as stop completion. do @@ -2002,7 +2012,12 @@ getcmdline_int( if (end_wildmenu) { if (cmdline_pum_active()) - cmdline_pum_remove(&ccline); + { + skip_pum_redraw = skip_pum_redraw && (vim_isprintc(c) + || c == K_BS || c == Ctrl_H || c == K_DEL + || c == K_KDEL || c == Ctrl_W || c == Ctrl_U); + cmdline_pum_remove(&ccline, skip_pum_redraw); + } if (xpc.xp_numfiles != -1) (void)ExpandOne(&xpc, NULL, NULL, 0, WILD_FREE); did_wild_list = FALSE; @@ -2081,7 +2096,7 @@ getcmdline_int( if (c == K_WILD) ++emsg_silent; // Silence the bell res = cmdline_wildchar_complete(c, firstc != '@', &did_wild_list, - &wim_index, &xpc, &gotesc, + &wim_index, &xpc, &gotesc, c == K_WILD, #ifdef FEAT_SEARCH_EXTRA &is_state.search_start #else @@ -2647,7 +2662,7 @@ returncmd: // if certain special keys like <Esc> or <C-\> were used as wildchar. Make // sure to still clean up to avoid memory corruption. if (cmdline_pum_active()) - cmdline_pum_remove(&ccline); + cmdline_pum_remove(&ccline, FALSE); wildmenu_cleanup(&ccline); did_wild_list = FALSE; wim_index = 0; diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index 2b2cc23e5..1ace23c5c 100644 --- a/src/proto/cmdexpand.pro +++ b/src/proto/cmdexpand.pro @@ -3,7 +3,7 @@ int cmdline_fuzzy_complete(char_u *fuzzystr); int nextwild(expand_T *xp, int type, int options, int escape); void cmdline_pum_display(void); int cmdline_pum_active(void); -void cmdline_pum_remove(cmdline_info_T *cclp); +void cmdline_pum_remove(cmdline_info_T *cclp, int defer); void cmdline_pum_cleanup(cmdline_info_T *cclp); int cmdline_compl_startcol(void); char_u *cmdline_compl_pattern(void); diff --git a/src/testdir/dumps/Test_wildtrigger_update_screen_1.dump b/src/testdir/dumps/Test_wildtrigger_update_screen_1.dump new file mode 100644 index 000000000..0b6383ffe --- /dev/null +++ b/src/testdir/dumps/Test_wildtrigger_update_screen_1.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @6| +0#0000001#ffd7ff255|a|b|c|1| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|2| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|3| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|4| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|5| @10| +0#4040ff13#ffffff0@50 +|:+0#0000000&|T|e|s|t|C|m|d| |a> @64 diff --git a/src/testdir/dumps/Test_wildtrigger_update_screen_2.dump b/src/testdir/dumps/Test_wildtrigger_update_screen_2.dump new file mode 100644 index 000000000..abc804705 --- /dev/null +++ b/src/testdir/dumps/Test_wildtrigger_update_screen_2.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @6| +0#0000001#ffd7ff255|a|b|c|1| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|2| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|3| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|4| @10| +0#4040ff13#ffffff0@50 +|~| @6| +0#0000001#ffd7ff255|a|b|c|5| @10| +0#4040ff13#ffffff0@50 +|:+0#0000000&|T|e|s|t|C|m|d| |a|x> @63 diff --git a/src/testdir/dumps/Test_wildtrigger_update_screen_3.dump b/src/testdir/dumps/Test_wildtrigger_update_screen_3.dump new file mode 100644 index 000000000..836ea6d15 --- /dev/null +++ b/src/testdir/dumps/Test_wildtrigger_update_screen_3.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|T|e|s|t|C|m|d| |a|x> @45|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 5bc698106..e6dcfd399 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -4822,4 +4822,41 @@ func Test_cmdline_changed() call test_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 <F8> <C-R>=wildtrigger()[-1]<CR> + [SCRIPT] + call writefile(lines, 'XTest_wildtrigger', 'D') + let buf = RunVimInTerminal('-S XTest_wildtrigger', {'rows': 10}) + + call term_sendkeys(buf, ":TestCmd a\<F8>") + 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, "\<F8>") + call VerifyScreenDump(buf, 'Test_wildtrigger_update_screen_3', {}) + + call term_sendkeys(buf, "\<esc>") + call StopVimInTerminal(buf) + cnoremap <buffer> <F8> <C-R>=wildtrigger()[-1]<CR> +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index da0f7a380..ee0bf8b41 100644 --- a/src/version.c +++ b/src/version.c @@ -719,6 +719,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1621, /**/ 1620, /**/ -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_dev+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1ul1Cq-0064Io-BT%40256bit.org.