]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1621: flicker in popup menu during cmdline autocompletion v9.1.1621
authorGirish Palya <girishji@gmail.com>
Fri, 8 Aug 2025 06:26:03 +0000 (08:26 +0200)
committerChristian Brabandt <cb@256bit.org>
Sun, 10 Aug 2025 07:55:18 +0000 (09:55 +0200)
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 <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/cmdexpand.c
src/ex_getln.c
src/proto/cmdexpand.pro
src/testdir/dumps/Test_wildtrigger_update_screen_1.dump [new file with mode: 0644]
src/testdir/dumps/Test_wildtrigger_update_screen_2.dump [new file with mode: 0644]
src/testdir/dumps/Test_wildtrigger_update_screen_3.dump [new file with mode: 0644]
src/testdir/test_cmdline.vim
src/version.c

index ab849e296489769d67c35fefbf4dc5f8d189f63e..4ef18791177d9a4e00e4a4d279cb10f293f9e8cc 100644 (file)
@@ -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;
 
index 93ce0706269520f89c6d89ac62ecba195ed7cd37..a96ece70c5476ce7c4d0ff49539cad564287b4f9 100644 (file)
@@ -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;
index 2b2cc23e5afda88777d2c11ef73c6ead206bf594..1ace23c5c348f2dc18b71ad8b7f2e2d49a3f6820 100644 (file)
@@ -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 (file)
index 0000000..0b6383f
--- /dev/null
@@ -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 (file)
index 0000000..abc8047
--- /dev/null
@@ -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 (file)
index 0000000..836ea6d
--- /dev/null
@@ -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| 
index 5bc698106abfcf2f91c97260ac62cb25fd0d52a8..e6dcfd3998876677ac8f12e603fb45e596451383 100644 (file)
@@ -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
index da0f7a3803a3b433fa5b52f52aac4ebf09583363..ee0bf8b413a3e68307d1e3050977d8162a386f10 100644 (file)
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1621,
 /**/
     1620,
 /**/