neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 7f2298ad32a6f1158f05b4f0a0c119bc65bfa554
parent acb99b8a6572d8ea8d917955a653945550923be0
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Tue,  9 Sep 2025 08:11:13 +0800

fix(cmdline): fix inconsistent behavior with ext_popupmenu (#35688)


Diffstat:
Msrc/nvim/cmdexpand.c | 16++++++++++++----
Mtest/functional/ui/cmdline_spec.lua | 60++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mtest/functional/ui/wildmode_spec.lua | 47++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 108 insertions(+), 15 deletions(-)

diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c @@ -451,6 +451,16 @@ bool cmdline_compl_is_fuzzy(void) return xp != NULL && cmdline_fuzzy_completion_supported(xp); } +/// Checks whether popup menu should be used for cmdline completion wildmenu. +/// +/// @param wildmenu whether wildmenu is needed by current 'wildmode' part +bool cmdline_compl_use_pum(bool need_wildmenu) +{ + return ((need_wildmenu && (wop_flags & kOptWopFlagPum) + && !(ui_has(kUICmdline) && cmdline_win == NULL)) + || ui_has(kUIWildmenu) || (ui_has(kUICmdline) && ui_has(kUIPopupmenu))); +} + /// Return the number of characters that should be skipped in the wildmenu /// These are backslashes used for escaping. Do show backslashes in help tags /// and in search pattern completion matches. @@ -748,7 +758,7 @@ static char *get_next_or_prev_match(int mode, expand_T *xp) if (compl_match_array) { compl_selected = findex; cmdline_pum_display(false); - } else if (wop_flags & kOptWopFlagPum) { + } else if (cmdline_compl_use_pum(true)) { if (cmdline_pum_create(get_cmdline_info(), xp, xp->xp_files, xp->xp_numfiles, cmd_showtail, false) == EXPAND_OK) { compl_selected = findex; @@ -1122,9 +1132,7 @@ int showmatches(expand_T *xp, bool display_wildmenu, bool display_list, bool nos showtail = cmd_showtail; } - if (((!ui_has(kUICmdline) || cmdline_win != NULL) && display_wildmenu && !display_list - && (wop_flags & kOptWopFlagPum)) - || ui_has(kUIWildmenu) || (ui_has(kUICmdline) && ui_has(kUIPopupmenu))) { + if (cmdline_compl_use_pum(display_wildmenu && !display_list)) { int retval = cmdline_pum_create(ccline, xp, matches, numMatches, showtail, noselect); if (retval == EXPAND_OK) { compl_selected = noselect ? -1 : 0; diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua @@ -454,7 +454,7 @@ local function test_cmdline(linegrid) } end) - it('works together with ext_popupmenu', function() + local function test_ext_cmdline_popupmenu() local expected = { { 'define', '', '', '' }, { 'jump', '', '', '' }, @@ -557,19 +557,63 @@ local function test_cmdline(linegrid) {1:~ }|*3 | ]], - cmdline = { - { - content = { { 'e wildpum/Xnamedir/XdirA/' } }, - firstc = ':', - pos = 25, - }, - }, + cmdline = { { content = { { 'e wildpum/Xnamedir/XdirA/' } }, firstc = ':', pos = 25 } }, popupmenu = { anchor = { -1, 0, 19 }, items = { { 'XdirA/', '', '', '' }, { 'XfileA', '', '', '' } }, pos = 0, }, } + + feed('<Esc>') + command('set wildmode=longest,full') + feed(':sign u<tab>') + screen:expect { + grid = [[ + ^ | + {1:~ }|*3 + | + ]], + cmdline = { { content = { { 'sign un' } }, firstc = ':', pos = 7 } }, + } + + feed('<tab>') + local s_undefine_unplace_0 = { + grid = [[ + ^ | + {1:~ }|*3 + | + ]], + cmdline = { { content = { { 'sign undefine' } }, firstc = ':', pos = 13 } }, + popupmenu = { + anchor = { -1, 0, 5 }, + items = { { 'undefine', '', '', '' }, { 'unplace', '', '', '' } }, + pos = 0, + }, + } + screen:expect(s_undefine_unplace_0) + + feed('<Esc>') + screen:expect([[ + ^ | + {1:~ }|*3 + | + ]]) + + feed(':sign un<tab>') + screen:expect(s_undefine_unplace_0) + end + + describe('works together with ext_popupmenu', function() + it('with wildoptions=pum', function() + command('set wildoptions=pum') + test_ext_cmdline_popupmenu() + end) + + it('with wildoptions=', function() + command('set wildoptions=') + test_ext_cmdline_popupmenu() + end) end) it('ext_wildmenu takes precedence over ext_popupmenu', function() diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua @@ -593,7 +593,7 @@ describe('ui/ext_wildmenu', function() screen = Screen.new(25, 5, { rgb = true, ext_wildmenu = true }) end) - it('works with :sign <tab>', function() + local function test_ext_wildmenu_sign_cmd() local expected = { 'define', 'jump', @@ -650,12 +650,53 @@ describe('ui/ext_wildmenu', function() } feed('a') - screen:expect { - grid = [[ + screen:expect([[ | {1:~ }|*3 :sign definea^ | + ]]) + + feed('<Esc>') + command('set wildmode=longest,full') + feed(':sign u<tab>') + screen:expect([[ + | + {1:~ }|*3 + :sign un^ | + ]]) + + feed('<tab>') + local s_undefine_unplace_0 = { + grid = [[ + | + {1:~ }|*3 + :sign undefine^ | ]], + wildmenu_items = { 'undefine', 'unplace' }, + wildmenu_pos = 0, } + screen:expect(s_undefine_unplace_0) + + feed('<Esc>') + screen:expect([[ + ^ | + {1:~ }|*3 + | + ]]) + + feed(':sign un<tab>') + screen:expect(s_undefine_unplace_0) + end + + describe('works with :sign <tab>', function() + it('with wildoptions=pum', function() + command('set wildoptions=pum') + test_ext_wildmenu_sign_cmd() + end) + + it('with wildoptions=', function() + command('set wildoptions=') + test_ext_wildmenu_sign_cmd() + end) end) end)