mirror of
https://github.com/neovim/neovim.git
synced 2026-02-19 09:58:32 +00:00
Problem: prompt_setprompt does not adjust extmarks or trigger on_bytes buffer-updates when fixing the prompt line. Solution: adjust them, trigger on_bytes. Notably, hides extmarks when replacing the entire line (and clearing user input). Otherwise, when just replacing the prompt text, hides extmarks there, but moves those after (in the user input area) to the correct spot.
1895 lines
56 KiB
Lua
1895 lines
56 KiB
Lua
-- Test suite for testing interactions with API bindings
|
|
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
local Screen = require('test.functional.ui.screen')
|
|
|
|
local command = n.command
|
|
local api = n.api
|
|
local fn = n.fn
|
|
local clear = n.clear
|
|
local eq = t.eq
|
|
local fail = t.fail
|
|
local exec_lua = n.exec_lua
|
|
local feed = n.feed
|
|
local expect_events = t.expect_events
|
|
local write_file = t.write_file
|
|
local dedent = t.dedent
|
|
local matches = t.matches
|
|
local pcall_err = t.pcall_err
|
|
|
|
local origlines = {
|
|
'original line 1',
|
|
'original line 2',
|
|
'original line 3',
|
|
'original line 4',
|
|
'original line 5',
|
|
'original line 6',
|
|
' indented line',
|
|
}
|
|
|
|
before_each(function()
|
|
clear()
|
|
exec_lua(function()
|
|
local events = {}
|
|
|
|
function _G.test_register(bufnr, evname, id, changedtick, utf_sizes, preview)
|
|
local function callback(...)
|
|
table.insert(events, { id, ... })
|
|
if _G.test_unreg == id then
|
|
return true
|
|
end
|
|
end
|
|
local opts = {
|
|
[evname] = callback,
|
|
on_detach = callback,
|
|
on_reload = callback,
|
|
utf_sizes = utf_sizes,
|
|
preview = preview,
|
|
}
|
|
if changedtick then
|
|
opts.on_changedtick = callback
|
|
end
|
|
vim.api.nvim_buf_attach(bufnr, false, opts)
|
|
end
|
|
|
|
function _G.get_events()
|
|
local ret_events = events
|
|
events = {}
|
|
return ret_events
|
|
end
|
|
end)
|
|
end)
|
|
|
|
describe('lua: nvim_buf_attach on_lines', function()
|
|
local function setup_eventcheck(verify, utf_sizes, lines)
|
|
local lastsize
|
|
api.nvim_buf_set_lines(0, 0, -1, true, lines)
|
|
if verify then
|
|
lastsize = api.nvim_buf_get_offset(0, api.nvim_buf_line_count(0))
|
|
end
|
|
exec_lua('return test_register(...)', 0, 'on_lines', 'test1', false, utf_sizes)
|
|
local verify_name = 'test1'
|
|
|
|
local function check_events(expected)
|
|
local events = exec_lua('return get_events(...)')
|
|
if utf_sizes then
|
|
-- this test case uses ASCII only, so sizes should be the same.
|
|
-- Unicode is tested below.
|
|
for _, event in ipairs(expected) do
|
|
event[9] = event[9] or event[8]
|
|
event[10] = event[10] or event[9]
|
|
end
|
|
end
|
|
expect_events(expected, events, 'line updates')
|
|
if verify then
|
|
for _, event in ipairs(events) do
|
|
if event[1] == verify_name and event[2] == 'lines' then
|
|
local startline, endline = event[5], event[7]
|
|
local newrange = api.nvim_buf_get_offset(0, endline)
|
|
- api.nvim_buf_get_offset(0, startline)
|
|
local newsize = api.nvim_buf_get_offset(0, api.nvim_buf_line_count(0))
|
|
local oldrange = newrange + lastsize - newsize
|
|
eq(oldrange, event[8])
|
|
lastsize = newsize
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return check_events, function(new)
|
|
verify_name = new
|
|
end
|
|
end
|
|
|
|
-- verifying the sizes with nvim_buf_get_offset is nice (checks we cannot
|
|
-- assert the wrong thing), but masks errors with unflushed lines (as
|
|
-- nvim_buf_get_offset forces a flush of the memline). To be safe run the
|
|
-- test both ways.
|
|
local function check(verify, utf_sizes)
|
|
local check_events, verify_name = setup_eventcheck(verify, utf_sizes, origlines)
|
|
|
|
local tick = api.nvim_buf_get_changedtick(0)
|
|
command('set autoindent')
|
|
command('normal! GyyggP')
|
|
tick = tick + 1
|
|
check_events { { 'test1', 'lines', 1, tick, 0, 0, 1, 0 } }
|
|
|
|
api.nvim_buf_set_lines(0, 3, 5, true, { 'changed line' })
|
|
tick = tick + 1
|
|
check_events { { 'test1', 'lines', 1, tick, 3, 5, 4, 32 } }
|
|
|
|
exec_lua('return test_register(...)', 0, 'on_lines', 'test2', true, utf_sizes)
|
|
tick = tick + 1
|
|
command('undo')
|
|
|
|
-- plugins can opt in to receive changedtick events, or choose
|
|
-- to only receive actual changes.
|
|
check_events {
|
|
{ 'test1', 'lines', 1, tick, 3, 4, 5, 13 },
|
|
{ 'test2', 'lines', 1, tick, 3, 4, 5, 13 },
|
|
{ 'test2', 'changedtick', 1, tick + 1 },
|
|
}
|
|
tick = tick + 1
|
|
|
|
tick = tick + 1
|
|
command('redo')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, tick, 3, 5, 4, 32 },
|
|
{ 'test2', 'lines', 1, tick, 3, 5, 4, 32 },
|
|
{ 'test2', 'changedtick', 1, tick + 1 },
|
|
}
|
|
tick = tick + 1
|
|
|
|
tick = tick + 1
|
|
command('undo!')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, tick, 3, 4, 5, 13 },
|
|
{ 'test2', 'lines', 1, tick, 3, 4, 5, 13 },
|
|
{ 'test2', 'changedtick', 1, tick + 1 },
|
|
}
|
|
tick = tick + 1
|
|
|
|
-- simulate next callback returning true
|
|
exec_lua("test_unreg = 'test1'")
|
|
|
|
api.nvim_buf_set_lines(0, 6, 7, true, { 'x1', 'x2', 'x3' })
|
|
tick = tick + 1
|
|
|
|
-- plugins can opt in to receive changedtick events, or choose
|
|
-- to only receive actual changes.
|
|
check_events {
|
|
{ 'test1', 'lines', 1, tick, 6, 7, 9, 16 },
|
|
{ 'test2', 'lines', 1, tick, 6, 7, 9, 16 },
|
|
}
|
|
|
|
verify_name 'test2'
|
|
|
|
api.nvim_buf_set_lines(0, 1, 1, true, { 'added' })
|
|
tick = tick + 1
|
|
check_events { { 'test2', 'lines', 1, tick, 1, 1, 2, 0 } }
|
|
|
|
feed('wix')
|
|
tick = tick + 1
|
|
check_events { { 'test2', 'lines', 1, tick, 4, 5, 5, 16 } }
|
|
|
|
-- check hot path for multiple insert
|
|
feed('yz')
|
|
tick = tick + 1
|
|
check_events { { 'test2', 'lines', 1, tick, 4, 5, 5, 17 } }
|
|
|
|
feed('<bs>')
|
|
tick = tick + 1
|
|
check_events { { 'test2', 'lines', 1, tick, 4, 5, 5, 19 } }
|
|
|
|
feed('<esc>Go')
|
|
tick = tick + 1
|
|
check_events { { 'test2', 'lines', 1, tick, 11, 11, 12, 0 } }
|
|
|
|
feed('x')
|
|
tick = tick + 1
|
|
check_events { { 'test2', 'lines', 1, tick, 11, 12, 12, 5 } }
|
|
|
|
command('bwipe!')
|
|
check_events { { 'test2', 'detach', 1 } }
|
|
end
|
|
|
|
it('works', function()
|
|
check(false)
|
|
end)
|
|
|
|
it('works with verify', function()
|
|
check(true)
|
|
end)
|
|
|
|
it('works with utf_sizes and ASCII text', function()
|
|
check(false, true)
|
|
end)
|
|
|
|
local function check_unicode(verify)
|
|
local unicode_text = {
|
|
'ascii text',
|
|
'latin text åäö',
|
|
'BMP text ɧ αλφά',
|
|
'BMP text 汉语 ↥↧',
|
|
'SMP 🤦 🦄🦃',
|
|
'combining å بِيَّة',
|
|
}
|
|
local check_events, verify_name = setup_eventcheck(verify, true, unicode_text)
|
|
|
|
local tick = api.nvim_buf_get_changedtick(0)
|
|
|
|
feed('ggdd')
|
|
tick = tick + 1
|
|
check_events { { 'test1', 'lines', 1, tick, 0, 1, 0, 11, 11, 11 } }
|
|
|
|
feed('A<bs>')
|
|
tick = tick + 1
|
|
check_events { { 'test1', 'lines', 1, tick, 0, 1, 1, 18, 15, 15 } }
|
|
|
|
feed('<esc>jylp')
|
|
tick = tick + 1
|
|
check_events { { 'test1', 'lines', 1, tick, 1, 2, 2, 21, 16, 16 } }
|
|
|
|
feed('+eea<cr>')
|
|
tick = tick + 1
|
|
check_events { { 'test1', 'lines', 1, tick, 2, 3, 4, 23, 15, 15 } }
|
|
|
|
feed('<esc>jdw')
|
|
tick = tick + 1
|
|
-- non-BMP chars count as 2 UTF-2 codeunits
|
|
check_events { { 'test1', 'lines', 1, tick, 4, 5, 5, 18, 9, 12 } }
|
|
|
|
feed('+rx')
|
|
tick = tick + 1
|
|
-- count the individual codepoints of a composed character.
|
|
check_events { { 'test1', 'lines', 1, tick, 5, 6, 6, 27, 20, 20 } }
|
|
|
|
feed('kJ')
|
|
tick = tick + 1
|
|
-- verification fails with multiple line updates, sorry about that
|
|
verify_name ''
|
|
-- NB: this is inefficient (but not really wrong).
|
|
check_events {
|
|
{ 'test1', 'lines', 1, tick, 4, 5, 5, 14, 5, 8 },
|
|
{ 'test1', 'lines', 1, tick + 1, 5, 6, 5, 27, 20, 20 },
|
|
}
|
|
end
|
|
|
|
it('works with utf_sizes and unicode text', function()
|
|
check_unicode(false)
|
|
end)
|
|
|
|
it('works with utf_sizes and unicode text with verify', function()
|
|
check_unicode(true)
|
|
end)
|
|
|
|
it('has valid cursor position while shifting', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'line1' })
|
|
exec_lua(function()
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_lines = function()
|
|
vim.api.nvim_set_var('listener_cursor_line', vim.api.nvim_win_get_cursor(0)[1])
|
|
end,
|
|
})
|
|
end)
|
|
feed('>>')
|
|
eq(1, api.nvim_get_var('listener_cursor_line'))
|
|
end)
|
|
|
|
it('has valid cursor position while deleting lines', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'line_1', 'line_2', 'line_3', 'line_4' })
|
|
api.nvim_win_set_cursor(0, { 2, 0 })
|
|
eq(2, api.nvim_win_get_cursor(0)[1])
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'line_1', 'line_2', 'line_3' })
|
|
eq(2, api.nvim_win_get_cursor(0)[1])
|
|
end)
|
|
|
|
it('#12718 lnume', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { '1', '2', '3' })
|
|
exec_lua(function()
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_lines = function(...)
|
|
vim.api.nvim_set_var('linesev', { ... })
|
|
end,
|
|
})
|
|
end)
|
|
feed('1G0')
|
|
feed('y<C-v>2j')
|
|
feed('G0')
|
|
feed('p')
|
|
-- Is the last arg old_byte_size correct? Doesn't matter for this PR
|
|
eq({ 'lines', 1, 4, 2, 3, 5, 4 }, api.nvim_get_var('linesev'))
|
|
|
|
feed('2G0')
|
|
feed('p')
|
|
eq({ 'lines', 1, 5, 1, 4, 4, 8 }, api.nvim_get_var('linesev'))
|
|
|
|
feed('1G0')
|
|
feed('P')
|
|
eq({ 'lines', 1, 6, 0, 3, 3, 9 }, api.nvim_get_var('linesev'))
|
|
end)
|
|
|
|
it('nvim_buf_call() from callback does not cause wrong Normal mode CTRL-A #16729', function()
|
|
exec_lua(function()
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_lines = function()
|
|
vim.api.nvim_buf_call(0, function() end)
|
|
end,
|
|
})
|
|
end)
|
|
feed('itest123<Esc><C-A>')
|
|
eq('test124', api.nvim_get_current_line())
|
|
end)
|
|
|
|
it('setting extmark in on_lines callback works', function()
|
|
local screen = Screen.new(40, 6)
|
|
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'aaa', 'bbb', 'ccc' })
|
|
exec_lua(function()
|
|
local ns = vim.api.nvim_create_namespace('')
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_lines = function(_, _, _, row, _, end_row)
|
|
vim.api.nvim_buf_clear_namespace(0, ns, row, end_row)
|
|
for i = row, end_row - 1 do
|
|
vim.api.nvim_buf_set_extmark(0, ns, i, 0, {
|
|
virt_text = { { 'NEW' .. tostring(i), 'WarningMsg' } },
|
|
})
|
|
end
|
|
end,
|
|
})
|
|
end)
|
|
|
|
feed('o')
|
|
screen:expect({
|
|
grid = [[
|
|
aaa |
|
|
^ {19:NEW1} |
|
|
bbb |
|
|
ccc |
|
|
{1:~ }|
|
|
{5:-- INSERT --} |
|
|
]],
|
|
})
|
|
feed('<CR>')
|
|
screen:expect({
|
|
grid = [[
|
|
aaa |
|
|
{19:NEW1} |
|
|
^ {19:NEW2} |
|
|
bbb |
|
|
ccc |
|
|
{5:-- INSERT --} |
|
|
]],
|
|
})
|
|
end)
|
|
|
|
it('line lengths are correct when pressing TAB with folding #29119', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'a', 'b' })
|
|
|
|
exec_lua(function()
|
|
_G.res = {}
|
|
vim.o.foldmethod = 'indent'
|
|
vim.o.softtabstop = -1
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_lines = function(_, bufnr, _, row, _, end_row)
|
|
local lines = vim.api.nvim_buf_get_lines(bufnr, row, end_row, true)
|
|
table.insert(_G.res, lines)
|
|
end,
|
|
})
|
|
end)
|
|
|
|
feed('i<Tab>')
|
|
eq({ '\ta' }, exec_lua('return _G.res[#_G.res]'))
|
|
end)
|
|
|
|
it('quickfix buffer send change', function()
|
|
command('copen')
|
|
exec_lua(function()
|
|
vim.api.nvim_buf_attach(vim.api.nvim_get_current_buf(), false, {
|
|
on_lines = function(...)
|
|
vim.g.qf_on_lines = { ... }
|
|
end,
|
|
on_bytes = function(...)
|
|
vim.g.qf_on_bytes = { ... }
|
|
end,
|
|
})
|
|
end)
|
|
command('caddexpr "foo"')
|
|
eq({ 'bytes', 2, 2, 0, 0, 0, 0, 0, 0, 0, 6, 6 }, api.nvim_get_var('qf_on_bytes'))
|
|
eq({ 'lines', 2, 3, 0, 1, 1, 1 }, api.nvim_get_var('qf_on_lines'))
|
|
|
|
command('caddexpr "bar"')
|
|
eq({ 'bytes', 2, 3, 0, 6, 6, 0, 0, 0, 1, 6, 6 }, api.nvim_get_var('qf_on_bytes'))
|
|
eq({ 'lines', 2, 4, 1, 1, 2, 0 }, api.nvim_get_var('qf_on_lines'))
|
|
|
|
command('caddexpr ["line1", "line2", "line3"]')
|
|
eq({ 'bytes', 2, 4, 1, 6, 13, 0, 0, 0, 3, 8, 26 }, api.nvim_get_var('qf_on_bytes'))
|
|
eq({ 'lines', 2, 5, 2, 2, 5, 0 }, api.nvim_get_var('qf_on_lines'))
|
|
|
|
command('cexpr "replace"')
|
|
eq({ 'bytes', 2, 5, 0, 0, 0, 4, 0, 40, 0, 10, 10 }, api.nvim_get_var('qf_on_bytes'))
|
|
eq({ 'lines', 2, 6, 0, 5, 1, 42 }, api.nvim_get_var('qf_on_lines'))
|
|
end)
|
|
|
|
it('single-line visual block insert should not trigger extra on_lines #22009', function()
|
|
exec_lua(function()
|
|
_G.res = {}
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_lines = function(_, bufnr, _, first, last, last_updated, _, _, _)
|
|
_G.res = { bufnr, first, last, last_updated }
|
|
end,
|
|
})
|
|
end)
|
|
feed('<C-v>I <ESC>')
|
|
eq({ api.nvim_get_current_buf(), 0, 1, 1 }, exec_lua('return _G.res'))
|
|
end)
|
|
|
|
it('prompt buffer', function()
|
|
local check_events = setup_eventcheck(false, nil, {})
|
|
api.nvim_set_option_value('buftype', 'prompt', {})
|
|
feed('i')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, 4, 0, 1, 1, 1 },
|
|
}
|
|
fn.prompt_setprompt('', 'foo > ')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, 5, 0, 1, 1, 3 },
|
|
}
|
|
feed('hello')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, 6, 0, 1, 1, 7 },
|
|
}
|
|
fn.prompt_setprompt('', 'super-foo > ')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, 7, 0, 1, 1, 12 },
|
|
}
|
|
eq({ 'super-foo > hello' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
-- Do this in the same event.
|
|
exec_lua(function()
|
|
vim.fn.setpos("':", { 0, 1, 999, 0 })
|
|
vim.fn.prompt_setprompt('', 'discard > ')
|
|
end)
|
|
check_events {
|
|
{ 'test1', 'lines', 1, 8, 0, 1, 1, 18 },
|
|
}
|
|
eq({ 'discard > ' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
feed('hello<S-CR>there')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, 9, 0, 1, 1, 11 },
|
|
{ 'test1', 'lines', 1, 10, 0, 1, 2, 16 },
|
|
{ 'test1', 'lines', 1, 11, 1, 2, 2, 1 },
|
|
}
|
|
fn.prompt_setprompt('', 'foo > ')
|
|
check_events {
|
|
{ 'test1', 'lines', 1, 12, 0, 1, 1, 16 },
|
|
}
|
|
eq({ 'foo > hello', 'there' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
end)
|
|
end)
|
|
|
|
describe('lua: nvim_buf_attach on_bytes', function()
|
|
-- verifying the sizes with nvim_buf_get_offset is nice (checks we cannot
|
|
-- assert the wrong thing), but masks errors with unflushed lines (as
|
|
-- nvim_buf_get_offset forces a flush of the memline). To be safe run the
|
|
-- test both ways.
|
|
local function setup_eventcheck(verify, start_txt)
|
|
if start_txt then
|
|
api.nvim_buf_set_lines(0, 0, -1, true, start_txt)
|
|
else
|
|
start_txt = api.nvim_buf_get_lines(0, 0, -1, true)
|
|
end
|
|
local shadowbytes = table.concat(start_txt, '\n') .. '\n'
|
|
-- TODO: while we are brewing the real strong coffee,
|
|
-- verify should check buf_get_offset after every check_events
|
|
if verify then
|
|
local len = api.nvim_buf_get_offset(0, api.nvim_buf_line_count(0))
|
|
eq(len == -1 and 1 or len, string.len(shadowbytes))
|
|
end
|
|
exec_lua('return test_register(...)', 0, 'on_bytes', 'test1', false, false, true)
|
|
api.nvim_buf_get_changedtick(0)
|
|
|
|
local verify_name = 'test1'
|
|
local function check_events(expected)
|
|
local events = exec_lua('return get_events(...)')
|
|
expect_events(expected, events, 'byte updates')
|
|
|
|
if not verify then
|
|
return
|
|
end
|
|
|
|
for _, event in ipairs(events) do
|
|
for _, elem in ipairs(event) do
|
|
if type(elem) == 'number' and elem < 0 then
|
|
fail(string.format('Received event has negative values'))
|
|
end
|
|
end
|
|
|
|
if event[1] == verify_name and event[2] == 'bytes' then
|
|
local _, _, _, _, _, _, start_byte, _, _, old_byte, _, _, new_byte = unpack(event)
|
|
local before = string.sub(shadowbytes, 1, start_byte)
|
|
-- no text in the tests will contain 0xff bytes (invalid UTF-8)
|
|
-- so we can use it as marker for unknown bytes
|
|
local unknown = string.rep('\255', new_byte)
|
|
local after = string.sub(shadowbytes, start_byte + old_byte + 1)
|
|
shadowbytes = before .. unknown .. after
|
|
elseif event[1] == verify_name and event[2] == 'reload' then
|
|
shadowbytes = table.concat(api.nvim_buf_get_lines(0, 0, -1, true), '\n') .. '\n'
|
|
end
|
|
end
|
|
|
|
local text = api.nvim_buf_get_lines(0, 0, -1, true)
|
|
local bytes = table.concat(text, '\n') .. '\n'
|
|
|
|
eq(
|
|
string.len(bytes),
|
|
string.len(shadowbytes),
|
|
'\non_bytes: total bytecount of buffer is wrong'
|
|
)
|
|
for i = 1, string.len(shadowbytes) do
|
|
local shadowbyte = string.sub(shadowbytes, i, i)
|
|
if shadowbyte ~= '\255' then
|
|
eq(string.sub(bytes, i, i), shadowbyte, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
return check_events
|
|
end
|
|
|
|
-- Yes, we can do both
|
|
local function do_both(verify)
|
|
it('single and multiple join', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
feed 'ggJ'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 15, 15, 1, 0, 1, 0, 1, 1 },
|
|
}
|
|
|
|
feed '3J'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 31, 31, 1, 0, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 47, 47, 1, 0, 1, 0, 1, 1 },
|
|
}
|
|
end)
|
|
|
|
it('opening lines', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
api.nvim_set_option_value('autoindent', false, {})
|
|
feed 'Go'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 7, 0, 114, 0, 0, 0, 1, 0, 1 },
|
|
}
|
|
feed '<cr>'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 1 },
|
|
}
|
|
end)
|
|
|
|
it('opening lines with autoindent', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
api.nvim_set_option_value('autoindent', true, {})
|
|
feed 'Go'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 7, 0, 114, 0, 0, 0, 1, 0, 5 },
|
|
}
|
|
feed '<cr>'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 4, 4, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 0, 0, 1, 4, 5 },
|
|
}
|
|
end)
|
|
|
|
it('setline(num, line)', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
fn.setline(2, 'babla')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 1, 0, 16, 0, 15, 15, 0, 5, 5 },
|
|
}
|
|
|
|
fn.setline(2, { 'foo', 'bar' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 0, 16, 0, 5, 5, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 5, 2, 0, 20, 0, 15, 15, 0, 3, 3 },
|
|
}
|
|
|
|
local buf_len = api.nvim_buf_line_count(0)
|
|
fn.setline(buf_len + 1, 'baz')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 6, 7, 0, 90, 0, 0, 0, 1, 0, 4 },
|
|
}
|
|
end)
|
|
|
|
it('continuing comments with fo=or', function()
|
|
local check_events = setup_eventcheck(verify, { '// Comment' })
|
|
api.nvim_set_option_value('formatoptions', 'ro', {})
|
|
api.nvim_set_option_value('filetype', 'c', {})
|
|
feed 'A<CR>'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 10, 10, 0, 0, 0, 1, 3, 4 },
|
|
}
|
|
|
|
feed '<ESC>'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 2, 13, 0, 1, 1, 0, 0, 0 },
|
|
}
|
|
|
|
feed 'ggo' -- goto first line to continue testing
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 1, 0, 11, 0, 0, 0, 1, 0, 4 },
|
|
}
|
|
|
|
feed '<CR>'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 6, 1, 2, 13, 0, 1, 1, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 6, 1, 2, 13, 0, 0, 0, 1, 3, 4 },
|
|
}
|
|
end)
|
|
|
|
it('editing empty buffers', function()
|
|
local check_events = setup_eventcheck(verify, {})
|
|
|
|
feed 'ia'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
|
|
}
|
|
end)
|
|
|
|
it('deleting lines', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
|
|
feed('dd')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 1, 0, 16, 0, 0, 0 },
|
|
}
|
|
|
|
feed('d2j')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 0, 0, 0, 3, 0, 48, 0, 0, 0 },
|
|
}
|
|
|
|
feed('ld<c-v>2j')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 5, 1, 1, 16, 0, 1, 1, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 5, 2, 1, 31, 0, 1, 1, 0, 0, 0 },
|
|
}
|
|
|
|
feed('vjwd')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 10, 0, 1, 1, 1, 9, 23, 0, 0, 0 },
|
|
}
|
|
end)
|
|
|
|
it('changing lines', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
|
|
feed 'cc'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 15, 15, 0, 0, 0 },
|
|
}
|
|
|
|
feed '<ESC>'
|
|
check_events {}
|
|
|
|
feed 'c3j'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 0, 1, 3, 0, 48, 0, 0, 0 },
|
|
}
|
|
end)
|
|
|
|
it('visual charwise paste', function()
|
|
local check_events = setup_eventcheck(verify, { '1234567890' })
|
|
fn.setreg('a', '___')
|
|
|
|
feed '1G1|vll'
|
|
check_events {}
|
|
|
|
feed '"ap'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 0, 0, 0, 0, 0, 0, 3, 3 },
|
|
}
|
|
end)
|
|
|
|
it('blockwise paste', function()
|
|
local check_events = setup_eventcheck(verify, { '1', '2', '3' })
|
|
feed('1G0')
|
|
feed('y<C-v>2j')
|
|
feed('G0')
|
|
feed('p')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 2, 1, 5, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 3, 3, 0, 7, 0, 0, 0, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 3, 4, 0, 10, 0, 0, 0, 0, 3, 3 },
|
|
}
|
|
|
|
feed('2G0')
|
|
feed('p')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 1, 3, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 4, 2, 1, 6, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 4, 3, 1, 10, 0, 0, 0, 0, 1, 1 },
|
|
}
|
|
|
|
feed('1G0')
|
|
feed('P')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 5, 1, 0, 3, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 5, 2, 0, 7, 0, 0, 0, 0, 1, 1 },
|
|
}
|
|
end)
|
|
|
|
it('linewise paste', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
|
|
feed 'yyp'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 1, 0, 16, 0, 0, 0, 1, 0, 16 },
|
|
}
|
|
|
|
feed 'Gyyp'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 8, 0, 130, 0, 0, 0, 1, 0, 18 },
|
|
}
|
|
end)
|
|
|
|
it('inccomand=nosplit and substitute', function()
|
|
local check_events = setup_eventcheck(verify, { 'abcde', '12345' })
|
|
api.nvim_set_option_value('inccommand', 'nosplit', {})
|
|
|
|
-- linewise substitute
|
|
feed(':%s/bcd/')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 3, 3, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 0, 0, 0, 3, 3 },
|
|
}
|
|
|
|
feed('a')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 3, 3, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 1, 1, 0, 3, 3 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
|
|
-- splitting lines
|
|
feed([[:%s/abc/\r]])
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 3, 3, 1, 0, 1 },
|
|
{ 'test1', 'bytes', 1, 6, 0, 0, 0, 1, 0, 1, 0, 3, 3 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
-- multi-line regex
|
|
feed([[:%s/de\n123/a]])
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 3, 3, 1, 3, 6, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 6, 0, 3, 3, 0, 1, 1, 1, 3, 6 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
-- replacing with unicode
|
|
feed(':%s/b/→')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 1, 1, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 3, 3, 0, 1, 1 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
-- replacing with expression register
|
|
feed([[:%s/b/\=5+5]])
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 1, 1, 0, 2, 2 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 2, 2, 0, 1, 1 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
-- replacing with backslash
|
|
feed([[:%s/b/\\]])
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 1, 1, 0, 1, 1 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
-- replacing with backslash from expression register
|
|
feed([[:%s/b/\='\']])
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 1, 1, 0, 1, 1 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
-- replacing with backslash followed by another character
|
|
feed([[:%s/b/\\!]])
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 1, 1, 0, 2, 2 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 2, 2, 0, 1, 1 },
|
|
}
|
|
|
|
feed('<esc>')
|
|
-- replacing with backslash followed by another character from expression register
|
|
feed([[:%s/b/\='\!']])
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 1, 1, 0, 2, 2 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 2, 2, 0, 1, 1 },
|
|
}
|
|
end)
|
|
|
|
it('nvim_buf_set_text insert', function()
|
|
local check_events = setup_eventcheck(verify, { 'bastext' })
|
|
api.nvim_buf_set_text(0, 0, 3, 0, 3, { 'fiol', 'kontra' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 3, 3, 0, 0, 0, 1, 6, 11 },
|
|
}
|
|
|
|
api.nvim_buf_set_text(0, 1, 6, 1, 6, { 'punkt', 'syntgitarr', 'övnings' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 6, 14, 0, 0, 0, 2, 8, 25 },
|
|
}
|
|
|
|
eq(
|
|
{ 'basfiol', 'kontrapunkt', 'syntgitarr', 'övningstext' },
|
|
api.nvim_buf_get_lines(0, 0, -1, true)
|
|
)
|
|
end)
|
|
|
|
it('nvim_buf_set_text replace', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
|
|
api.nvim_buf_set_text(0, 2, 3, 2, 8, { 'very text' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 2, 3, 35, 0, 5, 5, 0, 9, 9 },
|
|
}
|
|
|
|
api.nvim_buf_set_text(0, 3, 5, 3, 7, { ' splitty', 'line ' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 3, 5, 57, 0, 2, 2, 1, 5, 14 },
|
|
}
|
|
|
|
api.nvim_buf_set_text(0, 0, 8, 1, 2, { 'JOINY' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 8, 8, 1, 2, 10, 0, 5, 5 },
|
|
}
|
|
|
|
api.nvim_buf_set_text(0, 4, 0, 6, 0, { 'was 5,6', '' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 6, 4, 0, 75, 2, 0, 32, 1, 0, 8 },
|
|
}
|
|
|
|
eq({
|
|
'originalJOINYiginal line 2',
|
|
'orivery text line 3',
|
|
'origi splitty',
|
|
'line l line 4',
|
|
'was 5,6',
|
|
' indented line',
|
|
}, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
end)
|
|
|
|
it('nvim_buf_set_text delete', function()
|
|
local check_events = setup_eventcheck(verify, origlines)
|
|
|
|
-- really {""} but accepts {} as a shorthand
|
|
api.nvim_buf_set_text(0, 0, 0, 1, 0, {})
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 1, 0, 16, 0, 0, 0 },
|
|
}
|
|
|
|
-- TODO(bfredl): this works but is not as convenient as set_lines
|
|
api.nvim_buf_set_text(0, 4, 15, 5, 17, { '' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 4, 15, 79, 1, 17, 18, 0, 0, 0 },
|
|
}
|
|
eq({
|
|
'original line 2',
|
|
'original line 3',
|
|
'original line 4',
|
|
'original line 5',
|
|
'original line 6',
|
|
}, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
end)
|
|
|
|
it('checktime autoread', function()
|
|
write_file(
|
|
'Xtest-reload',
|
|
dedent [[
|
|
old line 1
|
|
old line 2]]
|
|
)
|
|
local atime = os.time() - 10
|
|
vim.uv.fs_utime('Xtest-reload', atime, atime)
|
|
command 'e Xtest-reload'
|
|
command 'set autoread'
|
|
|
|
local check_events = setup_eventcheck(verify, nil)
|
|
|
|
write_file(
|
|
'Xtest-reload',
|
|
dedent [[
|
|
new line 1
|
|
new line 2
|
|
new line 3]]
|
|
)
|
|
|
|
command 'checktime'
|
|
check_events {
|
|
{ 'test1', 'reload', 1 },
|
|
}
|
|
|
|
feed 'ggJ'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 10, 10, 1, 0, 1, 0, 1, 1 },
|
|
}
|
|
|
|
eq({ 'new line 1 new line 2', 'new line 3' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
|
|
-- check we can undo and redo a reload event.
|
|
feed 'u'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 8, 0, 10, 10, 0, 1, 1, 1, 0, 1 },
|
|
}
|
|
|
|
feed 'u'
|
|
check_events {
|
|
{ 'test1', 'reload', 1 },
|
|
}
|
|
|
|
feed '<c-r>'
|
|
check_events {
|
|
{ 'test1', 'reload', 1 },
|
|
}
|
|
|
|
feed '<c-r>'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 14, 0, 10, 10, 1, 0, 1, 0, 1, 1 },
|
|
}
|
|
end)
|
|
|
|
it('tab with noexpandtab and softtabstop', function()
|
|
command('set noet')
|
|
command('set ts=4')
|
|
command('set sw=2')
|
|
command('set sts=4')
|
|
|
|
local check_events = setup_eventcheck(verify, { 'asdfasdf' })
|
|
|
|
feed('gg0i<tab>')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 4, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
|
|
}
|
|
feed('<tab>')
|
|
|
|
-- when spaces are merged into a tabstop
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 2, 2, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 6, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 7, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
|
|
}
|
|
|
|
feed('<esc>u')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 9, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
|
|
{ 'test1', 'bytes', 1, 9, 0, 0, 0, 0, 4, 4, 0, 0, 0 },
|
|
}
|
|
|
|
-- in REPLACE mode
|
|
feed('R<tab><tab>')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 10, 0, 0, 0, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 11, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 12, 0, 2, 2, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 13, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 14, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
|
|
}
|
|
feed('<esc>u')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 16, 0, 0, 0, 0, 1, 1, 0, 4, 4 },
|
|
{ 'test1', 'bytes', 1, 16, 0, 2, 2, 0, 2, 2, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 16, 0, 0, 0, 0, 2, 2, 0, 1, 1 },
|
|
}
|
|
|
|
-- in VISUALREPLACE mode
|
|
feed('gR<tab><tab>')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 17, 0, 0, 0, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 18, 0, 1, 1, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 19, 0, 2, 2, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 20, 0, 3, 3, 0, 1, 1, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 21, 0, 3, 3, 0, 1, 1, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 22, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 24, 0, 2, 2, 0, 1, 1, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 25, 0, 2, 2, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 27, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 28, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 30, 0, 0, 0, 0, 1, 1, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 31, 0, 0, 0, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 33, 0, 0, 0, 0, 4, 4, 0, 1, 1 },
|
|
}
|
|
|
|
-- inserting tab after other tabs
|
|
command('set sw=4')
|
|
feed('<esc>0a<tab>')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 34, 0, 1, 1, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 35, 0, 2, 2, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 36, 0, 3, 3, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 37, 0, 4, 4, 0, 0, 0, 0, 1, 1 },
|
|
{ 'test1', 'bytes', 1, 38, 0, 1, 1, 0, 4, 4, 0, 1, 1 },
|
|
}
|
|
end)
|
|
|
|
it('retab', function()
|
|
command('set noet')
|
|
command('set ts=4')
|
|
|
|
local check_events = setup_eventcheck(verify, { ' asdf' })
|
|
command('retab 8')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 7, 7, 0, 9, 9 },
|
|
}
|
|
end)
|
|
|
|
it('sends events when undoing with undofile', function()
|
|
write_file(
|
|
'Xtest-undofile',
|
|
dedent([[
|
|
12345
|
|
hello world
|
|
]])
|
|
)
|
|
|
|
command('e! Xtest-undofile')
|
|
command('set undodir=. | set undofile')
|
|
|
|
local ns = n.request('nvim_create_namespace', 'ns1')
|
|
api.nvim_buf_set_extmark(0, ns, 0, 0, {})
|
|
|
|
eq({ '12345', 'hello world' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
|
|
-- splice
|
|
feed('gg0d2l')
|
|
|
|
eq({ '345', 'hello world' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
|
|
-- move
|
|
command('.m+1')
|
|
|
|
eq({ 'hello world', '345' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
|
|
-- reload undofile and undo changes
|
|
command('w')
|
|
command('set noundofile')
|
|
command('bw!')
|
|
command('e! Xtest-undofile')
|
|
|
|
command('set undofile')
|
|
|
|
local check_events = setup_eventcheck(verify, nil)
|
|
|
|
feed('u')
|
|
eq({ '345', 'hello world' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 2, 6, 1, 0, 12, 1, 0, 4, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 2, 6, 0, 0, 0, 0, 0, 0, 1, 0, 4 },
|
|
}
|
|
|
|
feed('u')
|
|
eq({ '12345', 'hello world' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 2, 8, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
|
|
}
|
|
command('bw!')
|
|
end)
|
|
|
|
it('blockwise paste with uneven line lengths', function()
|
|
local check_events = setup_eventcheck(verify, { 'aaaa', 'aaa', 'aaa' })
|
|
|
|
-- eq({}, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
feed('gg0<c-v>jj$d')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 4, 4, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 3, 1, 0, 1, 0, 3, 3, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 3, 2, 0, 2, 0, 3, 3, 0, 0, 0 },
|
|
}
|
|
|
|
feed('p')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 0, 0, 0, 0, 0, 0, 0, 4, 4 },
|
|
{ 'test1', 'bytes', 1, 4, 1, 0, 5, 0, 0, 0, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 4, 2, 0, 9, 0, 0, 0, 0, 3, 3 },
|
|
}
|
|
end)
|
|
|
|
it(':luado', function()
|
|
local check_events = setup_eventcheck(verify, { 'abc', '12345' })
|
|
|
|
command(".luado return 'a'")
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 3, 3, 0, 1, 1 },
|
|
}
|
|
|
|
command('luado return 10')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 0, 0, 0, 0, 1, 1, 0, 2, 2 },
|
|
{ 'test1', 'bytes', 1, 5, 1, 0, 3, 0, 5, 5, 0, 2, 2 },
|
|
}
|
|
end)
|
|
|
|
it('flushes deleted bytes on move', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB', 'CCC', 'DDD' })
|
|
|
|
feed(':.move+1<cr>')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 0, 0, 1, 0, 4, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 5, 1, 0, 4, 0, 0, 0, 1, 0, 4 },
|
|
}
|
|
|
|
feed('jd2j')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 6, 2, 0, 8, 2, 0, 8, 0, 0, 0 },
|
|
}
|
|
end)
|
|
|
|
it('virtual edit', function()
|
|
local check_events = setup_eventcheck(verify, { '', ' ' })
|
|
|
|
api.nvim_set_option_value('virtualedit', 'all', {})
|
|
|
|
feed [[<Right><Right>iab<ESC>]]
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
|
|
{ 'test1', 'bytes', 1, 4, 0, 2, 2, 0, 0, 0, 0, 2, 2 },
|
|
}
|
|
|
|
feed [[j<Right><Right>iab<ESC>]]
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 1, 0, 5, 0, 1, 1, 0, 8, 8 },
|
|
{ 'test1', 'bytes', 1, 6, 1, 5, 10, 0, 0, 0, 0, 2, 2 },
|
|
}
|
|
end)
|
|
|
|
it('block visual paste', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'FFF' })
|
|
fn.setreg('a', '___')
|
|
feed([[gg0l<c-v>3jl"ap]])
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 1, 1, 0, 2, 2, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 3, 1, 1, 3, 0, 2, 2, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 3, 2, 1, 5, 0, 2, 2, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 3, 3, 1, 7, 0, 2, 2, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 5, 0, 1, 1, 0, 0, 0, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 6, 1, 1, 6, 0, 0, 0, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 7, 2, 1, 11, 0, 0, 0, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 8, 3, 1, 16, 0, 0, 0, 0, 3, 3 },
|
|
}
|
|
end)
|
|
|
|
it('visual paste', function()
|
|
local check_events = setup_eventcheck(verify, { 'aaa {', 'b', '}' })
|
|
-- Setting up
|
|
feed [[jdd]]
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 1, 0, 6, 1, 0, 2, 0, 0, 0 },
|
|
}
|
|
|
|
-- Actually testing
|
|
feed [[v%p]]
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 8, 0, 4, 4, 1, 1, 3, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 8, 0, 4, 4, 0, 0, 0, 2, 0, 3 },
|
|
}
|
|
end)
|
|
|
|
it('visual paste 2: splitting an empty line', function()
|
|
local check_events = setup_eventcheck(verify, { 'abc', '{', 'def', '}' })
|
|
feed('ggyyjjvi{p')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 6, 2, 0, 6, 1, 0, 4, 0, 0, 0 },
|
|
{ 'test1', 'bytes', 1, 6, 2, 0, 6, 0, 0, 0, 2, 0, 5 },
|
|
}
|
|
end)
|
|
|
|
it('nvim_buf_set_lines', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB' })
|
|
|
|
-- delete
|
|
api.nvim_buf_set_lines(0, 0, 1, true, {})
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 1, 0, 4, 0, 0, 0 },
|
|
}
|
|
|
|
-- add
|
|
api.nvim_buf_set_lines(0, 0, 0, true, { 'asdf' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 0, 0, 0, 0, 0, 0, 1, 0, 5 },
|
|
}
|
|
|
|
-- replace
|
|
api.nvim_buf_set_lines(0, 0, 1, true, { 'asdf', 'fdsa' })
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 0, 0, 1, 0, 5, 2, 0, 10 },
|
|
}
|
|
end)
|
|
|
|
it('flushes delbytes on substitute', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB', 'CCC' })
|
|
|
|
feed('gg0')
|
|
command('s/AAA/GGG/')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 3, 3, 0, 3, 3 },
|
|
}
|
|
|
|
-- check that byte updates for :delete (which uses curbuf->deleted_bytes2)
|
|
-- are correct
|
|
command('delete')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 0, 0, 0, 1, 0, 4, 0, 0, 0 },
|
|
}
|
|
end)
|
|
|
|
it('on_bytes sees modified buffer after substitute', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'Hello' })
|
|
|
|
local buffer_lines = exec_lua(function()
|
|
local lines
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_bytes = function()
|
|
lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
|
end,
|
|
})
|
|
vim.cmd('s/llo/y/')
|
|
return lines
|
|
end)
|
|
|
|
-- Make sure on_bytes is called after the buffer is modified.
|
|
eq({ 'Hey' }, buffer_lines)
|
|
end)
|
|
|
|
it('on_bytes called multiple times for multiple substitutions on same line', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'Hello Hello' })
|
|
|
|
local call_count, args = exec_lua(function()
|
|
local count = 0
|
|
local args = {}
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_bytes = function(
|
|
_,
|
|
_,
|
|
_,
|
|
start_row,
|
|
start_col,
|
|
start_byte,
|
|
old_row,
|
|
old_col,
|
|
old_byte,
|
|
new_row,
|
|
new_col,
|
|
new_byte
|
|
)
|
|
count = count + 1
|
|
table.insert(args, {
|
|
start_row = start_row,
|
|
start_col = start_col,
|
|
start_byte = start_byte,
|
|
old_row = old_row,
|
|
old_col = old_col,
|
|
old_byte = old_byte,
|
|
new_row = new_row,
|
|
new_col = new_col,
|
|
new_byte = new_byte,
|
|
buffer_lines = vim.api.nvim_buf_get_lines(0, 0, -1, true),
|
|
})
|
|
end,
|
|
})
|
|
vim.cmd('s/llo/y/g')
|
|
return count, args
|
|
end)
|
|
|
|
-- Should be called twice, once for each match.
|
|
eq(2, call_count)
|
|
|
|
-- First match: "llo" at column 2 -> "y".
|
|
eq({
|
|
start_row = 0,
|
|
start_col = 2,
|
|
start_byte = 2,
|
|
old_row = 0,
|
|
old_col = 3,
|
|
old_byte = 3,
|
|
new_row = 0,
|
|
new_col = 1,
|
|
new_byte = 1,
|
|
buffer_lines = { 'Hey Hey' },
|
|
}, args[1])
|
|
|
|
-- Second match: "llo" at column 8 (in original) -> column 6 (after first substitution).
|
|
eq({
|
|
start_row = 0,
|
|
start_col = 6, -- Adjusted position after first substitution.
|
|
start_byte = 6,
|
|
old_row = 0,
|
|
old_col = 3,
|
|
old_byte = 3,
|
|
new_row = 0,
|
|
new_col = 1,
|
|
new_byte = 1,
|
|
buffer_lines = { 'Hey Hey' },
|
|
}, args[2])
|
|
end)
|
|
|
|
it('on_bytes called correctly for multi-line substitutions', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'foo bar', 'baz qux' })
|
|
|
|
local call_count, args = exec_lua(function()
|
|
local count = 0
|
|
local args = {}
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_bytes = function(
|
|
_,
|
|
_,
|
|
_,
|
|
start_row,
|
|
start_col,
|
|
start_byte,
|
|
old_row,
|
|
old_col,
|
|
old_byte,
|
|
new_row,
|
|
new_col,
|
|
new_byte
|
|
)
|
|
count = count + 1
|
|
table.insert(args, {
|
|
start_row = start_row,
|
|
start_col = start_col,
|
|
start_byte = start_byte,
|
|
old_row = old_row,
|
|
old_col = old_col,
|
|
old_byte = old_byte,
|
|
new_row = new_row,
|
|
new_col = new_col,
|
|
new_byte = new_byte,
|
|
buffer_lines = vim.api.nvim_buf_get_lines(0, 0, -1, true),
|
|
})
|
|
end,
|
|
})
|
|
vim.cmd('s/bar/X\\rY/')
|
|
return count, args
|
|
end)
|
|
|
|
-- Should be called once for the substitution.
|
|
eq(1, call_count)
|
|
|
|
eq({
|
|
start_row = 0,
|
|
start_col = 4,
|
|
start_byte = 4,
|
|
old_row = 0,
|
|
old_col = 3,
|
|
old_byte = 3,
|
|
new_row = 1,
|
|
new_col = 1,
|
|
new_byte = 3,
|
|
buffer_lines = { 'foo X', 'Y', 'baz qux' },
|
|
}, args[1])
|
|
end)
|
|
|
|
it('on_bytes called multiple times for global substitution creating multiple lines', function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'foo bar baz' })
|
|
|
|
local call_count, args = exec_lua(function()
|
|
local count = 0
|
|
local args = {}
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_bytes = function(
|
|
_,
|
|
_,
|
|
_,
|
|
start_row,
|
|
start_col,
|
|
start_byte,
|
|
old_row,
|
|
old_col,
|
|
old_byte,
|
|
new_row,
|
|
new_col,
|
|
new_byte
|
|
)
|
|
count = count + 1
|
|
table.insert(args, {
|
|
start_row = start_row,
|
|
start_col = start_col,
|
|
start_byte = start_byte,
|
|
old_row = old_row,
|
|
old_col = old_col,
|
|
old_byte = old_byte,
|
|
new_row = new_row,
|
|
new_col = new_col,
|
|
new_byte = new_byte,
|
|
buffer_lines = vim.api.nvim_buf_get_lines(0, 0, -1, true),
|
|
})
|
|
end,
|
|
})
|
|
-- Global substitution with newlines in replacement.
|
|
vim.cmd([[s/ /\r/g]])
|
|
return count, args
|
|
end)
|
|
|
|
-- Should be called once per space replacement.
|
|
eq(2, call_count)
|
|
|
|
eq({
|
|
start_row = 0,
|
|
start_col = 3,
|
|
start_byte = 3,
|
|
old_row = 0,
|
|
old_col = 1,
|
|
old_byte = 1,
|
|
new_row = 1,
|
|
new_col = 0,
|
|
new_byte = 1,
|
|
buffer_lines = { 'foo', 'bar', 'baz' },
|
|
}, args[1])
|
|
|
|
eq({
|
|
start_row = 1,
|
|
start_col = 3,
|
|
start_byte = 7,
|
|
old_row = 0,
|
|
old_col = 1,
|
|
old_byte = 1,
|
|
new_row = 1,
|
|
new_col = 0,
|
|
new_byte = 1,
|
|
buffer_lines = { 'foo', 'bar', 'baz' },
|
|
}, args[2])
|
|
end)
|
|
|
|
it(
|
|
'no buffer update event is emitted while editing substitute command, only after confirmation',
|
|
function()
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'Hello world', 'Hello Neovim' })
|
|
|
|
exec_lua(function()
|
|
_G.num_buffer_updates = 0
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_bytes = function()
|
|
_G.num_buffer_updates = _G.num_buffer_updates + 1
|
|
end,
|
|
})
|
|
end)
|
|
|
|
-- Start typing the substitute command - no events should be emitted yet.
|
|
feed(':%s/Hello/Hi')
|
|
eq(0, exec_lua('return _G.num_buffer_updates'))
|
|
|
|
-- Continue editing the command - still no events.
|
|
feed('<BS><BS>Hey')
|
|
eq(0, exec_lua('return _G.num_buffer_updates'))
|
|
|
|
-- After confirming the substitution, two events should be emitted (one per line).
|
|
feed('<CR>')
|
|
eq(2, exec_lua('return _G.num_buffer_updates'))
|
|
|
|
-- Verify the buffer was actually modified.
|
|
eq({ 'Hey world', 'Hey Neovim' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
end
|
|
)
|
|
|
|
it('flushes delbytes on join', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB', 'CCC' })
|
|
|
|
feed('gg0J')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 3, 3, 1, 0, 1, 0, 1, 1 },
|
|
}
|
|
|
|
command('delete')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 0, 0, 0, 1, 0, 8, 0, 0, 0 },
|
|
}
|
|
end)
|
|
|
|
it('sends updates on U', function()
|
|
feed('ggiAAA<cr>BBB')
|
|
feed('<esc>gg$a CCC')
|
|
|
|
local check_events = setup_eventcheck(verify, nil)
|
|
|
|
feed('ggU')
|
|
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 6, 0, 7, 7, 0, 0, 0, 0, 3, 3 },
|
|
}
|
|
end)
|
|
|
|
it('delete in completely empty buffer', function()
|
|
local check_events = setup_eventcheck(verify, nil)
|
|
|
|
command 'delete'
|
|
check_events {}
|
|
end)
|
|
|
|
it('delete the only line of a buffer', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA' })
|
|
|
|
command 'delete'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 1, 0, 4, 1, 0, 1 },
|
|
}
|
|
end)
|
|
|
|
it('delete the last line of a buffer with two lines', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB' })
|
|
|
|
command '2delete'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 1, 0, 4, 1, 0, 4, 0, 0, 0 },
|
|
}
|
|
end)
|
|
|
|
it(':sort lines', function()
|
|
local check_events = setup_eventcheck(verify, { 'CCC', 'BBB', 'AAA' })
|
|
|
|
command '%sort'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 3, 0, 12, 3, 0, 12 },
|
|
}
|
|
end)
|
|
|
|
it('handles already sorted lines', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB', 'CCC' })
|
|
|
|
command '%sort'
|
|
check_events {}
|
|
end)
|
|
|
|
it('works with accepting spell suggestions', function()
|
|
local check_events = setup_eventcheck(verify, { 'hallo world', 'hallo world' })
|
|
|
|
feed('gg0z=4<cr><cr>') -- accepts 'Hello'
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 2, 2, 0, 2, 2 },
|
|
}
|
|
|
|
command('spellrepall') -- replaces whole words
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 0, 12, 0, 5, 5, 0, 5, 5 },
|
|
}
|
|
end)
|
|
|
|
it('works with :diffput and :diffget', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA' })
|
|
command('diffthis')
|
|
command('new')
|
|
command('diffthis')
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'AAA', 'BBB' })
|
|
feed('G')
|
|
command('diffput')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 1, 0, 4, 0, 0, 0, 1, 0, 4 },
|
|
}
|
|
api.nvim_buf_set_lines(0, 0, -1, true, { 'AAA', 'CCC' })
|
|
feed('<C-w>pG')
|
|
command('diffget')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 0, 4, 1, 0, 4, 1, 0, 4 },
|
|
}
|
|
end)
|
|
|
|
it('prompt buffer', function()
|
|
local check_events = setup_eventcheck(verify, {})
|
|
api.nvim_set_option_value('buftype', 'prompt', {})
|
|
feed('i')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 0, 0, 0, 2, 2 },
|
|
}
|
|
feed('<CR>')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 1, 0, 3, 0, 0, 0, 1, 0, 1 },
|
|
{ 'test1', 'bytes', 1, 5, 1, 0, 3, 0, 0, 0, 0, 2, 2 },
|
|
}
|
|
feed('<CR>')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 6, 2, 0, 6, 0, 0, 0, 1, 0, 1 },
|
|
{ 'test1', 'bytes', 1, 7, 2, 0, 6, 0, 0, 0, 0, 2, 2 },
|
|
}
|
|
fn.prompt_setprompt('', 'foo > ')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 8, 2, 0, 6, 0, 2, 2, 0, 6, 6 },
|
|
}
|
|
feed('hello')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 9, 2, 6, 12, 0, 0, 0, 0, 5, 5 },
|
|
}
|
|
fn.prompt_setprompt('', 'uber-foo > ')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 10, 2, 0, 6, 0, 6, 6, 0, 11, 11 },
|
|
}
|
|
eq({ '% ', '% ', 'uber-foo > hello' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
-- Do this in the same event.
|
|
exec_lua(function()
|
|
vim.fn.setpos("':", { 0, vim.fn.line('.'), 999, 0 })
|
|
vim.fn.prompt_setprompt('', 'discard > ')
|
|
end)
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 11, 2, 0, 6, 0, 16, 16, 0, 10, 10 },
|
|
}
|
|
eq({ '% ', '% ', 'discard > ' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
feed('sup<S-CR>dood')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 12, 2, 10, 16, 0, 0, 0, 0, 3, 3 },
|
|
{ 'test1', 'bytes', 1, 13, 2, 13, 19, 0, 0, 0, 1, 0, 1 },
|
|
{ 'test1', 'bytes', 1, 14, 3, 0, 20, 0, 0, 0, 0, 4, 4 },
|
|
}
|
|
eq({ '% ', '% ', 'discard > sup', 'dood' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
fn.prompt_setprompt('', 'cool > ')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 15, 2, 0, 6, 0, 10, 10, 0, 7, 7 },
|
|
}
|
|
eq({ '% ', '% ', 'cool > sup', 'dood' }, api.nvim_buf_get_lines(0, 0, -1, true))
|
|
end)
|
|
|
|
local function test_lockmarks(mode)
|
|
local description = (mode ~= '') and mode or '(baseline)'
|
|
it('test_lockmarks ' .. description .. ' %delete _', function()
|
|
local check_events = setup_eventcheck(verify, { 'AAA', 'BBB', 'CCC' })
|
|
|
|
command(mode .. ' %delete _')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 0, 0, 0, 3, 0, 12, 1, 0, 1 },
|
|
}
|
|
end)
|
|
|
|
it('test_lockmarks ' .. description .. ' append()', function()
|
|
local check_events = setup_eventcheck(verify)
|
|
|
|
command(mode .. " call append(0, 'CCC')")
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 2, 0, 0, 0, 0, 0, 0, 1, 0, 4 },
|
|
}
|
|
|
|
command(mode .. " call append(1, 'BBBB')")
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 3, 1, 0, 4, 0, 0, 0, 1, 0, 5 },
|
|
}
|
|
|
|
command(mode .. " call append(2, '')")
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 4, 2, 0, 9, 0, 0, 0, 1, 0, 1 },
|
|
}
|
|
|
|
command(mode .. ' $delete _')
|
|
check_events {
|
|
{ 'test1', 'bytes', 1, 5, 3, 0, 10, 1, 0, 1, 0, 0, 0 },
|
|
}
|
|
|
|
eq('CCC|BBBB|', table.concat(api.nvim_buf_get_lines(0, 0, -1, true), '|'))
|
|
end)
|
|
end
|
|
|
|
-- check that behavior is identical with and without "lockmarks"
|
|
test_lockmarks ''
|
|
test_lockmarks 'lockmarks'
|
|
|
|
teardown(function()
|
|
os.remove 'Xtest-reload'
|
|
os.remove 'Xtest-undofile'
|
|
os.remove '.Xtest-undofile.un~'
|
|
end)
|
|
end
|
|
|
|
describe('(with verify) handles', function()
|
|
do_both(true)
|
|
end)
|
|
|
|
describe('(without verify) handles', function()
|
|
do_both(false)
|
|
end)
|
|
end)
|
|
|
|
describe('nvim_buf_attach on_detach', function()
|
|
it('does not SEGFAULT when accessing window buffer info #14998', function()
|
|
local code = function()
|
|
local buf = vim.api.nvim_create_buf(false, false)
|
|
|
|
vim.cmd 'split'
|
|
vim.api.nvim_win_set_buf(0, buf)
|
|
|
|
vim.api.nvim_buf_attach(buf, false, {
|
|
on_detach = function(_, buf0)
|
|
vim.fn.tabpagebuflist()
|
|
vim.fn.win_findbuf(buf0)
|
|
end,
|
|
})
|
|
end
|
|
|
|
exec_lua(code)
|
|
command('q!')
|
|
n.assert_alive()
|
|
|
|
exec_lua(code)
|
|
command('bd!')
|
|
n.assert_alive()
|
|
end)
|
|
|
|
it('no invalid lnum error for closed memline #31251', function()
|
|
eq(vim.NIL, exec_lua('return _G.did_detach'))
|
|
exec_lua(function()
|
|
vim.api.nvim_buf_set_lines(0, 0, -1, false, { '' })
|
|
local bufname = 'buf2'
|
|
local buf = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_set_name(buf, bufname)
|
|
vim.bo[buf].bufhidden = 'wipe'
|
|
vim.cmd('vertical diffsplit ' .. bufname)
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_detach = function()
|
|
vim.cmd('redraw')
|
|
_G.did_detach = true
|
|
end,
|
|
})
|
|
vim.cmd.bdelete()
|
|
end)
|
|
eq(true, exec_lua('return _G.did_detach'))
|
|
end)
|
|
|
|
it('called before buf_freeall autocommands', function()
|
|
exec_lua(function()
|
|
vim.api.nvim_create_autocmd({ 'BufUnload', 'BufDelete', 'BufWipeout' }, {
|
|
callback = function(args)
|
|
table.insert(
|
|
_G.events,
|
|
('%s: %d %s'):format(
|
|
args.event,
|
|
args.buf,
|
|
tostring(vim.api.nvim_buf_is_loaded(args.buf))
|
|
)
|
|
)
|
|
end,
|
|
})
|
|
function _G.on_detach(_, b)
|
|
table.insert(
|
|
_G.events,
|
|
('on_detach: %d %s'):format(b, tostring(vim.api.nvim_buf_is_loaded(b)))
|
|
)
|
|
end
|
|
_G.events = {}
|
|
vim.cmd 'new'
|
|
vim.bo.bufhidden = 'wipe'
|
|
vim.api.nvim_buf_attach(0, false, { on_detach = _G.on_detach })
|
|
vim.cmd 'quit!'
|
|
end)
|
|
|
|
eq(
|
|
{ 'on_detach: 2 true', 'BufUnload: 2 true', 'BufDelete: 2 true', 'BufWipeout: 2 true' },
|
|
exec_lua('return _G.events')
|
|
)
|
|
eq(false, api.nvim_buf_is_valid(2))
|
|
|
|
exec_lua(function()
|
|
_G.events = {}
|
|
local buf = vim.api.nvim_create_buf(false, true)
|
|
vim.api.nvim_buf_attach(buf, false, { on_detach = _G.on_detach })
|
|
vim.api.nvim_buf_delete(buf, { force = true })
|
|
end)
|
|
|
|
-- Was unlisted, so no BufDelete.
|
|
eq(
|
|
{ 'on_detach: 3 true', 'BufUnload: 3 true', 'BufWipeout: 3 true' },
|
|
exec_lua('return _G.events')
|
|
)
|
|
eq(false, api.nvim_buf_is_valid(3))
|
|
|
|
exec_lua(function()
|
|
_G.events = {}
|
|
vim.api.nvim_buf_attach(1, false, { on_detach = _G.on_detach })
|
|
vim.api.nvim_create_autocmd('BufUnload', {
|
|
buffer = 1,
|
|
once = true,
|
|
callback = function()
|
|
vim.api.nvim_buf_attach(1, false, {
|
|
on_detach = function(...)
|
|
vim.fn.bufload(1) -- Leaks the memfile it were to run inside free_buffer_stuff.
|
|
return _G.on_detach(...)
|
|
end,
|
|
})
|
|
table.insert(_G.events, 'local BufUnload')
|
|
end,
|
|
})
|
|
vim.cmd 'edit asdf' -- Reuses buffer 1.
|
|
end)
|
|
|
|
-- on_detach shouldn't run after autocommands when reusing a buffer (in free_buffer_stuff), even
|
|
-- if those autocommands registered it, as curbuf may be in a semi-unloaded state at that point.
|
|
eq({
|
|
'on_detach: 1 true',
|
|
'BufUnload: 1 true',
|
|
'local BufUnload',
|
|
'BufDelete: 1 true',
|
|
'BufWipeout: 1 true',
|
|
}, exec_lua('return _G.events'))
|
|
|
|
exec_lua(function()
|
|
_G.events = {}
|
|
vim.api.nvim_buf_attach(0, false, { on_detach = _G.on_detach })
|
|
vim.cmd 'edit'
|
|
end)
|
|
|
|
-- Re-edit buffer; on_detach is called.
|
|
eq({ 'on_detach: 1 true', 'BufUnload: 1 true' }, exec_lua('return _G.events'))
|
|
eq(true, api.nvim_buf_is_valid(1))
|
|
|
|
exec_lua(function()
|
|
vim.cmd '%bwipeout!'
|
|
vim.bo.modified = true
|
|
_G.events = {}
|
|
vim.api.nvim_buf_attach(0, false, { on_detach = _G.on_detach })
|
|
vim.api.nvim_buf_delete(0, { force = true })
|
|
end)
|
|
|
|
-- on_detach must still be first when wiping the last buffer if it's listed and non-reusable.
|
|
-- Previously: BufUnload → BufDelete → on_detach → BufWipeout.
|
|
eq(
|
|
{ 'on_detach: 4 true', 'BufUnload: 4 true', 'BufDelete: 4 true', 'BufWipeout: 4 false' },
|
|
exec_lua('return _G.events')
|
|
)
|
|
end)
|
|
|
|
it('disallows splitting', function()
|
|
command('new | setlocal bufhidden=wipe')
|
|
local buf = api.nvim_get_current_buf()
|
|
exec_lua(function()
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_detach = function()
|
|
-- Used to allow opening more views into a closing buffer, resulting in open windows to an
|
|
-- unloaded buffer.
|
|
vim.cmd [=[execute "normal! \<C-W>s"]=]
|
|
end,
|
|
})
|
|
end)
|
|
matches('E1159: Cannot split a window when closing the buffer$', pcall_err(command, 'quit!'))
|
|
eq({}, fn.win_findbuf(buf))
|
|
eq(false, api.nvim_buf_is_valid(buf))
|
|
end)
|
|
end)
|
|
|
|
it('nvim_buf_attach from buf_freeall autocommands does not leak', function()
|
|
exec_lua(function()
|
|
local b = vim.api.nvim_create_buf(true, true)
|
|
vim.api.nvim_create_autocmd('BufWipeout', {
|
|
buffer = b,
|
|
once = true,
|
|
callback = function()
|
|
vim.api.nvim_buf_attach(b, false, {})
|
|
_G.autocmd_fired = true
|
|
end,
|
|
})
|
|
vim.api.nvim_buf_delete(b, { force = true })
|
|
end)
|
|
eq(true, exec_lua('return _G.autocmd_fired'))
|
|
end)
|