Files
neovim/test/functional/ui/cmdline2_spec.lua
zeertzjq 749e0a06c2 vim-patch:9.2.1031: 'wildmode' list:full does not show 'wildmenu'
Problem:  With 'wildmode' set to list:full the matches are listed but the
          wildmenu is not shown, although it is "full" that starts
          wildmenu mode (zeertzjq).
Solution: List the matches and show the menu, as the two behaviors in
          the same phase ask for.  The menu is left to the phases that
          ask for it, so that "list" on its own still only lists
          (Hirohito Higashi).

fixes:  vim/vim#21196
closes: vim/vim#21205

fa1ddffcce

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
2026-09-02 09:51:47 +08:00

379 lines
15 KiB
Lua

-- Tests for (protocol-driven) ui2, intended to replace the legacy message grid UI.
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local describe, it, before_each = t.describe, t.it, t.before_each
local clear, exec, exec_lua, feed = n.clear, n.exec, n.exec_lua, n.feed
describe('cmdline2', function()
local screen
before_each(function()
clear()
screen = Screen.new()
screen:add_extra_attr_ids({
[100] = { foreground = Screen.colors.Magenta1, bold = true },
[101] = { background = Screen.colors.Yellow, foreground = Screen.colors.Grey0 },
[102] = { background = Screen.colors.Cyan1, foreground = Screen.colors.SlateBlue },
})
exec_lua(function()
require('vim._core.ui2').enable({})
end)
end)
it("no crash for invalid grid after 'cmdheight' OptionSet", function()
exec('tabnew | tabprev')
feed(':set ch=0')
screen:expect([[
{5: [No Name] }{24: [No Name] }{2: }{24:X}|
|
{1:~ }|*11
{16::}{15:set} {16:ch}{15:=}0^ |
]])
feed('<CR>')
exec('tabnext')
screen:expect([[
{24: [No Name] }{5: [No Name] }{2: }{24:X}|
^ |
{1:~ }|*11
{16::}{15:set} {16:ch}{15:=}0 |
]])
exec('tabnext')
screen:expect([[
{5: [No Name] }{24: [No Name] }{2: }{24:X}|
^ |
{1:~ }|*12
]])
n.assert_alive()
end)
it("redraw does not clear 'incsearch' highlight with conceal", function()
exec('call setline(1, ["foo", "foobar"]) | set conceallevel=1 concealcursor=c')
feed('/foo')
screen:expect([[
{10:foo} |
{2:foo}bar |
{1:~ }|*11
/foo^ |
]])
end)
it('block mode', function()
feed(':if 1<CR>')
screen:expect([[
|
{1:~ }|*11
{16::}{15:if} {26:1} |
{16::} ^ |
]])
feed('echo "foo"')
screen:expect([[
|
{1:~ }|*11
{16::}{15:if} {26:1} |
{16::} {15:echo} {26:"foo"}^ |
]])
feed('<CR>')
screen:expect([[
|
{1:~ }|*9
{16::}{15:if} {26:1} |
{16::} {15:echo} {26:"foo"} |
foo |
{16::} ^ |
]])
feed([[echo input("foo\nbar:")<CR>]])
screen:expect([[
|
{1:~ }|*7
{16::}{15:if} {26:1} |
{16::} {15:echo} {26:"foo"} |
foo |
{16::} {15:echo} {25:input}{16:(}{26:"foo\nbar:"}{16:)} |
foo |
bar:^ |
]])
feed('baz')
screen:expect([[
|
{1:~ }|*7
{16::}{15:if} {26:1} |
{16::} {15:echo} {26:"foo"} |
foo |
{16::} {15:echo} {25:input}{16:(}{26:"foo\nbar:"}{16:)} |
foo |
bar:baz^ |
]])
feed('<CR>')
screen:expect([[
|
{1:~ }|*5
{16::}{15:if} {26:1} |
{16::} {15:echo} {26:"foo"} |
foo |
{16::} {15:echo} {25:input}{16:(}{26:"foo\nbar:"}{16:)} |
foo |
bar:baz |
baz |
{16::} ^ |
]])
feed('endif')
screen:expect([[
|
{1:~ }|*5
{16::}{15:if} {26:1} |
{16::} {15:echo} {26:"foo"} |
foo |
{16::} {15:echo} {25:input}{16:(}{26:"foo\nbar:"}{16:)} |
foo |
bar:baz |
baz |
{16::} {15:endif}^ |
]])
feed('<CR>')
screen:expect([[
^ |
{1:~ }|*12
|
]])
end)
it('handles empty prompt', function()
feed(":call input('')<CR>")
screen:expect([[
|
{1:~ }|*12
^ |
]])
-- Message moved to dialog with empty prompt
feed("<CR>:echo 'foo' | call input('')<CR>")
screen:expect([[
|
{1:~ }|*10
{3: }|
foo |
^ |
]])
end)
it('highlights after deleting buffer', function()
feed(':sil %bw!<CR>:call foo()')
screen:expect([[
|
{1:~ }|*12
{16::}{15:call} {25:foo}{16:()}^ |
]])
end)
it('can change cmdline buffer during textlock', function()
exec([[
func Foo(a, b)
redrawstatus!
endfunc
set wildoptions=pum findfunc=Foo wildmode=noselect:lastused,full
au CmdlineChanged * call wildtrigger()
]])
feed(':find ')
screen:expect([[
|
{1:~ }|*12
{16::}{15:find} ^ |
]])
t.eq("E1514: 'findfunc' did not return a List type", n.eval('v:errmsg'))
end)
it('substitution match, empty message does not clear active cmdline', function()
exec('call setline(1, "foo")')
feed(':s/f')
screen:expect([[
{10:f}oo |
{1:~ }|*12
{16::}{15:s}{16:/f^ } |
]])
feed('<Esc>:foo')
screen:expect([[
foo |
{1:~ }|*12
{16::}{15:foo}^ |
]])
exec('echo')
screen:expect_unchanged(true)
end)
it('keeps the confirmation match highlighted with nohlsearch #41039', function()
exec('call setline(1, "test test") | set nohlsearch')
feed('/te<CR>')
screen:expect([[
test ^test |
{1:~ }|*12
/te |
]])
feed(':%s/te/t/gc<CR>')
screen:expect([[
{2:te}st test |
{1:~ }|*11
{6:replace with t? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll}|
{6: up(^E)/down(^Y)}^ |
]])
end)
it('dialog position is adjusted for toggled non-pum wildmenu', function()
exec([[
set wildmode=list:full,full wildoptions-=pum
func Foo()
endf
func Fooo()
endf
]])
feed(':call Fo<C-Z>')
screen:expect([[
|
{1:~ }|*8
{3: }|
Foo() Fooo() |
|
{101:Foo()}{3: Fooo() }|
{16::}{15:call} {25:Foo}{16:()}^ |
]])
feed('<BS><BS>')
exec('set wildoptions+=pum laststatus=2')
screen:expect([[
|
{1:~ }|*9
{3: }|
Foo() Fooo() |
|
{16::}{15:call} Foo^ |
]])
feed('<C-Z>')
screen:expect([[
|
{1:~ }|*9
{3: }|
Foo(){12: Foo() } |
{4: Fooo() } |
{16::}{15:call} {25:Foo}{16:()}^ |
]])
end)
it('updated after setcmdline() #38764', function()
-- Also check that command-preview is updated.
exec('call setline(1, "foo")')
feed(':%s/foo')
screen:expect([[
{10:foo} |
{1:~ }|*12
{16::}%{15:s}{16:/foo^ } |
]])
exec_lua(function()
_G.events = {}
vim.api.nvim_create_autocmd({ 'CmdlineChanged', 'CursorMovedC' }, {
callback = function(ev)
_G.events[ev.event] = (_G.events[ev.event] or 0) + 1
end,
})
end)
exec('call setcmdline("%s/fo")')
screen:expect([[
{10:fo}o |
{1:~ }|*12
{16::}%{15:s}{16:/fo^ } |
]])
t.eq({ CmdlineChanged = 1, CursorMovedC = 1 }, exec_lua('return _G.events'))
end)
it("no 'incsearch' recursion with 'verbose' regex message", function()
exec('set verbose=1')
feed([[:%s/.\{//}]])
screen:expect([[
|
{1:~ }|*9
{3: }|
Switching to backtracking RE engine for pattern: .\{ |*2
{16::}%{15:s}{16:/.\{//}^ } |
]])
end)
it('is empty after backspace', function()
feed(':')
screen:expect([[
|
{1:~ }|*12
{16::}^ |
]])
feed('<BS>')
screen:expect([[
^ |
{1:~ }|*12
|
]])
end)
it('matchparen highlights', function()
exec('source $VIMRUNTIME/plugin/matchparen.lua')
feed(':call foo(bar())')
screen:expect([[
|
{1:~ }|*12
{16::}{15:call} {25:foo}{102:(}{25:bar}{16:()}{102:)}^ |
]])
feed('<Left>')
screen:expect([[
|
{1:~ }|*12
{16::}{15:call} {25:foo}{16:(}{25:bar}{102:()}{16:^)} |
]])
feed('<Right><BS><BS>')
screen:expect([[
|
{1:~ }|*12
{16::}{15:call} {25:foo}{16:(}{25:bar}{16:(}^ |
]])
end)
it("doesn't interfere with :$q", function()
exec('split b')
exec('wincmd J')
exec('wincmd p')
exec('$q') -- this would close a ui2 window, not the second visible window
local nwins = vim.fn.winnr('$')
assert(nwins == 1)
end)
end)
describe('cmdline2', function()
it('resizing during startup shows confirm prompt #36439', function()
clear({
args = {
'--clean',
'+lua require("vim._core.ui2").enable({})',
"+call feedkeys(':')",
},
})
local screen = Screen.new()
feed('call confirm("Ok?")<CR>')
screen:try_resize(screen._width + 1, screen._height)
screen:expect([[
|*10
{3: }|
|
{6:Ok?} |
{6:[O]k: }^ |
]])
-- And resizing the next event loop iteration also works.
feed('k')
screen:try_resize(screen._width, screen._height + 1)
screen:expect([[
|*11
{3: }|
|
{6:Ok?} |
{6:[O]k: }^ |
]])
end)
end)