Files
neovim/test/functional/ui/messages2_spec.lua
Sébastien Hoffmann 0ffd74ae85 fix(ui2): fix ruler on repeated messages
Problem: a long message is cropped to keep the ruler visible. But when
the message is repeated, it can be written over the ruler, while the
repetition indicator "(1)" is still placed just before the ruler, which
ends up somewhere inside the message.

Solution: currently, long messages are only cropped when setting the
'last' virttext. Make sure this happens after a repeated long message.
2026-07-11 18:44:42 +02:00

1221 lines
55 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 api, clear, command, exec_lua, feed = n.api, n.clear, n.command, n.exec_lua, n.feed
local msg_timeout = 400
local function set_msg_target_zero_ch()
exec_lua(function()
require('vim._core.ui2').enable({ msg = { targets = 'msg', msg = { timeout = msg_timeout } } })
vim.o.cmdheight = 0
end)
end
describe('messages2', function()
local screen
before_each(function()
clear()
screen = Screen.new()
screen:add_extra_attr_ids({
[100] = { foreground = Screen.colors.Magenta1, bold = true },
})
exec_lua(function()
require('vim._core.ui2').enable({})
end)
end)
after_each(function()
-- Since ui2 module lasts until Nvim exits, there may be unfinished timers.
-- Close unfinished timers to avoid 2s delay on exit with ASAN or TSAN.
exec_lua(function()
vim.uv.walk(function(handle)
if not handle:is_closing() then
handle:close()
end
end)
end)
end)
it('multiline messages and pager', function()
command('set ruler showcmd noshowmode | echo "foo\nbar"')
screen:expect([[
^ |
{1:~ }|*10
{3: }|
foo |
bar |
]])
feed('g')
screen:expect([[
^ |
{1:~ }|*10
{3: }|
foo |
bar g |
]])
feed('<lt>')
screen:expect([[
|
{1:~ }|*9
{3: }|
^foo |
bar |
1,1 All|
]])
-- Multiple messages in same event loop iteration are appended and shown in full.
feed([[q:echo "foo" | echo "bar\nbaz\n"->repeat(&lines)<CR>]])
screen:expect([[
^ |
{1:~ }|*5
{3: }|
foo |
bar |
baz |
bar |
baz |
bar |
baz [+23] |
]])
-- Any key press resizes the cmdline and updates the spill indicator.
feed('j')
screen:expect({ any = { '+29' } })
command('echo "foo"')
-- New message clears spill indicator.
screen:expect([[
^ |
{1:~ }|*12
foo 0,0-1 All|
]])
command('echo "foo"')
-- Ruler still positioned correctly after dupe message.
screen:expect([[
^ |
{1:~ }|*12
foo(1) 0,0-1 All|
]])
command('echo "foo"')
-- Dupe counter increases beyond 1
screen:expect([[
^ |
{1:~ }|*12
foo(2) 0,0-1 All|
]])
-- No error for ruler virt_text msg_row exceeding buffer length.
command([[map Q <cmd>echo "foo\nbar" <bar> ls<CR>]])
feed('Q')
screen:expect([[
^ |
{1:~ }|*9
{3: }|
foo |
bar |
1 %a "[No Name]" line 1 |
]])
feed('<C-L>')
screen:expect([[
^ |
{1:~ }|*12
0,0-1 All|
]])
-- g< shows messages from last command
feed('g<lt>')
screen:expect([[
|
{1:~ }|*8
{3: }|
^foo |
bar |
1 %a "[No Name]" line 1 |
1,1 All|
]])
-- edit_unputchar() does not clear already updated screen #34515.
feed('qix<Esc>dwi<C-r>')
screen:expect([[
{18:^"} |
{1:~ }|*12
^R 1,1 All|
]])
feed('-<Esc>')
screen:expect([[
^x |
{1:~ }|*12
1,1 All|
]])
-- Switching tabpage closes expanded cmdline #37659.
command('tabnew | echo "foo\nbar"')
screen:expect([[
{24: + [No Name] }{5: [No Name] }{2: }{24:X}|
^ |
{1:~ }|*9
{3: }|
foo |
bar |
]])
feed('gt')
screen:expect([[
{5: + [No Name] }{24: [No Name] }{2: }{24:X}|
^x |
{1:~ }|*11
foo [+1] 1,1 All|
]])
-- Don't enter the pager in insert mode.
command('tabonly | call nvim_echo([["foo\n"]]->repeat(&lines), 1, {}) | startinsert')
screen:expect([[
^x |
{1:~ }|*5
{3: }|
foo |*6
foo [+8] |
]])
feed('<CR>')
screen:expect([[
|
^x |
{1:~ }|*11
foo [+14] 2,1 All|
]])
feed('<BS><Esc>')
-- First multiline message expands cmdline, additional message updates spill indicator.
command('call nvim_echo([["foo\n"]]->repeat(&lines), 1, {}) | echo "bar"')
screen:expect([[
^x |
{1:~ }|*5
{3: }|
foo |*6
foo [+9] |
]])
-- Do enter the pager in normal mode.
command('nmap <Esc> <Cmd>fclose<CR>')
feed('<CR>')
screen:expect([[
^foo |
foo |*12
1,1 Top|
]])
-- Changing 'laststatus' reveals the global statusline with a pager height
-- exceeding the available lines: #38008.
command('set laststatus=3')
screen:expect([[
^foo |
foo |*11
{3:[Pager] 1,1 Top}|
|
]])
feed(':<C-F>')
screen:expect([[
x |
{1:~ }|*3
─────────────────────────────────────────────────────|
{1::}echo "foo" | echo "bar\nbaz\n"->repeat(&lines) |
{1::}^ |
{1:~ }|*5
{3:[Command Line] 2,0-1 All}|
|
]])
command('wincmd +')
screen:expect([[
x |
{1:~ }|*2
─────────────────────────────────────────────────────|
{1::}echo "foo" | echo "bar\nbaz\n"->repeat(&lines) |
{1::}^ |
{1:~ }|*6
{3:[Command Line] 2,0-1 All}|
|
]])
command('echo "foo"')
screen:expect([[
x |
{1:~ }|*2
─────────────────────────────────────────────────────|
{1::}echo "foo" | echo "bar\nbaz\n"->repeat(&lines) |
{1::}^ |
{1:~ }|*6
{3:[Command Line] 2,0-1 All}|
foo |
]])
-- <C-C> closes the cmdwin and drops back into pre-filled cmdline. The pager remains visible.
feed('<C-C>')
screen:expect([[
x |
{1:~ }|*11
{3:[Pager] 1,1 Top}|
{16::}^ |
]])
feed('<Esc><Esc>')
-- Can enter pager from cmdwin (use cmdwin.open() since "q" is consumed by the pager on_key
-- callback before nv_record can read it).
exec_lua([[require('vim._core.cmdwin').open(':')]])
screen:expect([[
x |
{1:~ }|*3
─────────────────────────────────────────────────────|
{1::}echo "foo" | echo "bar\nbaz\n"->repeat(&lines) |
{1::}^ |
{1:~ }|*5
{3:[Command Line] 2,0-1 All}|
|
]])
feed(':messages<CR>')
screen:expect([[
^foo |
foo |*4
{1:~ }|*7
{3:[Pager] 1,1 Top}|
{16::}{15:messages} |
]])
-- Cmdwin is restored after pager is closed.
feed('q')
screen:expect([[
x |
{1:~ }|*3
─────────────────────────────────────────────────────|
{1::}echo "foo" | echo "bar\nbaz\n"->repeat(&lines) |
{1::}messages |
{1::}^ |
{1:~ }|*4
{3:[Command Line] 3,0-1 All}|
{16::}{15:messages} |
]])
-- Configured maximum height.
command('quit | lua require("vim._core.ui2").enable({msg = {pager = {height = 2 } } })')
command('messages')
screen:expect([[
x |
{1:~ }|*8
{3: }|
^foo |
foo |
{3:[Pager] 1,1 Top}|
{16::}{15:messages} |
]])
end)
it('new buffer, window and options after closing a buffer or switching tabpage', function()
command('set nomodifiable | echom "foo" | messages')
screen:expect([[
|
{1:~ }|*10
{3: }|
^foo |
foo |
]])
command('bdelete | messages')
screen:expect_unchanged()
set_msg_target_zero_ch()
command('quit | echo "foo\nbar" | tabnew')
screen:expect([[
{24: [No Name] }{5: [No Name] }{2: }{24:X}|
^ |
{1:~ }|*10
{1:~ }{4:foo}|
{1:~ }{4:bar}|
]])
end)
it('screenclear and empty message clears messages', function()
command('echo "foo"')
screen:expect([[
^ |
{1:~ }|*12
foo |
]])
command('mode')
screen:expect([[
^ |
{1:~ }|*12
|
]])
command('echo "foo"')
screen:expect([[
^ |
{1:~ }|*12
foo |
]])
command('echo ""')
screen:expect([[
^ |
{1:~ }|*12
|
]])
-- A redraw indicates the start of messages in the cmdline, which empty should clear.
command('echo "foo" | redraw | echo "bar"')
screen:expect([[
^ |
{1:~ }|*12
bar |
]])
command('echo "foo" | redraw | echo ""')
screen:expect([[
^ |
{1:~ }|*12
|
]])
set_msg_target_zero_ch()
command('echo "foo"')
screen:expect([[
^ |
{1:~ }|*12
{1:~ }{4:foo}|
]])
command('mode')
screen:expect([[
^ |
{1:~ }|*13
]])
-- But not with target='msg'
command('echo "foo"')
screen:expect([[
^ |
{1:~ }|*12
{1:~ }{4:foo}|
]])
command('echo ""')
screen:expect_unchanged()
-- Or a screen resize
screen:try_resize(screen._width, screen._height - 1)
screen:expect([[
^ |
{1:~ }|*11
{1:~ }{4:foo}|
]])
-- Moved up when opening cmdline
feed(':')
screen:expect([[
|
{1:~ }|*10
{1:~ }{4:foo}|
{16::}^ |
]])
-- Highlighter disabled when message is moved to cmdline #34884
feed([[echo "bar\n"->repeat(&lines)<CR>]])
screen:expect([[
^ |
{1:~ }|*4
{3: }|
foo |
bar |*5
bar [+8] |
]])
end)
it('no prompt and newlines with Visual filter command #38273', function()
set_msg_target_zero_ch()
feed('V:w !printf foo<CR>')
screen:expect([[
^ |
{1:~ }|*12
{1:~ }{4:foo}|
]])
end)
it('empty kind after message that does not flush immediately', function()
command('echon "foo" | echo')
screen:expect([[
^ |
{1:~ }|*12
foo |
]])
end)
it("deleting buffer restores 'buftype'", function()
feed(':%bdelete<CR>')
screen:expect([[
^ |
{1:~ }|*12
5 buffers deleted |
]])
-- Would trigger changed dialog if 'buftype' was not restored.
command('%bdelete')
screen:expect_unchanged()
end)
it('showmode does not overwrite important messages', function()
command('set readonly')
feed('i')
screen:expect([[
^ |
{1:~ }|*12
{19:W10: Warning: Changing a readonly file} |
]])
feed('<Esc>Qi')
screen:expect([[
^ |
{1:~ }|*12
{9:E354: Invalid register name: '^@'} |
]])
end)
it('hit-enter prompt does not error for invalid window #35095', function()
command('echo "foo\nbar"')
screen:expect([[
^ |
{1:~ }|*10
{3: }|
foo |
bar |
]])
feed('<C-w>o')
screen:expect([[
^ |
{1:~ }|*12
foo [+1] |
]])
end)
it('entering cmdline below expanded messages', function()
command('echo "foo\n"->repeat(&lines)')
screen:expect([[
^ |
{1:~ }|*5
{3: }|
foo |*6
foo [+8] |
]])
-- Place cmdline below expanded messages: #37653, without "more" title #38481.
feed(':call setline(1, "foo")')
screen:expect([[
|
{1:~ }|*4
{3: }|
foo |*6
foo [+8] |
{16::}{15:call} {25:setline}{16:(}{26:1}{16:,} {26:"foo"}{16:)}^ |
]])
-- No message closes expanded cmdline and keeps the entered command.
feed('<CR>')
screen:expect([[
^foo |
{1:~ }|*12
{16::}{15:call} {25:setline}{16:(}{26:1}{16:,} {26:"foo"}{16:)} |
]])
-- If command emits another message we enter the pager to closely mimic useful UI1 behavior.
command('echo "foo\nbar"')
feed(':echo "baz"<CR>')
screen:expect([[
foo |
{1:~ }|*8
{3: }|
foo |
bar |
^baz |
{16::}{15:echo} {26:"baz"} |
]])
-- Subsequent typed commands are appended to the pager.
feed(':echo "typed append"<CR>')
screen:expect([[
foo |
{1:~ }|*7
{3: }|
foo |
bar |
baz |
^typed append |
{16::}{15:echo} {26:"typed append"} |
]])
-- Other messages that fit 'cmdheight' are not.
feed('n')
screen:expect([[
foo |
{1:~ }|*7
{3: }|
foo |
bar |
baz |
^typed append |
{9:E35: No previous regular expression} |
]])
-- Non-typed key doesn't dismiss expanded cmdline #39221
command('nnoremap b :ls!<cr>:b<space>')
feed('qb')
screen:expect([[
foo |
{1:~ }|*6
{3: }|
1 %a + "[No Name]" line 1 |
2u a "[Cmd]" line 0 |
3u a "[Dialog]" line 0 |
4u a "[Msg]" line 0 |
5u a "[Pager]" line 0 |
{16::}{15:b} ^ |
]])
end)
it('paging prompt dialog #35191', function()
screen:try_resize(71, screen._height)
-- Don't consume <Esc> when paging is not necessary.
feed(':call confirm("Ok?")<CR>')
screen:expect([[
|
{1:~ }|*10
{3: }|
{6:Ok?} |
{6:[O]k: }^ |
]])
feed('<Esc>')
screen:expect([[
^ |
{1:~ }|*12
|
]])
local top = [[
|
{1:~ }|*4
{3: }|
0 |
1 |
2 |
3 |
4 |
5 |
6 [+93] |
Type number and <Enter> or click with the mouse (q or empty cancels): ^ |
]]
feed(':call inputlist(range(100))<CR>')
screen:expect(top)
feed('<Down>')
screen:expect([[
|
{1:~ }|*4
{3: }|
1 [+1] |
2 |
3 |
4 |
5 |
6 |
7 [+92] |
Type number and <Enter> or click with the mouse (q or empty cancels): ^ |
]])
feed('<Up>')
screen:expect(top)
feed('<PageDown>')
screen:expect([[
|
{1:~ }|*4
{3: }|
5 [+5] |
6 |
7 |
8 |
9 |
10 |
11 [+88] |
Type number and <Enter> or click with the mouse (q or empty cancels): ^ |
]])
feed('<PageUp>')
screen:expect(top)
feed('<End>')
screen:expect([[
|
{1:~ }|*4
{3: }|
93 [+93] |
94 |
95 |
96 |
97 |
98 |
99 |
Type number and <Enter> or click with the mouse (q or empty cancels): ^ |
]])
-- No scrolling beyond end of buffer #36114
feed('<PageDown>')
screen:expect_unchanged()
feed('<Home>')
screen:expect(top)
end)
it('FileType is fired after default options are set', function()
n.exec([[
let g:set = {}
au FileType pager set nowrap
au OptionSet * let g:set[expand('<amatch>')] = g:set->get(expand('<amatch>'), 0) + 1
echom 'foo'->repeat(&columns)
messages
]])
screen:expect([[
|
{1:~ }|*10
{3: }|
^foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofo|
|
]])
t.eq(5, n.eval('g:set').filetype) -- still fires for 'filetype'
end)
it('Search highlights only apply to pager', function()
screen:add_extra_attr_ids({
[100] = { background = Screen.colors.Blue1, foreground = Screen.colors.Red },
[101] = { background = Screen.colors.Red1, foreground = Screen.colors.Blue1 },
})
command('hi MsgArea guifg=Red guibg=Blue')
command('hi Search guifg=Blue guibg=Red')
command('set hlsearch shortmess+=s')
feed('/foo<CR>')
screen:expect([[
^ |
{1:~ }|*12
{9:E486: Pattern not found: foo}{100: }|
]])
set_msg_target_zero_ch()
command('echo "foo"')
screen:expect([[
^ |
{1:~ }|*12
{1:~ }{4:foo}|
]])
feed('g<lt>')
screen:expect([[
|
{1:~ }|*11
{3: }|
{101:^foo}{100: }|
]])
end)
it(':echon appends message', function()
command([[echo 1 | echon 2]])
screen:expect([[
^ |
{1:~ }|*12
12 |
]])
feed('g<lt>')
screen:expect([[
|
{1:~ }|*10
{3: }|
^12 |
|
]])
feed([[q:echo 1 | echon 2 | echon 2 | echon 3<CR>]])
screen:expect([[
^ |
{1:~ }|*12
1223 |
]])
feed('g<lt>')
screen:expect([[
|
{1:~ }|*10
{3: }|
^1223 |
|
]])
end)
it('shows message from still running command', function()
exec_lua(function()
vim.schedule(function()
print('foo')
vim.fn.getchar()
end)
end)
screen:expect([[
^ |
{1:~ }|*12
foo |
]])
feed('<CR>')
-- Fast context is not determined by message kind #39666
exec_lua(function()
vim.schedule(function()
vim.api.nvim_echo({ { 'bar' } }, false, { kind = 'search_cmd' })
vim.fn.getchar()
end)
end)
screen:expect([[
^ |
{1:~ }|*12
bar |
]])
end)
it('properly formatted carriage return messages', function()
screen:try_resize(screen._width, 20)
command([[echon "\r" | echon "Hello" | echon " " | echon "World"]])
screen:expect([[
^ |
{1:~ }|*18
Hello World |
]])
exec_lua(function()
vim.api.nvim_echo({ { 'fooo\nbarbaz\n\nlol', 'statement' }, { '\rbar' } }, true, {})
vim.api.nvim_echo({ { 'foooooooo', 'statement' }, { 'baz\rb', 'error' } }, true, {})
vim.api.nvim_echo({ { 'fooobar', 'statement' }, { '\rbaz\n' } }, true, {})
vim.api.nvim_echo({ { 'fooobar', 'statement' }, { '\rbaz\rb', 'error' } }, true, {})
vim.api.nvim_echo({ { 'fooo\rbar', 'statement' }, { 'baz', 'error' } }, true, {})
end)
screen:expect([[
^ |
{1:~ }|*9
{3: }|
{15:fooo} |
{15:barbaz} |
|
bar |
{9:b}{15:oooooooo}{9:baz} |
baz{15:obar} |
|
{9:baz}{15:obar} |
{15:bar}{9:baz} |
]])
end)
it('can show message during textlock', function()
exec_lua(function()
_G.omnifunc = function()
print('x!')
vim.cmd.sleep('100m')
end
vim.bo.omnifunc = 'v:lua.omnifunc'
end)
feed('i<C-X>')
screen:expect([[
^ |
{1:~ }|*12
{5:-- ^X mode (^]^D^E^F^I^K^L^N^O^P^Rs^U^V^Y)} |
]])
feed('<C-O>')
screen:expect([[
^ |
{1:~ }|*12
x! |
]])
screen:expect([[
^ |
{1:~ }|*12
{5:-- Omni completion (^O^N^P) }{9:Pattern not found} |
]])
exec_lua(function()
vim.keymap.set('n', '<F1>', function()
print('i hate locks so much!!!!')
vim.cmd.messages()
end, { expr = true })
end)
feed('<Esc><F1>')
screen:expect([[
|
{1:~ }|*8
{3: }|
^x! |
x! |
i hate locks so much!!!! |*2
]])
end)
it('replace by message ID', function()
exec_lua(function()
assert(1 == vim.api.nvim_echo({ { 'foo' } }, true, {}))
assert(2 == vim.api.nvim_echo({ { 'bar\nbaz' } }, true, {}))
assert(3 == vim.api.nvim_echo({ { 'foo' } }, true, {}))
vim.keymap.set('n', 'Q', function()
vim.api.nvim_echo({ { 'Syntax', 23 }, { '\n - ', 0 }, { 'cCommentL', 439 } }, false, {})
end)
end)
screen:expect([[
^ |
{1:~ }|*8
{3: }|
foo |
bar |
baz |
foo |
]])
api.nvim_echo({ { 'foo' } }, true, { id = 2 })
screen:expect([[
^ |
{1:~ }|*9
{3: }|
foo |*3
]])
api.nvim_echo({ { 'bar\nbaz' } }, true, { id = 1 })
screen:expect([[
^ |
{1:~ }|*8
{3: }|
bar |
baz |
foo |*2
]])
-- Pressing a key immediately dismisses an expanded cmdline, and
-- replacing a multiline, multicolored message doesn't error due
-- to unnecessarily inserted lines #37994.
feed('Q')
screen:expect([[
^ |
{1:~ }|*10
{3: }|
{100:Syntax} |
- cCommentL |
]])
feed('Q')
screen:expect_unchanged(true)
feed('<C-L>') -- close expanded cmdline
set_msg_target_zero_ch()
api.nvim_echo({ { 'foo' } }, true, { id = 1 })
api.nvim_echo({ { 'bar\nbaz' } }, true, { id = 2 })
api.nvim_echo({ { 'foo' } }, true, { id = 3 })
screen:expect([[
^ |
{1:~ }|*9
{1:~ }{4:foo}|
{1:~ }{4:bar}|
{1:~ }{4:baz}|
{1:~ }{4:foo}|
]])
api.nvim_echo({ { 'foo' } }, true, { id = 2 })
screen:expect([[
^ |
{1:~ }|*10
{1:~ }{4:foo}|*3
]])
api.nvim_echo({ { 'f', 'Conceal' }, { 'oo\nbar' } }, true, { id = 3 })
screen:expect([[
^ |
{1:~ }|*9
{1:~ }{4:foo}|*2
{1:~ }{14:f}{4:oo}|
{1:~ }{4:bar}|
]])
-- No error expanding the cmdline when trying to copy over message span marks #37672.
screen:try_resize(screen._width, 6)
command('ls!')
screen:expect([[
^ |
{1:~ }|
{3: }|
foo |*2
{14:f}oo [+6] |
]])
feed('<Esc>')
screen:expect([[
^ |
{1:~ }|*5
]])
end)
it('while cmdline is open', function()
command('cnoremap <C-A> <Cmd>lua error("foo")<CR>')
feed(':echo "bar"<C-A>')
screen:expect([[
|
{1:~ }|*7
{3: }|
{9:E5108: Lua: [string ":lua"]:1: foo} |
{9:stack traceback:} |
{9: [C]: in function 'error'} |
{9: [string ":lua"]:1: in main chunk} |
{16::}{15:echo} {26:"bar"}^ |
]])
feed('<CR>')
screen:expect([[
^ |
{1:~ }|*12
bar |
]])
set_msg_target_zero_ch()
feed([[:call confirm("foo\nbar")<C-A>]])
screen:expect([[
|
{1:~ }|*8
{1:~ }{9:E5108: Lua: [string ":lua"]:1: foo}{4: }|
{1:~ }{9:stack traceback:}{4: }|
{1:~ }{9: [C]: in function 'error'}{4: }|
{1:~ }{9: [string ":lua"]:1: in main chunk}|
{16::}{15:call} {25:confirm}{16:(}{26:"foo\nbar"}{16:)}^ |
]])
feed('<CR>')
screen:expect([[
|
{1:~ }|*8
{1:~ }{9:E5108: Lua: [string ":lua"]:1: foo}{4: }|
{3: }|
{6:foo} |
{6:bar} |
{6:[O]k: }^ |
]])
end)
it('no search_cmd with cmdheight=0', function()
set_msg_target_zero_ch()
feed('ifoo<Esc>?foo<CR>')
screen:expect([[
{10:^foo} |
{1:~ }|*13
]])
end)
it('closed msg window timer removes empty lines', function()
set_msg_target_zero_ch()
command('echo "foo" | echo "bar\n"')
screen:expect([[
^ |
{1:~ }|*10
{1:~ }{4:foo}|
{1:~ }{4:bar}|
{1:~ }{4: }|
]])
command('fclose!')
screen:sleep(msg_timeout + 50)
command('echo "baz"')
screen:expect([[
^ |
{1:~ }|*12
{1:~ }{4:baz}|
]])
-- Last message line is at bottom of window after closing it.
screen:try_resize(screen._width, 8)
command('mode | echo "1\n" | echo "2\n" | echo "3\n" | echo "4\n"')
screen:expect([[
^ |
{1:~ }|*3
{1:~ }{4:3}|
{1:~ }{4: }|
{1:~ }{4:4}|
{1:~ }{4: }|
]])
command('fclose!')
screen:expect([[
^ |
{1:~ }|*7
]])
command('echo "5\n"')
screen:expect([[
^ |
{1:~ }|*3
{1:~ }{4:4}|
{1:~ }{4: }|
{1:~ }{4:5}|
{1:~ }{4: }|
]])
end)
it('configured targets per kind', function()
exec_lua(function()
local targets = { echo = 'msg', list_cmd = 'pager', bufwrite = 'cmd', lua_print = 'cmd' }
require('vim._core.ui2').enable({ msg = { targets = targets } })
print('foo') -- "lua_print" kind goes to cmd
vim.cmd.echo('"bar"') -- "echo" kind goes to msg
vim.cmd.highlight('VisualNC') -- "list_cmd" kind goes to pager
end)
screen:expect([[
|
{1:~ }|*10
{3: }|
^VisualNC xxx cleared {4:bar}|
foo |
]])
command('hi VisualNC') -- cursor moved to last message in pager
screen:expect([[
|
{1:~ }|*9
{3: }|
VisualNC xxx cleared |
^VisualNC xxx cleared {4:bar}|
foo |
]])
-- Duplicate indicator in msg and cmd target simultaneously
command('close | echo "bar" | lua print("foo")')
screen:expect([[
^ |
{1:~ }|*11
{1:~ }{4:bar(1)}|
foo(1) |
]])
finally(function()
os.remove('Xfile')
end)
-- Route bufwrite as a pattern to the message ID #39341
command('edit Xfile | write')
screen:expect([[
^ |
{1:~ }|*11
{1:~ }{4:bar(1)}|
"Xfile" [New] 0L, 0B written |
]])
end)
it('message survives after closing tabpage without error #39055', function()
set_msg_target_zero_ch()
command('tabnew')
command('echo "hello"')
screen:expect([[
{24: [No Name] }{5: [No Name] }{2: }{24:X}|
^ |
{1:~ }|*11
{1:~ }{4:hello}|
]])
command('quit!')
screen:expect([[
^ |
{1:~ }|*12
{1:~ }{4:hello}|
]])
end)
it('no crash for resized grid during redraw #39075', function()
exec_lua(function()
vim.api.nvim_set_decoration_provider(vim.api.nvim_create_namespace(''), {
on_win = function()
print('\n')
end,
})
end)
feed(':')
screen:expect([[
|
{1:~ }|*12
{16::}^ |
]])
feed('f')
screen:expect([[
|
{1:~ }|*9
{3: }|
|*2
{16::}{15:f}^ |
]])
end)
it('configured cmd window height prevents expanded message #39375', function()
exec_lua('require("vim._core.ui2").enable({ msg = { cmd = { height = 1 } } })')
command('echo "foo\nbar"')
screen:expect([[
^ |
{1:~ }|*12
foo [+1] |
]])
end)
it('search count is cleared', function()
command('set ruler showcmd shortmess-=S | call setline(1, ["foo", "bar"])')
feed('/foo<CR>')
local search_count = [[
{10:^foo} |
bar |
{1:~ }|*11
/foo W [1/1] 1,1 All|
]]
screen:expect(search_count)
feed('<C-L>j')
screen:expect([[
{10:foo} |
^bar |
{1:~ }|*11
2,1 All|
]])
feed('n')
screen:expect(search_count)
command('echo "-"->repeat(&columns)')
screen:expect([[
{10:^foo} |
bar |
{1:~ }|*11
-----------------------------------1,1 All|
]])
end)
it('crops long messages to make place for ruler', function()
command('set noruler | echo "-"->repeat(&columns)')
screen:expect([[
^ |
{1:~ }|*12
-----------------------------------------------------|
]])
feed('<C-L>')
command('set ruler showcmd | echo "-"->repeat(&columns)')
screen:expect([[
^ |
{1:~ }|*12
-----------------------------------0,0-1 All|
]])
feed('<C-L>')
-- message with multibyte characters
command('echo "∙"->repeat(&columns)')
screen:expect([[
^ |
{1:~ }|*12
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙0,0-1 All|
]])
feed('<C-L>')
-- message with multicell characters
command('echo "-".."🙂"->repeat(&columns/2-1)')
screen:expect([[
^ |
{1:~ }|*12
-🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂0,0-1 All|
]])
feed('<C-L>')
command('echo "🙂"->repeat(&columns/2)')
screen:expect([[
^ |
{1:~ }|*12
🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂 0,0-1 All|
]])
feed('<C-L>')
-- when cmdheight>1, only the last line is cropped
command('set cmdheight=2 | redraw | echo "-"->repeat(2*&columns)')
screen:expect([[
^ |
{1:~ }|*11
-----------------------------------------------------|
-----------------------------------0,0-1 All|
]])
feed('<C-L>')
command('set cmdheight=2 showbreak=> | redraw | echo "-"->repeat(2*&columns-1)')
screen:expect([[
^ |
{1:~ }|*11
-----------------------------------------------------|
{1:>}----------------------------------0,0-1 All|
]])
command('set showbreak&')
feed('<C-L>')
-- ruler is not shown after dismissing expanded cmdline with key press -> no need to crop
command('set cmdheight=1 | redraw | echo "-"->repeat(2*&columns)')
feed('k')
screen:expect([[
^ |
{1:~ }|*12
-----------------------------------------------------|
]])
feed('<C-L>')
-- messages are deferred until showcmd is cleared -> no need to crop for showcmd
command('lua vim.defer_fn(function() vim.cmd[[echo "-"->repeat(&columns)]] end, 50)')
feed('g')
screen:expect([[
^ |
{1:~ }|*12
g 0,0-1 All|
]])
screen:sleep(100)
feed('<Esc>')
screen:expect([[
^ |
{1:~ }|*12
-----------------------------------0,0-1 All|
]])
feed('<C-L>')
-- message with repetition indicator keeps ruler intact
command('echo "-"->repeat(&columns)')
command('echo "-"->repeat(&columns)')
screen:expect([[
^ |
{1:~ }|*12
--------------------------------(1)0,0-1 All|
]])
feed('<C-L>')
-- when the ruler is configured smaller, there is more space for messages
command('set rulerformat=%l | redraw | echo "-"->repeat(&columns)')
screen:expect([[
^ |
{1:~ }|*12
----------------------------------------------------0|
]])
end)
end)