mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 19:25:11 -07:00
1037ce2e46
This is the command invoked repeatedly to make the changes: :%s/^\(.*\)|\%(\*\(\d\+\)\)\?$\n\1|\%(\*\(\d\+\)\)\?$/\=submatch(1)..'|*'..(max([str2nr(submatch(2)),1])+max([str2nr(submatch(3)),1]))/g
485 lines
16 KiB
Lua
485 lines
16 KiB
Lua
local Screen = require('test.functional.ui.screen')
|
|
local helpers = require('test.functional.helpers')(after_each)
|
|
local luv = require('luv')
|
|
local eq, eval, expect, exec =
|
|
helpers.eq, helpers.eval, helpers.expect, helpers.exec
|
|
local assert_alive = helpers.assert_alive
|
|
local clear = helpers.clear
|
|
local command = helpers.command
|
|
local feed = helpers.feed
|
|
local funcs = helpers.funcs
|
|
local nvim_prog = helpers.nvim_prog
|
|
local ok = helpers.ok
|
|
local rmdir = helpers.rmdir
|
|
local new_argv = helpers.new_argv
|
|
local new_pipename = helpers.new_pipename
|
|
local pesc = helpers.pesc
|
|
local os_kill = helpers.os_kill
|
|
local set_session = helpers.set_session
|
|
local spawn = helpers.spawn
|
|
local async_meths = helpers.async_meths
|
|
local expect_msg_seq = helpers.expect_msg_seq
|
|
local pcall_err = helpers.pcall_err
|
|
local mkdir = helpers.mkdir
|
|
local poke_eventloop = helpers.poke_eventloop
|
|
local meths = helpers.meths
|
|
local retry = helpers.retry
|
|
local write_file = helpers.write_file
|
|
|
|
describe(':recover', function()
|
|
before_each(clear)
|
|
|
|
it('fails if given a non-existent swapfile', function()
|
|
local swapname = 'bogus_swapfile'
|
|
local swapname2 = 'bogus_swapfile.swp'
|
|
eq('Vim(recover):E305: No swap file found for '..swapname,
|
|
pcall_err(command, 'recover '..swapname)) -- Should not segfault. #2117
|
|
-- Also check filename ending with ".swp". #9504
|
|
eq('Vim(recover):E306: Cannot open '..swapname2,
|
|
pcall_err(command, 'recover '..swapname2)) -- Should not segfault. #2117
|
|
assert_alive()
|
|
end)
|
|
|
|
end)
|
|
|
|
describe("preserve and (R)ecover with custom 'directory'", function()
|
|
local swapdir = luv.cwd()..'/Xtest_recover_dir'
|
|
local testfile = 'Xtest_recover_file1'
|
|
-- Put swapdir at the start of the 'directory' list. #1836
|
|
-- Note: `set swapfile` *must* go after `set directory`: otherwise it may
|
|
-- attempt to create a swapfile in different directory.
|
|
local init = [[
|
|
set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//
|
|
set swapfile fileformat=unix undolevels=-1
|
|
]]
|
|
|
|
local nvim0
|
|
before_each(function()
|
|
nvim0 = spawn(new_argv())
|
|
set_session(nvim0)
|
|
rmdir(swapdir)
|
|
mkdir(swapdir)
|
|
end)
|
|
after_each(function()
|
|
command('%bwipeout!')
|
|
rmdir(swapdir)
|
|
end)
|
|
|
|
local function setup_swapname()
|
|
exec(init)
|
|
command('edit! '..testfile)
|
|
feed('isometext<esc>')
|
|
exec('redir => g:swapname | silent swapname | redir END')
|
|
return eval('g:swapname')
|
|
end
|
|
|
|
local function test_recover(swappath1)
|
|
-- Start another Nvim instance.
|
|
local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, true)
|
|
set_session(nvim2)
|
|
|
|
exec(init)
|
|
|
|
-- Use the "SwapExists" event to choose the (R)ecover choice at the dialog.
|
|
command('autocmd SwapExists * let v:swapchoice = "r"')
|
|
command('silent edit! '..testfile)
|
|
exec('redir => g:swapname | silent swapname | redir END')
|
|
|
|
local swappath2 = eval('g:swapname')
|
|
|
|
expect('sometext')
|
|
-- swapfile from session 1 should end in .swp
|
|
eq(testfile..'.swp', string.match(swappath1, '[^%%]+$'))
|
|
-- swapfile from session 2 should end in .swo
|
|
eq(testfile..'.swo', string.match(swappath2, '[^%%]+$'))
|
|
-- Verify that :swapname was not truncated (:help 'shortmess').
|
|
ok(nil == string.find(swappath1, '%.%.%.'))
|
|
ok(nil == string.find(swappath2, '%.%.%.'))
|
|
end
|
|
|
|
it('with :preserve and SIGKILL', function()
|
|
local swappath1 = setup_swapname()
|
|
command('preserve')
|
|
os_kill(eval('getpid()'))
|
|
test_recover(swappath1)
|
|
end)
|
|
|
|
it('closing stdio channel without :preserve #22096', function()
|
|
local swappath1 = setup_swapname()
|
|
nvim0:close()
|
|
test_recover(swappath1)
|
|
end)
|
|
|
|
it('killing TUI process without :preserve #22096', function()
|
|
helpers.skip(helpers.is_os('win'))
|
|
local screen0 = Screen.new()
|
|
screen0:attach()
|
|
local child_server = new_pipename()
|
|
funcs.termopen({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--listen', child_server }, {
|
|
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }
|
|
})
|
|
screen0:expect({any = pesc('[No Name]')}) -- Wait for the child process to start.
|
|
local child_session = helpers.connect(child_server)
|
|
set_session(child_session)
|
|
local swappath1 = setup_swapname()
|
|
set_session(nvim0)
|
|
command('call chanclose(&channel)') -- Kill the child process.
|
|
screen0:expect({any = pesc('[Process exited 1]')}) -- Wait for the child process to stop.
|
|
test_recover(swappath1)
|
|
end)
|
|
|
|
end)
|
|
|
|
describe('swapfile detection', function()
|
|
local swapdir = luv.cwd()..'/Xtest_swapdialog_dir'
|
|
local nvim0
|
|
-- Put swapdir at the start of the 'directory' list. #1836
|
|
-- Note: `set swapfile` *must* go after `set directory`: otherwise it may
|
|
-- attempt to create a swapfile in different directory.
|
|
local init = [[
|
|
set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//
|
|
set swapfile fileformat=unix nomodified undolevels=-1 nohidden
|
|
]]
|
|
before_each(function()
|
|
nvim0 = spawn(new_argv())
|
|
set_session(nvim0)
|
|
rmdir(swapdir)
|
|
mkdir(swapdir)
|
|
end)
|
|
after_each(function()
|
|
set_session(nvim0)
|
|
command('%bwipeout!')
|
|
rmdir(swapdir)
|
|
end)
|
|
|
|
it('always show swapfile dialog #8840 #9027', function()
|
|
local testfile = 'Xtest_swapdialog_file1'
|
|
|
|
local expected_no_dialog = '^'..(' '):rep(256)..'|\n'
|
|
for _=1,37 do
|
|
expected_no_dialog = expected_no_dialog..'~'..(' '):rep(255)..'|\n'
|
|
end
|
|
expected_no_dialog = expected_no_dialog..testfile..(' '):rep(216)..'0,0-1 All|\n'
|
|
expected_no_dialog = expected_no_dialog..(' '):rep(256)..'|\n'
|
|
|
|
exec(init)
|
|
command('edit! '..testfile)
|
|
feed('isometext<esc>')
|
|
command('preserve')
|
|
|
|
-- Start another Nvim instance.
|
|
local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, true, nil, true)
|
|
set_session(nvim2)
|
|
local screen2 = Screen.new(256, 40)
|
|
screen2:attach()
|
|
exec(init)
|
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
|
|
|
-- With shortmess+=F
|
|
command('set shortmess+=F')
|
|
feed(':edit '..testfile..'<CR>')
|
|
screen2:expect{any=[[E325: ATTENTION.*]]..'\n'..[[Found a swap file by the name ".*]]
|
|
..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
|
|
feed('e') -- Chose "Edit" at the swap dialog.
|
|
screen2:expect(expected_no_dialog)
|
|
|
|
-- With :silent and shortmess+=F
|
|
feed(':silent edit %<CR>')
|
|
screen2:expect{any=[[Found a swap file by the name ".*]]
|
|
..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
|
|
feed('e') -- Chose "Edit" at the swap dialog.
|
|
screen2:expect(expected_no_dialog)
|
|
|
|
-- With :silent! and shortmess+=F
|
|
feed(':silent! edit %<CR>')
|
|
screen2:expect{any=[[Found a swap file by the name ".*]]
|
|
..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
|
|
feed('e') -- Chose "Edit" at the swap dialog.
|
|
screen2:expect(expected_no_dialog)
|
|
|
|
-- With API (via eval/Vimscript) call and shortmess+=F
|
|
feed(':call nvim_command("edit %")<CR>')
|
|
screen2:expect{any=[[Found a swap file by the name ".*]]
|
|
..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
|
|
feed('e') -- Chose "Edit" at the swap dialog.
|
|
feed('<c-c>')
|
|
screen2:expect(expected_no_dialog)
|
|
|
|
-- With API call and shortmess+=F
|
|
async_meths.command('edit %')
|
|
screen2:expect{any=[[Found a swap file by the name ".*]]
|
|
..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]}
|
|
feed('e') -- Chose "Edit" at the swap dialog.
|
|
expect_msg_seq({
|
|
ignore={'redraw'},
|
|
seqs={
|
|
{ {'notification', 'nvim_error_event', {0, 'Vim(edit):E325: ATTENTION'}},
|
|
}
|
|
}
|
|
})
|
|
feed('<cr>')
|
|
|
|
nvim2:close()
|
|
end)
|
|
|
|
it('default SwapExists handler selects "(E)dit" and skips prompt', function()
|
|
exec(init)
|
|
command('edit Xfile1')
|
|
command("put ='some text...'")
|
|
command('preserve') -- Make sure the swap file exists.
|
|
local nvimpid = funcs.getpid()
|
|
|
|
local nvim1 = spawn(new_argv(), true, nil, true)
|
|
set_session(nvim1)
|
|
local screen = Screen.new(75, 18)
|
|
screen:attach()
|
|
exec(init)
|
|
feed(':edit Xfile1\n')
|
|
|
|
screen:expect({ any = ('W325: Ignoring swapfile from Nvim process %d'):format(nvimpid) })
|
|
nvim1:close()
|
|
end)
|
|
|
|
-- oldtest: Test_swap_prompt_splitwin()
|
|
it('selecting "q" in the attention prompt', function()
|
|
exec(init)
|
|
command('edit Xfile1')
|
|
command('preserve') -- Make sure the swap file exists.
|
|
|
|
local screen = Screen.new(75, 18)
|
|
screen:set_default_attr_ids({
|
|
[0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
|
|
[1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
|
|
})
|
|
|
|
local nvim1 = spawn(new_argv(), true, nil, true)
|
|
set_session(nvim1)
|
|
screen:attach()
|
|
exec(init)
|
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
|
feed(':split Xfile1\n')
|
|
-- The default SwapExists handler does _not_ skip this prompt.
|
|
screen:expect({
|
|
any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
|
|
})
|
|
feed('q')
|
|
feed(':<CR>')
|
|
screen:expect([[
|
|
^ |
|
|
{0:~ }|*16
|
|
: |
|
|
]])
|
|
nvim1:close()
|
|
|
|
local nvim2 = spawn(new_argv(), true, nil, true)
|
|
set_session(nvim2)
|
|
screen:attach()
|
|
exec(init)
|
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
|
command('set more')
|
|
command('au bufadd * let foo_w = wincol()')
|
|
feed(':e Xfile1<CR>')
|
|
screen:expect({any = pesc('{1:-- More --}^')})
|
|
feed('<Space>')
|
|
screen:expect({
|
|
any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
|
|
})
|
|
feed('q')
|
|
command([[echo 'hello']])
|
|
screen:expect([[
|
|
^ |
|
|
{0:~ }|*16
|
|
hello |
|
|
]])
|
|
nvim2:close()
|
|
end)
|
|
|
|
--- @param swapexists boolean Enable the default SwapExists handler.
|
|
--- @param on_swapfile_running fun(screen: any) Called after swapfile ("STILL RUNNING") prompt.
|
|
local function test_swapfile_after_reboot(swapexists, on_swapfile_running)
|
|
local screen = Screen.new(75, 30)
|
|
screen:set_default_attr_ids({
|
|
[0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
|
|
[1] = {bold = true, foreground = Screen.colors.SeaGreen}, -- MoreMsg
|
|
[2] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg
|
|
})
|
|
screen:attach()
|
|
|
|
exec(init)
|
|
if not swapexists then
|
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
|
end
|
|
command('set nohidden')
|
|
|
|
exec([=[
|
|
" Make a copy of the current swap file to "Xswap".
|
|
" Return the name of the swap file.
|
|
func CopySwapfile()
|
|
preserve
|
|
" get the name of the swap file
|
|
let swname = split(execute("swapname"))[0]
|
|
let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '')
|
|
" make a copy of the swap file in Xswap
|
|
set binary
|
|
exe 'sp ' . fnameescape(swname)
|
|
w! Xswap
|
|
set nobinary
|
|
return swname
|
|
endfunc
|
|
]=])
|
|
|
|
-- Edit a file and grab its swapfile.
|
|
exec([[
|
|
edit Xswaptest
|
|
call setline(1, ['a', 'b', 'c'])
|
|
]])
|
|
local swname = funcs.CopySwapfile()
|
|
|
|
-- Forget we edited this file
|
|
exec([[
|
|
new
|
|
only!
|
|
bwipe! Xswaptest
|
|
]])
|
|
|
|
os.rename('Xswap', swname)
|
|
|
|
feed(':edit Xswaptest<CR>')
|
|
on_swapfile_running(screen)
|
|
|
|
feed('e')
|
|
|
|
-- Forget we edited this file
|
|
exec([[
|
|
new
|
|
only!
|
|
bwipe! Xswaptest
|
|
]])
|
|
|
|
-- pretend that the swapfile was created before boot
|
|
local atime = os.time() - luv.uptime() - 10
|
|
luv.fs_utime(swname, atime, atime)
|
|
|
|
feed(':edit Xswaptest<CR>')
|
|
screen:expect({any = table.concat({
|
|
pesc('{2:E325: ATTENTION}'),
|
|
pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort: }^'),
|
|
}, '.*')})
|
|
|
|
feed('e')
|
|
end
|
|
|
|
-- oldtest: Test_nocatch_process_still_running()
|
|
it('swapfile created before boot vim-patch:8.2.2586', function()
|
|
test_swapfile_after_reboot(false, function(screen)
|
|
screen:expect({any = table.concat({
|
|
pesc('{2:E325: ATTENTION}'),
|
|
'file name: .*Xswaptest',
|
|
'process ID: %d* %(STILL RUNNING%)',
|
|
pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
|
|
}, '.*')})
|
|
end)
|
|
end)
|
|
|
|
it('swapfile created before boot + default SwapExists handler', function()
|
|
test_swapfile_after_reboot(true, function(screen)
|
|
screen:expect({ any = 'W325: Ignoring swapfile from Nvim process' })
|
|
end)
|
|
end)
|
|
|
|
end)
|
|
|
|
describe('quitting swapfile dialog on startup stops TUI properly', function()
|
|
local swapdir = luv.cwd()..'/Xtest_swapquit_dir'
|
|
local testfile = 'Xtest_swapquit_file1'
|
|
local otherfile = 'Xtest_swapquit_file2'
|
|
-- Put swapdir at the start of the 'directory' list. #1836
|
|
-- Note: `set swapfile` *must* go after `set directory`: otherwise it may
|
|
-- attempt to create a swapfile in different directory.
|
|
local init_dir = [[set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//]]
|
|
local init_set = [[set swapfile fileformat=unix nomodified undolevels=-1 nohidden]]
|
|
|
|
before_each(function()
|
|
clear({args = {'--cmd', init_dir, '--cmd', init_set}})
|
|
rmdir(swapdir)
|
|
mkdir(swapdir)
|
|
write_file(testfile, [[
|
|
first
|
|
second
|
|
third
|
|
|
|
]])
|
|
command('edit! '..testfile)
|
|
feed('Gisometext<esc>')
|
|
poke_eventloop()
|
|
clear() -- Leaves a swap file behind
|
|
meths.ui_attach(80, 30, {})
|
|
end)
|
|
after_each(function()
|
|
rmdir(swapdir)
|
|
os.remove(testfile)
|
|
os.remove(otherfile)
|
|
end)
|
|
|
|
it('(Q)uit at first file argument', function()
|
|
local chan = funcs.termopen({nvim_prog, '-u', 'NONE', '-i', 'NONE',
|
|
'--cmd', init_dir, '--cmd', init_set,
|
|
testfile}, {
|
|
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }
|
|
})
|
|
retry(nil, nil, function()
|
|
eq('[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort:',
|
|
eval("getline('$')->trim(' ', 2)"))
|
|
end)
|
|
meths.chan_send(chan, 'q')
|
|
retry(nil, nil, function()
|
|
eq({'', '[Process exited 1]', ''},
|
|
eval("[1, 2, '$']->map({_, lnum -> getline(lnum)->trim(' ', 2)})"))
|
|
end)
|
|
end)
|
|
|
|
it('(A)bort at second file argument with -p', function()
|
|
local chan = funcs.termopen({nvim_prog, '-u', 'NONE', '-i', 'NONE',
|
|
'--cmd', init_dir, '--cmd', init_set,
|
|
'-p', otherfile, testfile}, {
|
|
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }
|
|
})
|
|
retry(nil, nil, function()
|
|
eq('[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort:',
|
|
eval("getline('$')->trim(' ', 2)"))
|
|
end)
|
|
meths.chan_send(chan, 'a')
|
|
retry(nil, nil, function()
|
|
eq({'', '[Process exited 1]', ''},
|
|
eval("[1, 2, '$']->map({_, lnum -> getline(lnum)->trim(' ', 2)})"))
|
|
end)
|
|
end)
|
|
|
|
it('(Q)uit at file opened by -t', function()
|
|
write_file(otherfile, ([[
|
|
!_TAG_FILE_ENCODING utf-8 //
|
|
first %s /^ \zsfirst$/
|
|
second %s /^ \zssecond$/
|
|
third %s /^ \zsthird$/]]):format(testfile, testfile, testfile))
|
|
local chan = funcs.termopen({nvim_prog, '-u', 'NONE', '-i', 'NONE',
|
|
'--cmd', init_dir, '--cmd', init_set,
|
|
'--cmd', 'set tags='..otherfile, '-tsecond'}, {
|
|
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }
|
|
})
|
|
retry(nil, nil, function()
|
|
eq('[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort:',
|
|
eval("getline('$')->trim(' ', 2)"))
|
|
end)
|
|
meths.chan_send(chan, 'q')
|
|
retry(nil, nil, function()
|
|
eq('Press ENTER or type command to continue',
|
|
eval("getline('$')->trim(' ', 2)"))
|
|
end)
|
|
meths.chan_send(chan, '\r')
|
|
retry(nil, nil, function()
|
|
eq({'', '[Process exited 1]', ''},
|
|
eval("[1, 2, '$']->map({_, lnum -> getline(lnum)->trim(' ', 2)})"))
|
|
end)
|
|
end)
|
|
end)
|