neovim

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

commit f2bda1effc17fd18b2a1552697c3ff4083b1258f
parent 1359578abbcf56036c46cc424a508a131147218f
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Sun,  7 Sep 2025 13:17:29 +0800

vim-patch:9.1.1737: Patch v9.1.1714 introduce a regression for wildmenu

Problem:  Patch v9.1.1714 introduce a regression for wildmenu (zeertzjq)
Solution: Restore behavior of "longest" in 'wildmode' (Girish Palya)

- Fixed a regression caused by PR vim/vim#18125 selecting wrong item
- Fixed another regression where the first pasted text did not appear on
  the command-line after starting Vim.

closes: vim/vim#18212

https://github.com/vim/vim/commit/8fec92d631cf9d852ad794863721c2710ee2ff9e

Co-authored-by: Girish Palya <girishji@gmail.com>

Diffstat:
Msrc/nvim/ex_getln.c | 25+++++++++++++++++--------
Mtest/functional/ui/popupmenu_spec.lua | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/old/testdir/test_cmdline.vim | 43++++++++++++++++++++++++++++++-------------
3 files changed, 149 insertions(+), 21 deletions(-)

diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c @@ -1179,16 +1179,25 @@ static int command_line_wildchar_complete(CommandLineState *s) // Display matches if (res == OK && s->xpc.xp_numfiles > (wim_noselect ? 0 : 1)) { - // If "longest" fails to identify the longest item, try other - // 'wim' values if available - if (wim_longest && ccline.cmdpos == cmdpos_before) { - if (wim_full) { - nextwild(&s->xpc, WILD_NEXT, options, escape); - } + if (wim_longest) { + bool found_longest_prefix = (ccline.cmdpos != cmdpos_before); if (wim_list || (p_wmnu && wim_full)) { - showmatches(&s->xpc, p_wmnu, wim_list, false); + showmatches(&s->xpc, p_wmnu, wim_list, true); + } else if (!found_longest_prefix) { + bool wim_list_next = (wim_flags[1] & kOptWimFlagList); + bool wim_full_next = (wim_flags[1] & kOptWimFlagFull); + bool wim_noselect_next = (wim_flags[1] & kOptWimFlagNoselect); + if (wim_list_next || (p_wmnu && (wim_full_next || wim_noselect_next))) { + if (wim_noselect_next) { + options |= WILD_NOSELECT; + } + if (wim_full_next || wim_noselect_next) { + nextwild(&s->xpc, WILD_NEXT, options, escape); + } + showmatches(&s->xpc, p_wmnu, wim_list_next, wim_noselect_next); + } } - } else if (!wim_longest) { + } else { if (wim_list || (p_wmnu && (wim_full || wim_noselect))) { showmatches(&s->xpc, p_wmnu, wim_list, wim_noselect); } else { diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua @@ -1060,6 +1060,7 @@ describe('builtin popupmenu', function() [110] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkYellow }, [111] = { background = Screen.colors.Plum1, foreground = Screen.colors.DarkBlue }, [112] = { background = Screen.colors.Plum1, foreground = Screen.colors.DarkGreen }, + [113] = { background = Screen.colors.Yellow, foreground = Screen.colors.Black }, -- popup non-selected item n = { background = Screen.colors.Plum1 }, -- popup scrollbar knob @@ -4694,6 +4695,107 @@ describe('builtin popupmenu', function() feed('<Esc>') + -- "longest:list" shows list whether it finds a candidate or not + command('set wildmode=longest:list,full wildoptions=') + feed(':cn<Tab>') + screen:expect([[ + | + {1:~ }|*3 + {3: }| + :cn | + cnewer cnoreabbrev | + cnext cnoremap | + cnfile cnoremenu | + :cn^ | + ]]) + feed('<Tab>') + screen:expect([[ + | + {1:~ }|*2 + {3: }| + :cn | + cnewer cnoreabbrev | + cnext cnoremap | + cnfile cnoremenu | + {113:cnewer}{3: cnext cnfile > }| + :cnewer^ | + ]]) + feed('<Esc>:sign u<Tab>') + screen:expect([[ + | + {1:~ }|*5 + {3: }| + :sign un | + undefine unplace | + :sign un^ | + ]]) + + -- "longest:full" shows wildmenu whether it finds a candidate or not; + -- item not selected + feed('<Esc>') + command('set wildmode=longest:full,full') + feed(':sign u<Tab>') + screen:expect([[ + | + {1:~ }|*7 + {3:undefine unplace }| + :sign un^ | + ]]) + feed('<Tab>') + screen:expect([[ + | + {1:~ }|*7 + {113:undefine}{3: unplace }| + :sign undefine^ | + ]]) + + feed('<Esc>:cn<Tab>') + screen:expect([[ + | + {1:~ }|*7 + {3:cnewer cnext cnfile > }| + :cn^ | + ]]) + feed('<Tab>') + screen:expect([[ + | + {1:~ }|*7 + {113:cnewer}{3: cnext cnfile > }| + :cnewer^ | + ]]) + + -- If "longest,full" finds a candidate, wildmenu is not shown + feed('<Esc>') + command('set wildmode=longest,full') + feed(':sign u<Tab>') + screen:expect([[ + | + {1:~ }|*8 + :sign un^ | + ]]) + -- Subsequent wildchar shows wildmenu + feed('<Tab>') + screen:expect([[ + | + {1:~ }|*7 + {113:undefine}{3: unplace }| + :sign undefine^ | + ]]) + + -- 'longest' does not find candidate, and displays menu without selecting item + feed('<Esc>') + command('set wildmode=longest,noselect') + feed(':cn<Tab>') + screen:expect([[ + | + {1:~ }|*7 + {3:cnewer cnext cnfile > }| + :cn^ | + ]]) + + feed('<C-U><Esc>') + command('set wildmode& wildoptions=pum') + -- check positioning with multibyte char in pattern command('e långfile1') command('sp långfile2') diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim @@ -2900,7 +2900,7 @@ func Test_wildmenu_pum() call term_sendkeys(buf, "\<C-U>set wildmode=longest,list\<CR>") call term_sendkeys(buf, ":cn\<Tab>") call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {}) - call term_sendkeys(buf, "\<Tab>") + call term_sendkeys(buf, "s") call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {}) " Tests a directory name contained full-width characters. @@ -3002,28 +3002,45 @@ func Test_wildmenu_pum() call term_sendkeys(buf, "\<Esc>:set showtabline& laststatus& lazyredraw&\<CR>") - " Verify that if "longest" finds nothing, wildmenu is still shown - call term_sendkeys(buf, ":set wildmode=longest:full,full wildoptions&\<CR>") - call term_sendkeys(buf, ":cn\<Tab>") - call TermWait(buf, 50) - call VerifyScreenDump(buf, 'Test_wildmenu_pum_54', {}) - - " Verify that if "longest" finds nothing, "list" is still shown - call term_sendkeys(buf, "\<Esc>:set wildmode=longest:list,full\<CR>") + " "longest:list" shows list whether it finds a candidate or not + call term_sendkeys(buf, ":set wildmode=longest:list,full wildoptions&\<CR>") call term_sendkeys(buf, ":cn\<Tab>") call TermWait(buf, 50) call VerifyScreenDump(buf, 'Test_wildmenu_pum_55', {}) call term_sendkeys(buf, "\<Tab>") call TermWait(buf, 50) call VerifyScreenDump(buf, 'Test_wildmenu_pum_56', {}) + call term_sendkeys(buf, "\<Esc>:sign u\<Tab>") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_57', {}) - " Verify that if "longest" finds a candidate, wildmenu is not shown - call term_sendkeys(buf, "\<Esc>:set wildmode=longest:full,full wildoptions&\<CR>") + " "longest:full" shows wildmenu whether it finds a candidate or not; item not selected + call term_sendkeys(buf, "\<Esc>:set wildmode=longest:full,full\<CR>") call term_sendkeys(buf, ":sign u\<Tab>") - call VerifyScreenDump(buf, 'Test_wildmenu_pum_57', {}) + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_58', {}) + call term_sendkeys(buf, "\<Tab>") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_59', {}) + call term_sendkeys(buf, "\<Esc>:cn\<Tab>") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_60', {}) + call term_sendkeys(buf, "\<Tab>") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_61', {}) + + " If "longest,full" finds a candidate, wildmenu is not shown + call term_sendkeys(buf, "\<Esc>:set wildmode=longest,full\<CR>") + call term_sendkeys(buf, ":sign u\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_62', {}) " Subsequent wildchar shows wildmenu call term_sendkeys(buf, "\<Tab>") - call VerifyScreenDump(buf, 'Test_wildmenu_pum_58', {}) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_63', {}) + + " 'longest' does not find candidate, and displays menu without selecting item + call term_sendkeys(buf, "\<Esc>:set wildmode=longest,noselect\<CR>") + call term_sendkeys(buf, ":cn\<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_64', {}) call term_sendkeys(buf, "\<C-U>\<Esc>") call StopVimInTerminal(buf)