mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 11:28:22 +00:00

This is the first installment of a multi-PR series significantly refactoring how highlights are being specified. The end goal is to have a base set of 20 ish most common highlights, and then specific files only need to add more groups to that as needed. As a complicating factor, we also want to migrate to the new default color scheme eventually. But by sharing a base set, that future PR will hopefully be a lot smaller since a lot of tests will be migrated just simply by updating the base set in place. As a first step, fix the anti-pattern than Screen defaults to ignoring highlights. Highlights are integral part of the screen state, not something "extra" which we only test "sometimes". For now, we still allow opt-out via the intentionally ugly screen._default_attr_ids = nil The end goal is to get rid of all of these eventually (which will be easier as part of the color scheme migration)
339 lines
10 KiB
Lua
339 lines
10 KiB
Lua
local helpers = require('test.functional.helpers')(after_each)
|
|
local eq = helpers.eq
|
|
local eval = helpers.eval
|
|
local clear = helpers.clear
|
|
local source = helpers.source
|
|
local exc_exec = helpers.exc_exec
|
|
local pcall_err = helpers.pcall_err
|
|
local fn = helpers.fn
|
|
local Screen = require('test.functional.ui.screen')
|
|
local command = helpers.command
|
|
local feed = helpers.feed
|
|
local is_os = helpers.is_os
|
|
|
|
describe('execute()', function()
|
|
before_each(clear)
|
|
|
|
it('captures the same result as :redir', function()
|
|
command([[
|
|
echomsg 'foo 1'
|
|
echomsg 'foo 2'
|
|
redir => g:__redir_output
|
|
silent! messages
|
|
redir END
|
|
]])
|
|
eq(eval('g:__redir_output'), fn.execute('messages'))
|
|
end)
|
|
|
|
it('captures the concatenated outputs of a List of commands', function()
|
|
eq('foobar', fn.execute({ 'echon "foo"', 'echon "bar"' }))
|
|
eq('\nfoo\nbar', fn.execute({ 'echo "foo"', 'echo "bar"' }))
|
|
end)
|
|
|
|
it('supports nested execute("execute(...)")', function()
|
|
eq('42', fn.execute([[echon execute("echon execute('echon 42')")]]))
|
|
end)
|
|
|
|
it('supports nested :redir to a variable', function()
|
|
source([[
|
|
function! g:Foo()
|
|
let a = ''
|
|
redir => a
|
|
silent echon "foo"
|
|
redir END
|
|
return a
|
|
endfunction
|
|
function! g:Bar()
|
|
let a = ''
|
|
redir => a
|
|
silent echon "bar1"
|
|
call g:Foo()
|
|
silent echon "bar2"
|
|
redir END
|
|
silent echon "bar3"
|
|
return a
|
|
endfunction
|
|
]])
|
|
eq('top1bar1foobar2bar3', fn.execute('echon "top1"|call g:Bar()'))
|
|
end)
|
|
|
|
it('supports nested :redir to a register', function()
|
|
source([[
|
|
let @a = ''
|
|
function! g:Foo()
|
|
redir @a>>
|
|
silent echon "foo"
|
|
redir END
|
|
return @a
|
|
endfunction
|
|
function! g:Bar()
|
|
redir @a>>
|
|
silent echon "bar1"
|
|
call g:Foo()
|
|
silent echon "bar2"
|
|
redir END
|
|
silent echon "bar3"
|
|
return @a
|
|
endfunction
|
|
]])
|
|
eq('top1bar1foobar2bar3', fn.execute('echon "top1"|call g:Bar()'))
|
|
-- :redir itself doesn't nest, so the redirection ends in g:Foo
|
|
eq('bar1foo', eval('@a'))
|
|
end)
|
|
|
|
it('captures a transformed string', function()
|
|
eq('^A', fn.execute('echon "\\<C-a>"'))
|
|
end)
|
|
|
|
it('returns empty string if the argument list is empty', function()
|
|
eq('', fn.execute({}))
|
|
eq(0, exc_exec('let g:ret = execute(v:_null_list)'))
|
|
eq('', eval('g:ret'))
|
|
end)
|
|
|
|
it('captures errors', function()
|
|
local ret
|
|
ret = exc_exec('call execute(v:_null_dict)')
|
|
eq('Vim(call):E731: Using a Dictionary as a String', ret)
|
|
ret = exc_exec('call execute(function("tr"))')
|
|
eq('Vim(call):E729: Using a Funcref as a String', ret)
|
|
ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])')
|
|
eq('Vim:E731: Using a Dictionary as a String', ret)
|
|
ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])')
|
|
eq('Vim:E729: Using a Funcref as a String', ret)
|
|
end)
|
|
|
|
it('captures output with highlights', function()
|
|
eq(
|
|
'\nErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red',
|
|
eval('execute("hi ErrorMsg")')
|
|
)
|
|
end)
|
|
|
|
it('does not corrupt the command display #5422', function()
|
|
local screen = Screen.new(70, 7)
|
|
screen:attach()
|
|
feed(':echo execute("hi ErrorMsg")<CR>')
|
|
screen:expect(
|
|
[[
|
|
|
|
|
{1:~ }|*2
|
|
{2: }|
|
|
|
|
|
ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red |
|
|
{3:Press ENTER or type command to continue}^ |
|
|
]],
|
|
{
|
|
[1] = { bold = true, foreground = Screen.colors.Blue1 },
|
|
[2] = { bold = true, reverse = true },
|
|
[3] = { bold = true, foreground = Screen.colors.SeaGreen4 },
|
|
}
|
|
)
|
|
feed('<CR>')
|
|
end)
|
|
|
|
it('places cursor correctly #6035', function()
|
|
local screen = Screen.new(40, 6)
|
|
screen:attach()
|
|
source([=[
|
|
" test 1: non-silenced output goes as usual
|
|
function! Test1()
|
|
echo 1234
|
|
let x = execute('echon "abcdef"', '')
|
|
echon 'ABCD'
|
|
endfunction
|
|
|
|
" test 2: silenced output does not affect ui
|
|
function! Test2()
|
|
echo 1234
|
|
let x = execute('echon "abcdef"', 'silent')
|
|
echon 'ABCD'
|
|
endfunction
|
|
|
|
" test 3: silenced! error does not affect ui
|
|
function! Test3()
|
|
echo 1234
|
|
let x = execute('echoerr "abcdef"', 'silent!')
|
|
echon 'ABCDXZYZ'
|
|
endfunction
|
|
|
|
" test 4: silenced echoerr goes as usual
|
|
" bug here
|
|
function! Test4()
|
|
echo 1234
|
|
let x = execute('echoerr "abcdef"', 'silent')
|
|
echon 'ABCD'
|
|
endfunction
|
|
|
|
" test 5: silenced! echoerr does not affect ui
|
|
function! Test5()
|
|
echo 1234
|
|
let x = execute('echoerr "abcdef"', 'silent!')
|
|
echon 'ABCD'
|
|
endfunction
|
|
|
|
" test 6: silenced error goes as usual
|
|
function! Test6()
|
|
echo 1234
|
|
let x = execute('echo undefined', 'silent')
|
|
echon 'ABCD'
|
|
endfunction
|
|
|
|
" test 7: existing error does not mess the result
|
|
function! Test7()
|
|
" display from Test6() is still visible
|
|
" why does the "abcdef" goes into a newline
|
|
let x = execute('echon "abcdef"', '')
|
|
echon 'ABCD'
|
|
endfunction
|
|
]=])
|
|
|
|
feed([[:call Test1()<cr>]])
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*4
|
|
ABCD |
|
|
]])
|
|
|
|
feed([[:call Test2()<cr>]])
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*4
|
|
1234ABCD |
|
|
]])
|
|
|
|
feed([[:call Test3()<cr>]])
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*4
|
|
1234ABCDXZYZ |
|
|
]])
|
|
|
|
feed([[:call Test4()<cr>]])
|
|
-- unexpected: need to fix
|
|
-- echoerr does not set did_emsg
|
|
-- "ef" was overwritten since msg_col was recovered wrongly
|
|
screen:expect([[
|
|
1234 |
|
|
{9:Error detected while processing function}|
|
|
{9: Test4:} |
|
|
{8:line 2:} |
|
|
{9:abcd}ABCD |
|
|
{6:Press ENTER or type command to continue}^ |
|
|
]])
|
|
|
|
feed([[<cr>]]) -- to clear screen
|
|
feed([[:call Test5()<cr>]])
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*4
|
|
1234ABCD |
|
|
]])
|
|
|
|
feed([[:call Test6()<cr>]])
|
|
screen:expect([[
|
|
|
|
|
{9:Error detected while processing function}|
|
|
{9: Test6:} |
|
|
{8:line 2:} |
|
|
{9:E121}ABCD |
|
|
{6:Press ENTER or type command to continue}^ |
|
|
]])
|
|
|
|
feed([[:call Test7()<cr>]])
|
|
screen:expect([[
|
|
{9:Error detected while processing function}|
|
|
{9: Test6:} |
|
|
{8:line 2:} |
|
|
{9:E121}ABCD |
|
|
ABCD |
|
|
{6:Press ENTER or type command to continue}^ |
|
|
]])
|
|
end)
|
|
|
|
-- This deviates from vim behavior, but is consistent
|
|
-- with how nvim currently displays the output.
|
|
it('captures shell-command output', function()
|
|
local win_lf = is_os('win') and '\13' or ''
|
|
eq('\n:!echo foo\r\n\nfoo' .. win_lf .. '\n', fn.execute('!echo foo'))
|
|
end)
|
|
|
|
describe('{silent} argument', function()
|
|
it('captures & displays output for ""', function()
|
|
local screen = Screen.new(40, 5)
|
|
screen:attach()
|
|
command('let g:mes = execute("echon 42", "")')
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*3
|
|
42 |
|
|
]])
|
|
eq('42', eval('g:mes'))
|
|
end)
|
|
|
|
it('gives E493 instead of prompting on backwards range for ""', function()
|
|
command('split')
|
|
eq(
|
|
'Vim(windo):E493: Backwards range given: 2,1windo echo',
|
|
pcall_err(fn.execute, '2,1windo echo', '')
|
|
)
|
|
eq(
|
|
'Vim(windo):E493: Backwards range given: 2,1windo echo',
|
|
pcall_err(fn.execute, { '2,1windo echo' }, '')
|
|
)
|
|
end)
|
|
|
|
it('captures but does not display output for "silent"', function()
|
|
local screen = Screen.new(40, 5)
|
|
screen:attach()
|
|
command('let g:mes = execute("echon 42")')
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
eq('42', eval('g:mes'))
|
|
|
|
command('let g:mes = execute("echon 13", "silent")')
|
|
screen:expect {
|
|
grid = [[
|
|
^ |
|
|
{1:~ }|*3
|
|
|
|
|
]],
|
|
unchanged = true,
|
|
}
|
|
eq('13', eval('g:mes'))
|
|
end)
|
|
|
|
it('suppresses errors for "silent!"', function()
|
|
eq(0, exc_exec('let g:mes = execute(0.0, "silent!")'))
|
|
eq('', eval('g:mes'))
|
|
|
|
eq(0, exc_exec('let g:mes = execute("echon add(1, 1)", "silent!")'))
|
|
eq('1', eval('g:mes'))
|
|
|
|
eq(0, exc_exec('let g:mes = execute(["echon 42", "echon add(1, 1)"], "silent!")'))
|
|
eq('421', eval('g:mes'))
|
|
end)
|
|
|
|
it('propagates errors for "" and "silent"', function()
|
|
local ret
|
|
ret = exc_exec('call execute(v:_null_dict, "silent")')
|
|
eq('Vim(call):E731: Using a Dictionary as a String', ret)
|
|
|
|
ret = exc_exec('call execute("echo add(1, 1)", "")')
|
|
eq('Vim(echo):E897: List or Blob required', ret)
|
|
|
|
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")')
|
|
eq('Vim(echo):E897: List or Blob required', ret)
|
|
|
|
ret = exc_exec('call execute("echo add(1, 1)", "silent")')
|
|
eq('Vim(echo):E897: List or Blob required', ret)
|
|
|
|
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")')
|
|
eq('Vim(echo):E897: List or Blob required', ret)
|
|
end)
|
|
end)
|
|
end)
|