mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 21:25:04 -07:00
e61228a214
Before calling "attach" a screen object is just a dummy container for (row, col) values whose purpose is to be sent as part of the "attach" function call anyway. Just create the screen in an attached state directly. Keep the complete (row, col, options) config together. It is still completely valid to later detach and re-attach as needed, including to another session.
269 lines
7.8 KiB
Lua
269 lines
7.8 KiB
Lua
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
local Screen = require('test.functional.ui.screen')
|
|
|
|
local feed, eq, eval, ok = n.feed, t.eq, n.eval, t.ok
|
|
local source, async_meths, run = n.source, n.async_meths, n.run
|
|
local clear, command, fn = n.clear, n.command, n.fn
|
|
local exc_exec = n.exc_exec
|
|
local api = n.api
|
|
local load_adjust = n.load_adjust
|
|
local retry = t.retry
|
|
|
|
describe('timers', function()
|
|
before_each(function()
|
|
clear()
|
|
source([[
|
|
let g:val = 0
|
|
func MyHandler(timer)
|
|
let g:val += 1
|
|
endfunc
|
|
]])
|
|
end)
|
|
|
|
it('works one-shot', function()
|
|
eq(0, eval("[timer_start(10, 'MyHandler'), g:val][1]"))
|
|
run(nil, nil, nil, load_adjust(100))
|
|
eq(1, eval('g:val'))
|
|
end)
|
|
|
|
it('works one-shot when repeat=0', function()
|
|
eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 0}), g:val][1]"))
|
|
run(nil, nil, nil, load_adjust(100))
|
|
eq(1, eval('g:val'))
|
|
end)
|
|
|
|
it('works with repeat two', function()
|
|
eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]"))
|
|
run(nil, nil, nil, load_adjust(20))
|
|
retry(nil, load_adjust(300), function()
|
|
eq(2, eval('g:val'))
|
|
end)
|
|
end)
|
|
|
|
it('are triggered during sleep', function()
|
|
source([[
|
|
let g:val = -1
|
|
func! MyHandler(timer)
|
|
if g:val >= 0
|
|
let g:val += 1
|
|
if g:val == 2
|
|
call timer_stop(a:timer)
|
|
endif
|
|
endif
|
|
endfunc
|
|
]])
|
|
eval("timer_start(10, 'MyHandler', {'repeat': -1})")
|
|
async_meths.nvim_command('sleep 10')
|
|
eq(-1, eval('g:val')) -- timer did nothing yet.
|
|
async_meths.nvim_command('let g:val = 0')
|
|
run(nil, nil, nil, load_adjust(20))
|
|
retry(nil, nil, function()
|
|
eq(2, eval('g:val'))
|
|
end)
|
|
end)
|
|
|
|
it('works with zero timeout', function()
|
|
-- timer_start does still not invoke the callback immediately
|
|
eq(0, eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]"))
|
|
retry(nil, nil, function()
|
|
eq(1000, eval('g:val'))
|
|
end)
|
|
end)
|
|
|
|
it('can be started during sleep', function()
|
|
async_meths.nvim_command('sleep 10')
|
|
-- this also tests that remote requests works during sleep
|
|
eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]"))
|
|
run(nil, nil, nil, load_adjust(20))
|
|
retry(nil, load_adjust(300), function()
|
|
eq(2, eval('g:val'))
|
|
end)
|
|
end)
|
|
|
|
it('are paused when event processing is disabled', function()
|
|
command("call timer_start(5, 'MyHandler', {'repeat': -1})")
|
|
run(nil, nil, nil, load_adjust(10))
|
|
local count = eval('g:val')
|
|
-- shows two line error message and thus invokes the return prompt.
|
|
-- if we start to allow event processing here, we need to change this test.
|
|
feed(':throw "fatal error"<CR>')
|
|
run(nil, nil, nil, load_adjust(30))
|
|
feed('<cr>')
|
|
local diff = eval('g:val') - count
|
|
assert(0 <= diff and diff <= 4, 'expected (0 <= diff <= 4), got: ' .. tostring(diff))
|
|
end)
|
|
|
|
it('are triggered in blocking getchar() call', function()
|
|
command("call timer_start(5, 'MyHandler', {'repeat': -1})")
|
|
async_meths.nvim_command('let g:val = 0 | let g:c = getchar()')
|
|
retry(nil, nil, function()
|
|
local val = eval('g:val')
|
|
ok(val >= 2, '>= 2', tostring(val))
|
|
eq(0, eval('getchar(1)'))
|
|
end)
|
|
feed('c')
|
|
eq(99, eval('g:c'))
|
|
end)
|
|
|
|
it('can invoke redraw in blocking getchar() call', function()
|
|
local screen = Screen.new(40, 6)
|
|
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'ITEM 1', 'ITEM 2' })
|
|
source([[
|
|
let g:cont = 0
|
|
func! AddItem(timer)
|
|
if !g:cont
|
|
return
|
|
endif
|
|
call timer_stop(a:timer)
|
|
|
|
call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3'])
|
|
|
|
" Meant to test for what Vim tests in Test_peek_and_get_char.
|
|
call getchar(1)
|
|
|
|
redraw
|
|
endfunc
|
|
]])
|
|
async_meths.nvim_command('let g:c2 = getchar()')
|
|
async_meths.nvim_command(
|
|
'call timer_start(' .. load_adjust(100) .. ", 'AddItem', {'repeat': -1})"
|
|
)
|
|
|
|
screen:expect([[
|
|
^ITEM 1 |
|
|
ITEM 2 |
|
|
{1:~ }|*3
|
|
|
|
|
]])
|
|
async_meths.nvim_command('let g:cont = 1')
|
|
|
|
screen:expect([[
|
|
^ITEM 1 |
|
|
ITEM 2 |
|
|
ITEM 3 |
|
|
{1:~ }|*2
|
|
|
|
|
]])
|
|
|
|
feed('3')
|
|
eq(51, eval('g:c2'))
|
|
screen:expect {
|
|
grid = [[
|
|
^ITEM 1 |
|
|
ITEM 2 |
|
|
ITEM 3 |
|
|
{1:~ }|*2
|
|
|
|
|
]],
|
|
unchanged = true,
|
|
}
|
|
end)
|
|
|
|
it('can be stopped', function()
|
|
local t_init_val = eval("[timer_start(5, 'MyHandler', {'repeat': -1}), g:val]")
|
|
eq(0, t_init_val[2])
|
|
run(nil, nil, nil, load_adjust(30))
|
|
fn.timer_stop(t_init_val[1])
|
|
local count = eval('g:val')
|
|
run(nil, load_adjust(300), nil, load_adjust(30))
|
|
local count2 = eval('g:val')
|
|
-- when count is eval:ed after timer_stop this should be non-racy
|
|
eq(count, count2)
|
|
end)
|
|
|
|
it('can be stopped from the handler', function()
|
|
source([[
|
|
func! MyHandler(timer)
|
|
let g:val += 1
|
|
if g:val == 3
|
|
call timer_stop(a:timer)
|
|
" check double stop is ignored
|
|
call timer_stop(a:timer)
|
|
endif
|
|
endfunc
|
|
]])
|
|
eq(0, eval('g:val'))
|
|
command("call timer_start(10, 'MyHandler', {'repeat': -1})")
|
|
retry(nil, nil, function()
|
|
eq(3, eval('g:val'))
|
|
end)
|
|
end)
|
|
|
|
it('can have two timers', function()
|
|
source([[
|
|
let g:val2 = 0
|
|
func! MyHandler2(timer)
|
|
let g:val2 += 1
|
|
endfunc
|
|
]])
|
|
command("call timer_start(2, 'MyHandler', {'repeat': 3})")
|
|
command("call timer_start(4, 'MyHandler2', {'repeat': 2})")
|
|
retry(nil, nil, function()
|
|
eq(3, eval('g:val'))
|
|
eq(2, eval('g:val2'))
|
|
end)
|
|
end)
|
|
|
|
it('do not crash when processing events in the handler', function()
|
|
source([[
|
|
let g:val = 0
|
|
func! MyHandler(timer)
|
|
call timer_stop(a:timer)
|
|
sleep 10m
|
|
let g:val += 1
|
|
endfunc
|
|
]])
|
|
command("call timer_start(5, 'MyHandler', {'repeat': 1})")
|
|
run(nil, nil, nil, load_adjust(20))
|
|
retry(nil, load_adjust(150), function()
|
|
eq(1, eval('g:val'))
|
|
end)
|
|
end)
|
|
|
|
it("doesn't mess up the cmdline", function()
|
|
local screen = Screen.new(40, 6)
|
|
source([[
|
|
let g:val = 0
|
|
func! MyHandler(timer)
|
|
while !g:val
|
|
return
|
|
endwhile
|
|
call timer_stop(a:timer)
|
|
|
|
echo "evil"
|
|
redraw
|
|
let g:val = 2
|
|
endfunc
|
|
]])
|
|
command("call timer_start(100, 'MyHandler', {'repeat': -1})")
|
|
feed(':good')
|
|
screen:expect([[
|
|
|
|
|
{1:~ }|*4
|
|
:good^ |
|
|
]])
|
|
command('let g:val = 1')
|
|
screen:expect_unchanged(true, load_adjust(200))
|
|
|
|
eq(2, eval('g:val'))
|
|
end)
|
|
|
|
it("timer_start can't be used in the sandbox", function()
|
|
source [[
|
|
function! Scary(timer) abort
|
|
call execute('echo ''execute() should be disallowed''', '')
|
|
endfunction
|
|
]]
|
|
eq('Vim(call):E48: Not allowed in sandbox', exc_exec("sandbox call timer_start(0, 'Scary')"))
|
|
end)
|
|
|
|
it('can be triggered after an empty string <expr> mapping #17257', function()
|
|
local screen = Screen.new(40, 6)
|
|
command([=[imap <expr> <F2> [timer_start(0, { _ -> execute("throw 'x'", "") }), ''][-1]]=])
|
|
feed('i<F2>')
|
|
screen:expect({ any = 'E605: Exception not caught: x' })
|
|
end)
|
|
end)
|