mirror of
https://github.com/neovim/neovim.git
synced 2025-09-09 12:58:16 +00:00

Add new "splice" interface for tracking buffer changes at the byte level. This will later be reused for byte-resolution buffer updates. (Implementation has been started, but using undocumented "_on_bytes" option now as interface hasn't been finalized). Use this interface to improve many edge cases of extmark adjustment. Changed tests indicate previously incorrect behavior. Adding tests for more edge cases will be follow-up work (overlaps on_bytes tests) Don't consider creation/deletion of marks an undoable event by itself. This behavior was never documented, and imposes complexity for little gain. Add nvim__buf_add_decoration temporary API for direct access to the new implementation. This should be refactored into a proper API for decorations, probably involving a huge dict. fixes #11598
1479 lines
47 KiB
Lua
1479 lines
47 KiB
Lua
local helpers = require('test.functional.helpers')(after_each)
|
||
local Screen = require('test.functional.ui.screen')
|
||
|
||
local request = helpers.request
|
||
local eq = helpers.eq
|
||
local ok = helpers.ok
|
||
local curbufmeths = helpers.curbufmeths
|
||
local bufmeths = helpers.bufmeths
|
||
local pcall_err = helpers.pcall_err
|
||
local insert = helpers.insert
|
||
local feed = helpers.feed
|
||
local clear = helpers.clear
|
||
local command = helpers.command
|
||
local meths = helpers.meths
|
||
|
||
local function expect(contents)
|
||
return eq(contents, helpers.curbuf_contents())
|
||
end
|
||
|
||
local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end
|
||
local rv = curbufmeths.get_extmark_by_id(ns, mark)
|
||
eq({er, ec}, rv)
|
||
feed("u")
|
||
rv = curbufmeths.get_extmark_by_id(ns, mark)
|
||
eq({sr, sc}, rv)
|
||
feed("<c-r>")
|
||
rv = curbufmeths.get_extmark_by_id(ns, mark)
|
||
eq({er, ec}, rv)
|
||
end
|
||
|
||
local function set_extmark(ns_id, id, line, col, opts)
|
||
if opts == nil then
|
||
opts = {}
|
||
end
|
||
return curbufmeths.set_extmark(ns_id, id, line, col, opts)
|
||
end
|
||
|
||
local function get_extmarks(ns_id, start, end_, opts)
|
||
if opts == nil then
|
||
opts = {}
|
||
end
|
||
return curbufmeths.get_extmarks(ns_id, start, end_, opts)
|
||
end
|
||
|
||
local function batch_set(ns_id, positions)
|
||
local ids = {}
|
||
for _, pos in ipairs(positions) do
|
||
table.insert(ids, set_extmark(ns_id, 0, pos[1], pos[2]))
|
||
end
|
||
return ids
|
||
end
|
||
|
||
local function batch_check(ns_id, ids, positions)
|
||
local actual, expected = {}, {}
|
||
for i,id in ipairs(ids) do
|
||
expected[id] = positions[i]
|
||
end
|
||
for _, mark in pairs(get_extmarks(ns_id, 0, -1, {})) do
|
||
actual[mark[1]] = {mark[2], mark[3]}
|
||
end
|
||
eq(expected, actual)
|
||
end
|
||
|
||
local function batch_check_undo_redo(ns_id, ids, before, after)
|
||
batch_check(ns_id, ids, after)
|
||
feed("u")
|
||
batch_check(ns_id, ids, before)
|
||
feed("<c-r>")
|
||
batch_check(ns_id, ids, after)
|
||
end
|
||
|
||
describe('API/extmarks', function()
|
||
local screen
|
||
local marks, positions, init_text, row, col
|
||
local ns, ns2
|
||
|
||
before_each(function()
|
||
-- Initialize some namespaces and insert 12345 into a buffer
|
||
marks = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
||
positions = {{0, 0,}, {0, 2}, {0, 3}}
|
||
|
||
init_text = "12345"
|
||
row = 0
|
||
col = 2
|
||
|
||
clear()
|
||
|
||
insert(init_text)
|
||
ns = request('nvim_create_namespace', "my-fancy-plugin")
|
||
ns2 = request('nvim_create_namespace', "my-fancy-plugin2")
|
||
end)
|
||
|
||
it('adds, updates and deletes marks', function()
|
||
local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
|
||
eq(marks[1], rv)
|
||
rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({positions[1][1], positions[1][2]}, rv)
|
||
-- Test adding a second mark on same row works
|
||
rv = set_extmark(ns, marks[2], positions[2][1], positions[2][2])
|
||
eq(marks[2], rv)
|
||
|
||
-- Test an update, (same pos)
|
||
rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
|
||
eq(marks[1], rv)
|
||
rv = curbufmeths.get_extmark_by_id(ns, marks[2])
|
||
eq({positions[2][1], positions[2][2]}, rv)
|
||
-- Test an update, (new pos)
|
||
row = positions[1][1]
|
||
col = positions[1][2] + 1
|
||
rv = set_extmark(ns, marks[1], row, col)
|
||
eq(marks[1], rv)
|
||
rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({row, col}, rv)
|
||
|
||
-- remove the test marks
|
||
eq(true, curbufmeths.del_extmark(ns, marks[1]))
|
||
eq(false, curbufmeths.del_extmark(ns, marks[1]))
|
||
eq(true, curbufmeths.del_extmark(ns, marks[2]))
|
||
eq(false, curbufmeths.del_extmark(ns, marks[3]))
|
||
eq(false, curbufmeths.del_extmark(ns, 1000))
|
||
end)
|
||
|
||
it('can clear a specific namespace range', function()
|
||
set_extmark(ns, 1, 0, 1)
|
||
set_extmark(ns2, 1, 0, 1)
|
||
-- force a new undo buffer
|
||
feed('o<esc>')
|
||
curbufmeths.clear_namespace(ns2, 0, -1)
|
||
eq({{1, 0, 1}}, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
|
||
feed('u')
|
||
eq({{1, 0, 1}}, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
|
||
feed('<c-r>')
|
||
eq({{1, 0, 1}}, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
|
||
end)
|
||
|
||
it('can clear a namespace range using 0,-1', function()
|
||
set_extmark(ns, 1, 0, 1)
|
||
set_extmark(ns2, 1, 0, 1)
|
||
-- force a new undo buffer
|
||
feed('o<esc>')
|
||
curbufmeths.clear_namespace(-1, 0, -1)
|
||
eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
|
||
feed('u')
|
||
eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
|
||
feed('<c-r>')
|
||
eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
|
||
end)
|
||
|
||
it('querying for information and ranges', function()
|
||
--marks = {1, 2, 3}
|
||
--positions = {{0, 0,}, {0, 2}, {0, 3}}
|
||
-- add some more marks
|
||
for i, m in ipairs(marks) do
|
||
if positions[i] ~= nil then
|
||
local rv = set_extmark(ns, m, positions[i][1], positions[i][2])
|
||
eq(m, rv)
|
||
end
|
||
end
|
||
|
||
-- {0, 0} and {-1, -1} work as extreme values
|
||
eq({{1, 0, 0}}, get_extmarks(ns, {0, 0}, {0, 0}))
|
||
eq({}, get_extmarks(ns, {-1, -1}, {-1, -1}))
|
||
local rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
for i, m in ipairs(marks) do
|
||
if positions[i] ~= nil then
|
||
eq({m, positions[i][1], positions[i][2]}, rv[i])
|
||
end
|
||
end
|
||
|
||
-- 0 and -1 works as short hand extreme values
|
||
eq({{1, 0, 0}}, get_extmarks(ns, 0, 0))
|
||
eq({}, get_extmarks(ns, -1, -1))
|
||
rv = get_extmarks(ns, 0, -1)
|
||
for i, m in ipairs(marks) do
|
||
if positions[i] ~= nil then
|
||
eq({m, positions[i][1], positions[i][2]}, rv[i])
|
||
end
|
||
end
|
||
|
||
-- next with mark id
|
||
rv = get_extmarks(ns, marks[1], {-1, -1}, {limit=1})
|
||
eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
|
||
rv = get_extmarks(ns, marks[2], {-1, -1}, {limit=1})
|
||
eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
|
||
-- next with positional when mark exists at position
|
||
rv = get_extmarks(ns, positions[1], {-1, -1}, {limit=1})
|
||
eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
|
||
-- next with positional index (no mark at position)
|
||
rv = get_extmarks(ns, {positions[1][1], positions[1][2] +1}, {-1, -1}, {limit=1})
|
||
eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
|
||
-- next with Extremity index
|
||
rv = get_extmarks(ns, {0,0}, {-1, -1}, {limit=1})
|
||
eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
|
||
|
||
-- nextrange with mark id
|
||
rv = get_extmarks(ns, marks[1], marks[3])
|
||
eq({marks[1], positions[1][1], positions[1][2]}, rv[1])
|
||
eq({marks[2], positions[2][1], positions[2][2]}, rv[2])
|
||
-- nextrange with `limit`
|
||
rv = get_extmarks(ns, marks[1], marks[3], {limit=2})
|
||
eq(2, table.getn(rv))
|
||
-- nextrange with positional when mark exists at position
|
||
rv = get_extmarks(ns, positions[1], positions[3])
|
||
eq({marks[1], positions[1][1], positions[1][2]}, rv[1])
|
||
eq({marks[2], positions[2][1], positions[2][2]}, rv[2])
|
||
rv = get_extmarks(ns, positions[2], positions[3])
|
||
eq(2, table.getn(rv))
|
||
-- nextrange with positional index (no mark at position)
|
||
local lower = {positions[1][1], positions[2][2] -1}
|
||
local upper = {positions[2][1], positions[3][2] - 1}
|
||
rv = get_extmarks(ns, lower, upper)
|
||
eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
|
||
lower = {positions[3][1], positions[3][2] + 1}
|
||
upper = {positions[3][1], positions[3][2] + 2}
|
||
rv = get_extmarks(ns, lower, upper)
|
||
eq({}, rv)
|
||
-- nextrange with extremity index
|
||
lower = {positions[2][1], positions[2][2]+1}
|
||
upper = {-1, -1}
|
||
rv = get_extmarks(ns, lower, upper)
|
||
eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
|
||
|
||
-- prev with mark id
|
||
rv = get_extmarks(ns, marks[3], {0, 0}, {limit=1})
|
||
eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
|
||
rv = get_extmarks(ns, marks[2], {0, 0}, {limit=1})
|
||
eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
|
||
-- prev with positional when mark exists at position
|
||
rv = get_extmarks(ns, positions[3], {0, 0}, {limit=1})
|
||
eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
|
||
-- prev with positional index (no mark at position)
|
||
rv = get_extmarks(ns, {positions[1][1], positions[1][2] +1}, {0, 0}, {limit=1})
|
||
eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
|
||
-- prev with Extremity index
|
||
rv = get_extmarks(ns, {-1,-1}, {0,0}, {limit=1})
|
||
eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
|
||
|
||
-- prevrange with mark id
|
||
rv = get_extmarks(ns, marks[3], marks[1])
|
||
eq({marks[3], positions[3][1], positions[3][2]}, rv[1])
|
||
eq({marks[2], positions[2][1], positions[2][2]}, rv[2])
|
||
eq({marks[1], positions[1][1], positions[1][2]}, rv[3])
|
||
-- prevrange with limit
|
||
rv = get_extmarks(ns, marks[3], marks[1], {limit=2})
|
||
eq(2, table.getn(rv))
|
||
-- prevrange with positional when mark exists at position
|
||
rv = get_extmarks(ns, positions[3], positions[1])
|
||
eq({{marks[3], positions[3][1], positions[3][2]},
|
||
{marks[2], positions[2][1], positions[2][2]},
|
||
{marks[1], positions[1][1], positions[1][2]}}, rv)
|
||
rv = get_extmarks(ns, positions[2], positions[1])
|
||
eq(2, table.getn(rv))
|
||
-- prevrange with positional index (no mark at position)
|
||
lower = {positions[2][1], positions[2][2] + 1}
|
||
upper = {positions[3][1], positions[3][2] + 1}
|
||
rv = get_extmarks(ns, upper, lower)
|
||
eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
|
||
lower = {positions[3][1], positions[3][2] + 1}
|
||
upper = {positions[3][1], positions[3][2] + 2}
|
||
rv = get_extmarks(ns, upper, lower)
|
||
eq({}, rv)
|
||
-- prevrange with extremity index
|
||
lower = {0,0}
|
||
upper = {positions[2][1], positions[2][2] - 1}
|
||
rv = get_extmarks(ns, upper, lower)
|
||
eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
|
||
end)
|
||
|
||
it('querying for information with limit', function()
|
||
-- add some more marks
|
||
for i, m in ipairs(marks) do
|
||
if positions[i] ~= nil then
|
||
local rv = set_extmark(ns, m, positions[i][1], positions[i][2])
|
||
eq(m, rv)
|
||
end
|
||
end
|
||
|
||
local rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=1})
|
||
eq(1, table.getn(rv))
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=2})
|
||
eq(2, table.getn(rv))
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=3})
|
||
eq(3, table.getn(rv))
|
||
|
||
-- now in reverse
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=1})
|
||
eq(1, table.getn(rv))
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=2})
|
||
eq(2, table.getn(rv))
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=3})
|
||
eq(3, table.getn(rv))
|
||
end)
|
||
|
||
it('get_marks works when mark col > upper col', function()
|
||
feed('A<cr>12345<esc>')
|
||
feed('A<cr>12345<esc>')
|
||
set_extmark(ns, 10, 0, 2) -- this shouldn't be found
|
||
set_extmark(ns, 11, 2, 1) -- this shouldn't be found
|
||
set_extmark(ns, marks[1], 0, 4) -- check col > our upper bound
|
||
set_extmark(ns, marks[2], 1, 1) -- check col < lower bound
|
||
set_extmark(ns, marks[3], 2, 0) -- check is inclusive
|
||
eq({{marks[1], 0, 4},
|
||
{marks[2], 1, 1},
|
||
{marks[3], 2, 0}},
|
||
get_extmarks(ns, {0, 3}, {2, 0}))
|
||
end)
|
||
|
||
it('get_marks works in reverse when mark col < lower col', function()
|
||
feed('A<cr>12345<esc>')
|
||
feed('A<cr>12345<esc>')
|
||
set_extmark(ns, 10, 0, 1) -- this shouldn't be found
|
||
set_extmark(ns, 11, 2, 4) -- this shouldn't be found
|
||
set_extmark(ns, marks[1], 2, 1) -- check col < our lower bound
|
||
set_extmark(ns, marks[2], 1, 4) -- check col > upper bound
|
||
set_extmark(ns, marks[3], 0, 2) -- check is inclusive
|
||
local rv = get_extmarks(ns, {2, 3}, {0, 2})
|
||
eq({{marks[1], 2, 1},
|
||
{marks[2], 1, 4},
|
||
{marks[3], 0, 2}},
|
||
rv)
|
||
end)
|
||
|
||
it('get_marks limit=0 returns nothing', function()
|
||
set_extmark(ns, marks[1], positions[1][1], positions[1][2])
|
||
local rv = get_extmarks(ns, {-1, -1}, {-1, -1}, {limit=0})
|
||
eq({}, rv)
|
||
end)
|
||
|
||
|
||
it('marks move with line insertations', function()
|
||
set_extmark(ns, marks[1], 0, 0)
|
||
feed("yyP")
|
||
check_undo_redo(ns, marks[1], 0, 0, 1, 0)
|
||
end)
|
||
|
||
it('marks move with multiline insertations', function()
|
||
feed("a<cr>22<cr>33<esc>")
|
||
set_extmark(ns, marks[1], 1, 1)
|
||
feed('ggVGyP')
|
||
check_undo_redo(ns, marks[1], 1, 1, 4, 1)
|
||
end)
|
||
|
||
it('marks move with line join', function()
|
||
-- do_join in ops.c
|
||
feed("a<cr>222<esc>")
|
||
set_extmark(ns, marks[1], 1, 0)
|
||
feed('ggJ')
|
||
check_undo_redo(ns, marks[1], 1, 0, 0, 6)
|
||
end)
|
||
|
||
it('join works when no marks are present', function()
|
||
screen = Screen.new(15, 10)
|
||
screen:attach()
|
||
feed("a<cr>1<esc>")
|
||
feed('kJ')
|
||
-- This shouldn't seg fault
|
||
screen:expect([[
|
||
12345^ 1 |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
|
|
||
]])
|
||
end)
|
||
|
||
it('marks move with multiline join', function()
|
||
-- do_join in ops.c
|
||
feed("a<cr>222<cr>333<cr>444<esc>")
|
||
set_extmark(ns, marks[1], 3, 0)
|
||
feed('2GVGJ')
|
||
check_undo_redo(ns, marks[1], 3, 0, 1, 8)
|
||
end)
|
||
|
||
it('marks move with line deletes', function()
|
||
feed("a<cr>222<cr>333<cr>444<esc>")
|
||
set_extmark(ns, marks[1], 2, 1)
|
||
feed('ggjdd')
|
||
check_undo_redo(ns, marks[1], 2, 1, 1, 1)
|
||
end)
|
||
|
||
it('marks move with multiline deletes', function()
|
||
feed("a<cr>222<cr>333<cr>444<esc>")
|
||
set_extmark(ns, marks[1], 3, 0)
|
||
feed('gg2dd')
|
||
check_undo_redo(ns, marks[1], 3, 0, 1, 0)
|
||
-- regression test, undoing multiline delete when mark is on row 1
|
||
feed('ugg3dd')
|
||
check_undo_redo(ns, marks[1], 3, 0, 0, 0)
|
||
end)
|
||
|
||
it('marks move with open line', function()
|
||
-- open_line in misc1.c
|
||
-- testing marks below are also moved
|
||
feed("yyP")
|
||
set_extmark(ns, marks[1], 0, 4)
|
||
set_extmark(ns, marks[2], 1, 4)
|
||
feed('1G<s-o><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 4, 1, 4)
|
||
check_undo_redo(ns, marks[2], 1, 4, 2, 4)
|
||
feed('2Go<esc>')
|
||
check_undo_redo(ns, marks[1], 1, 4, 1, 4)
|
||
check_undo_redo(ns, marks[2], 2, 4, 3, 4)
|
||
end)
|
||
|
||
it('marks move with char inserts', function()
|
||
-- insertchar in edit.c (the ins_str branch)
|
||
screen = Screen.new(15, 10)
|
||
screen:attach()
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
feed('0')
|
||
insert('abc')
|
||
screen:expect([[
|
||
ab^c12345 |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
~ |
|
||
|
|
||
]])
|
||
local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({0, 6}, rv)
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 6)
|
||
end)
|
||
|
||
-- gravity right as definted in tk library
|
||
it('marks have gravity right', function()
|
||
-- insertchar in edit.c (the ins_str branch)
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('03l')
|
||
insert("X")
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
|
||
-- check multibyte chars
|
||
feed('03l<esc>')
|
||
insert("~~")
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
end)
|
||
|
||
it('we can insert multibyte chars', function()
|
||
-- insertchar in edit.c
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 1, 2)
|
||
-- Insert a fullwidth (two col) tilde, NICE
|
||
feed('0i~<esc>')
|
||
check_undo_redo(ns, marks[1], 1, 2, 1, 5)
|
||
end)
|
||
|
||
it('marks move with blockwise inserts', function()
|
||
-- op_insert in ops.c
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 1, 2)
|
||
feed('0<c-v>lkI9<esc>')
|
||
check_undo_redo(ns, marks[1], 1, 2, 1, 3)
|
||
end)
|
||
|
||
it('marks move with line splits (using enter)', function()
|
||
-- open_line in misc1.c
|
||
-- testing marks below are also moved
|
||
feed("yyP")
|
||
set_extmark(ns, marks[1], 0, 4)
|
||
set_extmark(ns, marks[2], 1, 4)
|
||
feed('1Gla<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 4, 1, 2)
|
||
check_undo_redo(ns, marks[2], 1, 4, 2, 4)
|
||
end)
|
||
|
||
it('marks at last line move on insert new line', function()
|
||
-- open_line in misc1.c
|
||
set_extmark(ns, marks[1], 0, 4)
|
||
feed('0i<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 4, 1, 4)
|
||
end)
|
||
|
||
it('yet again marks move with line splits', function()
|
||
-- the first test above wasn't catching all errors..
|
||
feed("A67890<esc>")
|
||
set_extmark(ns, marks[1], 0, 4)
|
||
feed("04li<cr><esc>")
|
||
check_undo_redo(ns, marks[1], 0, 4, 1, 0)
|
||
end)
|
||
|
||
it('and one last time line splits...', function()
|
||
set_extmark(ns, marks[1], 0, 1)
|
||
set_extmark(ns, marks[2], 0, 2)
|
||
feed("02li<cr><esc>")
|
||
check_undo_redo(ns, marks[1], 0, 1, 0, 1)
|
||
check_undo_redo(ns, marks[2], 0, 2, 1, 0)
|
||
end)
|
||
|
||
it('multiple marks move with mark splits', function()
|
||
set_extmark(ns, marks[1], 0, 1)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
feed("0li<cr><esc>")
|
||
check_undo_redo(ns, marks[1], 0, 1, 1, 0)
|
||
check_undo_redo(ns, marks[2], 0, 3, 1, 2)
|
||
end)
|
||
|
||
it('deleting right before a mark works', function()
|
||
-- op_delete in ops.c
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('0lx')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 1)
|
||
end)
|
||
|
||
it('deleting right after a mark works', function()
|
||
-- op_delete in ops.c
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('02lx')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
end)
|
||
|
||
it('marks move with char deletes', function()
|
||
-- op_delete in ops.c
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('02dl')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 0)
|
||
-- from the other side (nothing should happen)
|
||
feed('$x')
|
||
check_undo_redo(ns, marks[1], 0, 0, 0, 0)
|
||
end)
|
||
|
||
it('marks move with char deletes over a range', function()
|
||
-- op_delete in ops.c
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
feed('0l3dl<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 1)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 1)
|
||
-- delete 1, nothing should happen to our marks
|
||
feed('u')
|
||
feed('$x')
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 3)
|
||
end)
|
||
|
||
it('deleting marks at end of line works', function()
|
||
-- mark_extended.c/extmark_col_adjust_delete
|
||
set_extmark(ns, marks[1], 0, 4)
|
||
feed('$x')
|
||
check_undo_redo(ns, marks[1], 0, 4, 0, 4)
|
||
-- check the copy happened correctly on delete at eol
|
||
feed('$x')
|
||
check_undo_redo(ns, marks[1], 0, 4, 0, 3)
|
||
feed('u')
|
||
check_undo_redo(ns, marks[1], 0, 4, 0, 4)
|
||
end)
|
||
|
||
it('marks move with blockwise deletes', function()
|
||
-- op_delete in ops.c
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 1, 4)
|
||
feed('h<c-v>hhkd')
|
||
check_undo_redo(ns, marks[1], 1, 4, 1, 1)
|
||
end)
|
||
|
||
it('marks move with blockwise deletes over a range', function()
|
||
-- op_delete in ops.c
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 0, 1)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 1, 2)
|
||
feed('0<c-v>k3lx')
|
||
check_undo_redo(ns, marks[1], 0, 1, 0, 0)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 0)
|
||
check_undo_redo(ns, marks[3], 1, 2, 1, 0)
|
||
-- delete 1, nothing should happen to our marks
|
||
feed('u')
|
||
feed('$<c-v>jx')
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 3)
|
||
check_undo_redo(ns, marks[3], 1, 2, 1, 2)
|
||
end)
|
||
|
||
it('works with char deletes over multilines', function()
|
||
feed('a<cr>12345<cr>test-me<esc>')
|
||
set_extmark(ns, marks[1], 2, 5)
|
||
feed('gg')
|
||
feed('dv?-m?<cr>')
|
||
check_undo_redo(ns, marks[1], 2, 5, 0, 0)
|
||
end)
|
||
|
||
it('marks outside of deleted range move with visual char deletes', function()
|
||
-- op_delete in ops.c
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
feed('0vx<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 2)
|
||
|
||
feed("u")
|
||
feed('0vlx<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 1)
|
||
|
||
feed("u")
|
||
feed('0v2lx<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 0)
|
||
|
||
-- from the other side (nothing should happen)
|
||
feed('$vx')
|
||
check_undo_redo(ns, marks[1], 0, 0, 0, 0)
|
||
end)
|
||
|
||
it('marks outside of deleted range move with char deletes', function()
|
||
-- op_delete in ops.c
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
feed('0x<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 2)
|
||
|
||
feed("u")
|
||
feed('02x<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 1)
|
||
|
||
feed("u")
|
||
feed('0v3lx<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 0)
|
||
|
||
-- from the other side (nothing should happen)
|
||
feed("u")
|
||
feed('$vx')
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 3)
|
||
end)
|
||
|
||
it('marks move with P(backward) paste', function()
|
||
-- do_put in ops.c
|
||
feed('0iabc<esc>')
|
||
set_extmark(ns, marks[1], 0, 7)
|
||
feed('0veyP')
|
||
check_undo_redo(ns, marks[1], 0, 7, 0, 15)
|
||
end)
|
||
|
||
it('marks move with p(forward) paste', function()
|
||
-- do_put in ops.c
|
||
feed('0iabc<esc>')
|
||
set_extmark(ns, marks[1], 0, 7)
|
||
feed('0veyp')
|
||
check_undo_redo(ns, marks[1], 0, 7, 0, 15)
|
||
end)
|
||
|
||
it('marks move with blockwise P(backward) paste', function()
|
||
-- do_put in ops.c
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 1, 4)
|
||
feed('<c-v>hhkyP<esc>')
|
||
check_undo_redo(ns, marks[1], 1, 4, 1, 7)
|
||
end)
|
||
|
||
it('marks move with blockwise p(forward) paste', function()
|
||
-- do_put in ops.c
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 1, 4)
|
||
feed('<c-v>hhkyp<esc>')
|
||
check_undo_redo(ns, marks[1], 1, 4, 1, 7)
|
||
end)
|
||
|
||
describe('multiline regions', function()
|
||
before_each(function()
|
||
feed('dd')
|
||
-- Achtung: code has been spiced with some unicode,
|
||
-- to make life more interesting.
|
||
-- luacheck whines about TABs inside strings for whatever reason.
|
||
-- luacheck: push ignore 621
|
||
insert([[
|
||
static int nlua_rpcrequest(lua_State *lstate)
|
||
{
|
||
Ïf (!nlua_is_deferred_safe(lstate)) {
|
||
// strictly not allowed
|
||
Яetörn luaL_error(lstate, e_luv_api_disabled, "rpcrequest");
|
||
}
|
||
return nlua_rpc(lstate, true);
|
||
}]])
|
||
-- luacheck: pop
|
||
end)
|
||
|
||
it('delete', function()
|
||
local pos1 = {
|
||
{2, 4}, {2, 12}, {2, 13}, {2, 14}, {2, 25},
|
||
{4, 8}, {4, 10}, {4, 20},
|
||
{5, 3}, {6, 10}
|
||
}
|
||
local ids = batch_set(ns, pos1)
|
||
batch_check(ns, ids, pos1)
|
||
feed('3Gfiv2+ftd')
|
||
batch_check_undo_redo(ns, ids, pos1, {
|
||
{2, 4}, {2, 12}, {2, 13}, {2, 13}, {2, 13},
|
||
{2, 13}, {2, 15}, {2, 25},
|
||
{3, 3}, {4, 10}
|
||
})
|
||
end)
|
||
|
||
-- TODO(bfredl): add more tests!
|
||
end)
|
||
|
||
it('replace works', function()
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('0r2')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
end)
|
||
|
||
it('blockwise replace works', function()
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('0<c-v>llkr1<esc>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 3)
|
||
end)
|
||
|
||
it('shift line', function()
|
||
-- shift_line in ops.c
|
||
feed(':set shiftwidth=4<cr><esc>')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('0>>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 6)
|
||
expect(' 12345')
|
||
|
||
feed('>>')
|
||
-- this is counter-intuitive. But what happens
|
||
-- is that 4 spaces gets extended to one tab (== 8 spaces)
|
||
check_undo_redo(ns, marks[1], 0, 6, 0, 3)
|
||
expect('\t12345')
|
||
|
||
feed('<LT><LT>') -- have to escape, same as <<
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 6)
|
||
end)
|
||
|
||
it('blockwise shift', function()
|
||
-- shift_block in ops.c
|
||
feed(':set shiftwidth=4<cr><esc>')
|
||
feed('a<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 1, 2)
|
||
feed('0<c-v>k>')
|
||
check_undo_redo(ns, marks[1], 1, 2, 1, 6)
|
||
feed('<c-v>j>')
|
||
expect('\t12345\n\t12345')
|
||
check_undo_redo(ns, marks[1], 1, 6, 1, 3)
|
||
|
||
feed('<c-v>j<LT>')
|
||
check_undo_redo(ns, marks[1], 1, 3, 1, 6)
|
||
end)
|
||
|
||
it('tab works with expandtab', function()
|
||
-- ins_tab in edit.c
|
||
feed(':set expandtab<cr><esc>')
|
||
feed(':set shiftwidth=2<cr><esc>')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('0i<tab><tab><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 6)
|
||
end)
|
||
|
||
it('tabs work', function()
|
||
-- ins_tab in edit.c
|
||
feed(':set noexpandtab<cr><esc>')
|
||
feed(':set shiftwidth=2<cr><esc>')
|
||
feed(':set softtabstop=2<cr><esc>')
|
||
feed(':set tabstop=8<cr><esc>')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
feed('0i<tab><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 4)
|
||
feed('0iX<tab><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 4, 0, 6)
|
||
end)
|
||
|
||
it('marks move when using :move', function()
|
||
set_extmark(ns, marks[1], 0, 0)
|
||
feed('A<cr>2<esc>:1move 2<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 0, 1, 0)
|
||
-- test codepath when moving lines up
|
||
feed(':2move 0<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 1, 0, 0, 0)
|
||
end)
|
||
|
||
it('marks move when using :move part 2', function()
|
||
-- make sure we didn't get lucky with the math...
|
||
feed('A<cr>2<cr>3<cr>4<cr>5<cr>6<esc>')
|
||
set_extmark(ns, marks[1], 1, 0)
|
||
feed(':2,3move 5<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 1, 0, 3, 0)
|
||
-- test codepath when moving lines up
|
||
feed(':4,5move 1<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 3, 0, 1, 0)
|
||
end)
|
||
|
||
it('undo and redo of set and unset marks', function()
|
||
-- Force a new undo head
|
||
feed('o<esc>')
|
||
set_extmark(ns, marks[1], 0, 1)
|
||
feed('o<esc>')
|
||
set_extmark(ns, marks[2], 0, -1)
|
||
set_extmark(ns, marks[3], 0, -1)
|
||
|
||
feed("u")
|
||
local rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(3, table.getn(rv))
|
||
|
||
feed("<c-r>")
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(3, table.getn(rv))
|
||
|
||
-- Test updates
|
||
feed('o<esc>')
|
||
set_extmark(ns, marks[1], positions[1][1], positions[1][2])
|
||
rv = get_extmarks(ns, marks[1], marks[1], {limit=1})
|
||
eq(1, table.getn(rv))
|
||
feed("u")
|
||
feed("<c-r>")
|
||
-- old value is NOT kept in history
|
||
check_undo_redo(ns, marks[1], positions[1][1], positions[1][2], positions[1][1], positions[1][2])
|
||
|
||
-- Test unset
|
||
feed('o<esc>')
|
||
curbufmeths.del_extmark(ns, marks[3])
|
||
feed("u")
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
-- undo does NOT restore deleted marks
|
||
eq(2, table.getn(rv))
|
||
feed("<c-r>")
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(2, table.getn(rv))
|
||
end)
|
||
|
||
it('undo and redo of marks deleted during edits', function()
|
||
-- test extmark_adjust
|
||
feed('A<cr>12345<esc>')
|
||
set_extmark(ns, marks[1], 1, 2)
|
||
feed('dd')
|
||
check_undo_redo(ns, marks[1], 1, 2, 1, 0)
|
||
end)
|
||
|
||
it('namespaces work properly', function()
|
||
local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
|
||
eq(1, rv)
|
||
rv = set_extmark(ns2, marks[1], positions[1][1], positions[1][2])
|
||
eq(1, rv)
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(1, table.getn(rv))
|
||
rv = get_extmarks(ns2, {0, 0}, {-1, -1})
|
||
eq(1, table.getn(rv))
|
||
|
||
-- Set more marks for testing the ranges
|
||
set_extmark(ns, marks[2], positions[2][1], positions[2][2])
|
||
set_extmark(ns, marks[3], positions[3][1], positions[3][2])
|
||
set_extmark(ns2, marks[2], positions[2][1], positions[2][2])
|
||
set_extmark(ns2, marks[3], positions[3][1], positions[3][2])
|
||
|
||
-- get_next (limit set)
|
||
rv = get_extmarks(ns, {0, 0}, positions[2], {limit=1})
|
||
eq(1, table.getn(rv))
|
||
rv = get_extmarks(ns2, {0, 0}, positions[2], {limit=1})
|
||
eq(1, table.getn(rv))
|
||
-- get_prev (limit set)
|
||
rv = get_extmarks(ns, positions[1], {0, 0}, {limit=1})
|
||
eq(1, table.getn(rv))
|
||
rv = get_extmarks(ns2, positions[1], {0, 0}, {limit=1})
|
||
eq(1, table.getn(rv))
|
||
|
||
-- get_next (no limit)
|
||
rv = get_extmarks(ns, positions[1], positions[2])
|
||
eq(2, table.getn(rv))
|
||
rv = get_extmarks(ns2, positions[1], positions[2])
|
||
eq(2, table.getn(rv))
|
||
-- get_prev (no limit)
|
||
rv = get_extmarks(ns, positions[2], positions[1])
|
||
eq(2, table.getn(rv))
|
||
rv = get_extmarks(ns2, positions[2], positions[1])
|
||
eq(2, table.getn(rv))
|
||
|
||
curbufmeths.del_extmark(ns, marks[1])
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(2, table.getn(rv))
|
||
curbufmeths.del_extmark(ns2, marks[1])
|
||
rv = get_extmarks(ns2, {0, 0}, {-1, -1})
|
||
eq(2, table.getn(rv))
|
||
end)
|
||
|
||
it('mark set can create unique identifiers', function()
|
||
-- create mark with id 1
|
||
eq(1, set_extmark(ns, 1, positions[1][1], positions[1][2]))
|
||
-- ask for unique id, it should be the next one, i e 2
|
||
eq(2, set_extmark(ns, 0, positions[1][1], positions[1][2]))
|
||
eq(3, set_extmark(ns, 3, positions[2][1], positions[2][2]))
|
||
eq(4, set_extmark(ns, 0, positions[1][1], positions[1][2]))
|
||
|
||
-- mixing manual and allocated id:s are not recommened, but it should
|
||
-- do something reasonable
|
||
eq(6, set_extmark(ns, 6, positions[2][1], positions[2][2]))
|
||
eq(7, set_extmark(ns, 0, positions[1][1], positions[1][2]))
|
||
eq(8, set_extmark(ns, 0, positions[1][1], positions[1][2]))
|
||
end)
|
||
|
||
it('auto indenting with enter works', function()
|
||
-- op_reindent in ops.c
|
||
feed(':set cindent<cr><esc>')
|
||
feed(':set autoindent<cr><esc>')
|
||
feed(':set shiftwidth=2<cr><esc>')
|
||
feed("0iint <esc>A {1M1<esc>b<esc>")
|
||
-- Set the mark on the M, should move..
|
||
set_extmark(ns, marks[1], 0, 12)
|
||
-- Set the mark before the cursor, should stay there
|
||
set_extmark(ns, marks[2], 0, 10)
|
||
feed("i<cr><esc>")
|
||
local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({1, 3}, rv)
|
||
rv = curbufmeths.get_extmark_by_id(ns, marks[2])
|
||
eq({0, 10}, rv)
|
||
check_undo_redo(ns, marks[1], 0, 12, 1, 3)
|
||
end)
|
||
|
||
it('auto indenting entire line works', function()
|
||
feed(':set cindent<cr><esc>')
|
||
feed(':set autoindent<cr><esc>')
|
||
feed(':set shiftwidth=2<cr><esc>')
|
||
-- <c-f> will force an indent of 2
|
||
feed("0iint <esc>A {<cr><esc>0i1M1<esc>")
|
||
set_extmark(ns, marks[1], 1, 1)
|
||
feed("0i<c-f><esc>")
|
||
local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({1, 3}, rv)
|
||
check_undo_redo(ns, marks[1], 1, 1, 1, 3)
|
||
-- now check when cursor at eol
|
||
feed("uA<c-f><esc>")
|
||
rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({1, 3}, rv)
|
||
end)
|
||
|
||
it('removing auto indenting with <C-D> works', function()
|
||
feed(':set cindent<cr><esc>')
|
||
feed(':set autoindent<cr><esc>')
|
||
feed(':set shiftwidth=2<cr><esc>')
|
||
feed("0i<tab><esc>")
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
feed("bi<c-d><esc>")
|
||
local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({0, 1}, rv)
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 1)
|
||
-- check when cursor at eol
|
||
feed("uA<c-d><esc>")
|
||
rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({0, 1}, rv)
|
||
end)
|
||
|
||
it('indenting multiple lines with = works', function()
|
||
feed(':set cindent<cr><esc>')
|
||
feed(':set autoindent<cr><esc>')
|
||
feed(':set shiftwidth=2<cr><esc>')
|
||
feed("0iint <esc>A {<cr><bs>1M1<cr><bs>2M2<esc>")
|
||
set_extmark(ns, marks[1], 1, 1)
|
||
set_extmark(ns, marks[2], 2, 1)
|
||
feed('=gg')
|
||
check_undo_redo(ns, marks[1], 1, 1, 1, 3)
|
||
check_undo_redo(ns, marks[2], 2, 1, 2, 5)
|
||
end)
|
||
|
||
it('substitutes by deleting inside the replace matches', function()
|
||
-- do_sub in ex_cmds.c
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
feed(':s/34/xx<cr>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 4)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 4)
|
||
end)
|
||
|
||
it('substitutes when insert text > deleted', function()
|
||
-- do_sub in ex_cmds.c
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
feed(':s/34/xxx<cr>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 5)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 5)
|
||
end)
|
||
|
||
it('substitutes when marks around eol', function()
|
||
-- do_sub in ex_cmds.c
|
||
set_extmark(ns, marks[1], 0, 4)
|
||
set_extmark(ns, marks[2], 0, 5)
|
||
feed(':s/5/xxx<cr>')
|
||
check_undo_redo(ns, marks[1], 0, 4, 0, 7)
|
||
check_undo_redo(ns, marks[2], 0, 5, 0, 7)
|
||
end)
|
||
|
||
it('substitutes over range insert text > deleted', function()
|
||
-- do_sub in ex_cmds.c
|
||
feed('A<cr>x34xx<esc>')
|
||
feed('A<cr>xxx34<esc>')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 1, 1)
|
||
set_extmark(ns, marks[3], 2, 4)
|
||
feed(':1,3s/34/xxx<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 5)
|
||
check_undo_redo(ns, marks[2], 1, 1, 1, 4)
|
||
check_undo_redo(ns, marks[3], 2, 4, 2, 6)
|
||
end)
|
||
|
||
it('substitutes multiple matches in a line', function()
|
||
-- do_sub in ex_cmds.c
|
||
feed('ddi3x3x3<esc>')
|
||
set_extmark(ns, marks[1], 0, 0)
|
||
set_extmark(ns, marks[2], 0, 2)
|
||
set_extmark(ns, marks[3], 0, 4)
|
||
feed(':s/3/yy/g<cr><esc>')
|
||
check_undo_redo(ns, marks[1], 0, 0, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 2, 0, 5)
|
||
check_undo_redo(ns, marks[3], 0, 4, 0, 8)
|
||
end)
|
||
|
||
it('substitions over multiple lines with newline in pattern', function()
|
||
feed('A<cr>67890<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
set_extmark(ns, marks[2], 0, 4)
|
||
set_extmark(ns, marks[3], 1, 0)
|
||
set_extmark(ns, marks[4], 1, 5)
|
||
set_extmark(ns, marks[5], 2, 0)
|
||
feed([[:1,2s:5\n:5 <cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 3)
|
||
check_undo_redo(ns, marks[2], 0, 4, 0, 6)
|
||
check_undo_redo(ns, marks[3], 1, 0, 0, 6)
|
||
check_undo_redo(ns, marks[4], 1, 5, 0, 11)
|
||
check_undo_redo(ns, marks[5], 2, 0, 1, 0)
|
||
end)
|
||
|
||
it('inserting', function()
|
||
feed('A<cr>67890<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
set_extmark(ns, marks[2], 0, 4)
|
||
set_extmark(ns, marks[3], 1, 0)
|
||
set_extmark(ns, marks[4], 1, 5)
|
||
set_extmark(ns, marks[5], 2, 0)
|
||
set_extmark(ns, marks[6], 1, 2)
|
||
feed([[:1,2s:5\n67:X<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 3)
|
||
check_undo_redo(ns, marks[2], 0, 4, 0, 5)
|
||
check_undo_redo(ns, marks[3], 1, 0, 0, 5)
|
||
check_undo_redo(ns, marks[4], 1, 5, 0, 8)
|
||
check_undo_redo(ns, marks[5], 2, 0, 1, 0)
|
||
check_undo_redo(ns, marks[6], 1, 2, 0, 5)
|
||
end)
|
||
|
||
it('substitions with multiple newlines in pattern', function()
|
||
feed('A<cr>67890<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 4)
|
||
set_extmark(ns, marks[2], 0, 5)
|
||
set_extmark(ns, marks[3], 1, 0)
|
||
set_extmark(ns, marks[4], 1, 5)
|
||
set_extmark(ns, marks[5], 2, 0)
|
||
feed([[:1,2s:\n.*\n:X<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 4, 0, 4)
|
||
check_undo_redo(ns, marks[2], 0, 5, 0, 6)
|
||
check_undo_redo(ns, marks[3], 1, 0, 0, 6)
|
||
check_undo_redo(ns, marks[4], 1, 5, 0, 6)
|
||
check_undo_redo(ns, marks[5], 2, 0, 0, 6)
|
||
end)
|
||
|
||
it('substitions over multiple lines with replace in substition', function()
|
||
feed('A<cr>67890<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 1)
|
||
set_extmark(ns, marks[2], 0, 2)
|
||
set_extmark(ns, marks[3], 0, 4)
|
||
set_extmark(ns, marks[4], 1, 0)
|
||
set_extmark(ns, marks[5], 2, 0)
|
||
feed([[:1,2s:3:\r<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 1, 0, 1)
|
||
check_undo_redo(ns, marks[2], 0, 2, 1, 0)
|
||
check_undo_redo(ns, marks[3], 0, 4, 1, 1)
|
||
check_undo_redo(ns, marks[4], 1, 0, 2, 0)
|
||
check_undo_redo(ns, marks[5], 2, 0, 3, 0)
|
||
feed('u')
|
||
feed([[:1,2s:3:\rxx<cr>]])
|
||
eq({1, 3}, curbufmeths.get_extmark_by_id(ns, marks[3]))
|
||
end)
|
||
|
||
it('substitions over multiple lines with replace in substition', function()
|
||
feed('A<cr>x3<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 1, 0)
|
||
set_extmark(ns, marks[2], 1, 1)
|
||
set_extmark(ns, marks[3], 1, 2)
|
||
feed([[:2,2s:3:\r<cr>]])
|
||
check_undo_redo(ns, marks[1], 1, 0, 1, 0)
|
||
check_undo_redo(ns, marks[2], 1, 1, 2, 0)
|
||
check_undo_redo(ns, marks[3], 1, 2, 2, 0)
|
||
end)
|
||
|
||
it('substitions over multiple lines with replace in substition', function()
|
||
feed('A<cr>x3<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 1)
|
||
set_extmark(ns, marks[2], 0, 2)
|
||
set_extmark(ns, marks[3], 0, 4)
|
||
set_extmark(ns, marks[4], 1, 1)
|
||
set_extmark(ns, marks[5], 2, 0)
|
||
feed([[:1,2s:3:\r<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 1, 0, 1)
|
||
check_undo_redo(ns, marks[2], 0, 2, 1, 0)
|
||
check_undo_redo(ns, marks[3], 0, 4, 1, 1)
|
||
check_undo_redo(ns, marks[4], 1, 1, 3, 0)
|
||
check_undo_redo(ns, marks[5], 2, 0, 4, 0)
|
||
feed('u')
|
||
feed([[:1,2s:3:\rxx<cr>]])
|
||
check_undo_redo(ns, marks[3], 0, 4, 1, 3)
|
||
end)
|
||
|
||
it('substitions with newline in match and sub, delta is 0', function()
|
||
feed('A<cr>67890<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
set_extmark(ns, marks[2], 0, 4)
|
||
set_extmark(ns, marks[3], 0, 5)
|
||
set_extmark(ns, marks[4], 1, 0)
|
||
set_extmark(ns, marks[5], 1, 5)
|
||
set_extmark(ns, marks[6], 2, 0)
|
||
feed([[:1,1s:5\n:\r<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 3)
|
||
check_undo_redo(ns, marks[2], 0, 4, 1, 0)
|
||
check_undo_redo(ns, marks[3], 0, 5, 1, 0)
|
||
check_undo_redo(ns, marks[4], 1, 0, 1, 0)
|
||
check_undo_redo(ns, marks[5], 1, 5, 1, 5)
|
||
check_undo_redo(ns, marks[6], 2, 0, 2, 0)
|
||
end)
|
||
|
||
it('substitions with newline in match and sub, delta > 0', function()
|
||
feed('A<cr>67890<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
set_extmark(ns, marks[2], 0, 4)
|
||
set_extmark(ns, marks[3], 0, 5)
|
||
set_extmark(ns, marks[4], 1, 0)
|
||
set_extmark(ns, marks[5], 1, 5)
|
||
set_extmark(ns, marks[6], 2, 0)
|
||
feed([[:1,1s:5\n:\r\r<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 3)
|
||
check_undo_redo(ns, marks[2], 0, 4, 2, 0)
|
||
check_undo_redo(ns, marks[3], 0, 5, 2, 0)
|
||
check_undo_redo(ns, marks[4], 1, 0, 2, 0)
|
||
check_undo_redo(ns, marks[5], 1, 5, 2, 5)
|
||
check_undo_redo(ns, marks[6], 2, 0, 3, 0)
|
||
end)
|
||
|
||
it('substitions with newline in match and sub, delta < 0', function()
|
||
feed('A<cr>67890<cr>xx<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
set_extmark(ns, marks[2], 0, 4)
|
||
set_extmark(ns, marks[3], 0, 5)
|
||
set_extmark(ns, marks[4], 1, 0)
|
||
set_extmark(ns, marks[5], 1, 5)
|
||
set_extmark(ns, marks[6], 2, 1)
|
||
set_extmark(ns, marks[7], 3, 0)
|
||
feed([[:1,2s:5\n.*\n:\r<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 3)
|
||
check_undo_redo(ns, marks[2], 0, 4, 1, 0)
|
||
check_undo_redo(ns, marks[3], 0, 5, 1, 0)
|
||
check_undo_redo(ns, marks[4], 1, 0, 1, 0)
|
||
check_undo_redo(ns, marks[5], 1, 5, 1, 0)
|
||
check_undo_redo(ns, marks[6], 2, 1, 1, 1)
|
||
check_undo_redo(ns, marks[7], 3, 0, 2, 0)
|
||
end)
|
||
|
||
it('substitions with backrefs, newline inserted into sub', function()
|
||
feed('A<cr>67890<cr>xx<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 0, 3)
|
||
set_extmark(ns, marks[2], 0, 4)
|
||
set_extmark(ns, marks[3], 0, 5)
|
||
set_extmark(ns, marks[4], 1, 0)
|
||
set_extmark(ns, marks[5], 1, 5)
|
||
set_extmark(ns, marks[6], 2, 0)
|
||
feed([[:1,1s:5\(\n\):\0\1<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 3, 0, 3)
|
||
check_undo_redo(ns, marks[2], 0, 4, 2, 0)
|
||
check_undo_redo(ns, marks[3], 0, 5, 2, 0)
|
||
check_undo_redo(ns, marks[4], 1, 0, 2, 0)
|
||
check_undo_redo(ns, marks[5], 1, 5, 2, 5)
|
||
check_undo_redo(ns, marks[6], 2, 0, 3, 0)
|
||
end)
|
||
|
||
it('substitions a ^', function()
|
||
set_extmark(ns, marks[1], 0, 0)
|
||
set_extmark(ns, marks[2], 0, 1)
|
||
feed([[:s:^:x<cr>]])
|
||
check_undo_redo(ns, marks[1], 0, 0, 0, 1)
|
||
check_undo_redo(ns, marks[2], 0, 1, 0, 2)
|
||
end)
|
||
|
||
it('using <c-a> without increase in order of magnitude', function()
|
||
-- do_addsub in ops.c
|
||
feed('ddiabc998xxx<esc>Tc')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 5)
|
||
set_extmark(ns, marks[4], 0, 6)
|
||
set_extmark(ns, marks[5], 0, 7)
|
||
feed('<c-a>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 6)
|
||
check_undo_redo(ns, marks[3], 0, 5, 0, 6)
|
||
check_undo_redo(ns, marks[4], 0, 6, 0, 6)
|
||
check_undo_redo(ns, marks[5], 0, 7, 0, 7)
|
||
end)
|
||
|
||
it('using <c-a> when increase in order of magnitude', function()
|
||
-- do_addsub in ops.c
|
||
feed('ddiabc999xxx<esc>Tc')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 5)
|
||
set_extmark(ns, marks[4], 0, 6)
|
||
set_extmark(ns, marks[5], 0, 7)
|
||
feed('<c-a>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 7)
|
||
check_undo_redo(ns, marks[3], 0, 5, 0, 7)
|
||
check_undo_redo(ns, marks[4], 0, 6, 0, 7)
|
||
check_undo_redo(ns, marks[5], 0, 7, 0, 8)
|
||
end)
|
||
|
||
it('using <c-a> when negative and without decrease in order of magnitude', function()
|
||
feed('ddiabc-999xxx<esc>T-')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 6)
|
||
set_extmark(ns, marks[4], 0, 7)
|
||
set_extmark(ns, marks[5], 0, 8)
|
||
feed('<c-a>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 7)
|
||
check_undo_redo(ns, marks[3], 0, 6, 0, 7)
|
||
check_undo_redo(ns, marks[4], 0, 7, 0, 7)
|
||
check_undo_redo(ns, marks[5], 0, 8, 0, 8)
|
||
end)
|
||
|
||
it('using <c-a> when negative and decrease in order of magnitude', function()
|
||
feed('ddiabc-1000xxx<esc>T-')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 7)
|
||
set_extmark(ns, marks[4], 0, 8)
|
||
set_extmark(ns, marks[5], 0, 9)
|
||
feed('<c-a>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 7)
|
||
check_undo_redo(ns, marks[3], 0, 7, 0, 7)
|
||
check_undo_redo(ns, marks[4], 0, 8, 0, 7)
|
||
check_undo_redo(ns, marks[5], 0, 9, 0, 8)
|
||
end)
|
||
|
||
it('using <c-x> without decrease in order of magnitude', function()
|
||
-- do_addsub in ops.c
|
||
feed('ddiabc999xxx<esc>Tc')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 5)
|
||
set_extmark(ns, marks[4], 0, 6)
|
||
set_extmark(ns, marks[5], 0, 7)
|
||
feed('<c-x>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 6)
|
||
check_undo_redo(ns, marks[3], 0, 5, 0, 6)
|
||
check_undo_redo(ns, marks[4], 0, 6, 0, 6)
|
||
check_undo_redo(ns, marks[5], 0, 7, 0, 7)
|
||
end)
|
||
|
||
it('using <c-x> when decrease in order of magnitude', function()
|
||
-- do_addsub in ops.c
|
||
feed('ddiabc1000xxx<esc>Tc')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 6)
|
||
set_extmark(ns, marks[4], 0, 7)
|
||
set_extmark(ns, marks[5], 0, 8)
|
||
feed('<c-x>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 6)
|
||
check_undo_redo(ns, marks[3], 0, 6, 0, 6)
|
||
check_undo_redo(ns, marks[4], 0, 7, 0, 6)
|
||
check_undo_redo(ns, marks[5], 0, 8, 0, 7)
|
||
end)
|
||
|
||
it('using <c-x> when negative and without increase in order of magnitude', function()
|
||
feed('ddiabc-998xxx<esc>T-')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 6)
|
||
set_extmark(ns, marks[4], 0, 7)
|
||
set_extmark(ns, marks[5], 0, 8)
|
||
feed('<c-x>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 7)
|
||
check_undo_redo(ns, marks[3], 0, 6, 0, 7)
|
||
check_undo_redo(ns, marks[4], 0, 7, 0, 7)
|
||
check_undo_redo(ns, marks[5], 0, 8, 0, 8)
|
||
end)
|
||
|
||
it('using <c-x> when negative and increase in order of magnitude', function()
|
||
feed('ddiabc-999xxx<esc>T-')
|
||
set_extmark(ns, marks[1], 0, 2)
|
||
set_extmark(ns, marks[2], 0, 3)
|
||
set_extmark(ns, marks[3], 0, 6)
|
||
set_extmark(ns, marks[4], 0, 7)
|
||
set_extmark(ns, marks[5], 0, 8)
|
||
feed('<c-x>')
|
||
check_undo_redo(ns, marks[1], 0, 2, 0, 2)
|
||
check_undo_redo(ns, marks[2], 0, 3, 0, 8)
|
||
check_undo_redo(ns, marks[3], 0, 6, 0, 8)
|
||
check_undo_redo(ns, marks[4], 0, 7, 0, 8)
|
||
check_undo_redo(ns, marks[5], 0, 8, 0, 9)
|
||
end)
|
||
|
||
it('throws consistent error codes', function()
|
||
local ns_invalid = ns2 + 1
|
||
eq("Invalid ns_id", pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2]))
|
||
eq("Invalid ns_id", pcall_err(curbufmeths.del_extmark, ns_invalid, marks[1]))
|
||
eq("Invalid ns_id", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
|
||
eq("Invalid ns_id", pcall_err(curbufmeths.get_extmark_by_id, ns_invalid, marks[1]))
|
||
end)
|
||
|
||
it('when col = line-length, set the mark on eol', function()
|
||
set_extmark(ns, marks[1], 0, -1)
|
||
local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({0, init_text:len()}, rv)
|
||
-- Test another
|
||
set_extmark(ns, marks[1], 0, -1)
|
||
rv = curbufmeths.get_extmark_by_id(ns, marks[1])
|
||
eq({0, init_text:len()}, rv)
|
||
end)
|
||
|
||
it('when col = line-length, set the mark on eol', function()
|
||
local invalid_col = init_text:len() + 1
|
||
eq("col value outside range", pcall_err(set_extmark, ns, marks[1], 0, invalid_col))
|
||
end)
|
||
|
||
it('fails when line > line_count', function()
|
||
local invalid_col = init_text:len() + 1
|
||
local invalid_lnum = 3
|
||
eq('line value outside range', pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col))
|
||
eq({}, curbufmeths.get_extmark_by_id(ns, marks[1]))
|
||
end)
|
||
|
||
it('bug from check_col in extmark_set', function()
|
||
-- This bug was caused by extmark_set always using check_col. check_col
|
||
-- always uses the current buffer. This wasn't working during undo so we
|
||
-- now use check_col and check_lnum only when they are required.
|
||
feed('A<cr>67890<cr>xx<esc>')
|
||
feed('A<cr>12345<cr>67890<cr>xx<esc>')
|
||
set_extmark(ns, marks[1], 3, 4)
|
||
feed([[:1,5s:5\n:5 <cr>]])
|
||
check_undo_redo(ns, marks[1], 3, 4, 2, 6)
|
||
end)
|
||
|
||
it('in read-only buffer', function()
|
||
command("view! runtime/doc/help.txt")
|
||
eq(true, curbufmeths.get_option('ro'))
|
||
local id = set_extmark(ns, 0, 0, 2)
|
||
eq({{id, 0, 2}}, get_extmarks(ns,0, -1))
|
||
end)
|
||
|
||
it('can set a mark to other buffer', function()
|
||
local buf = request('nvim_create_buf', 0, 1)
|
||
request('nvim_buf_set_lines', buf, 0, -1, 1, {"", ""})
|
||
local id = bufmeths.set_extmark(buf, ns, 0, 1, 0, {})
|
||
eq({{id, 1, 0}}, bufmeths.get_extmarks(buf, ns, 0, -1, {}))
|
||
end)
|
||
|
||
it('does not crash with append/delete/undo seqence', function()
|
||
meths.exec([[
|
||
let ns = nvim_create_namespace('myplugin')
|
||
call nvim_buf_set_extmark(0, ns, 0, 0, 0, {})
|
||
call append(0, '')
|
||
%delete
|
||
undo]],false)
|
||
eq(2, meths.eval('1+1')) -- did not crash
|
||
end)
|
||
end)
|
||
|
||
describe('Extmarks buffer api with many marks', function()
|
||
local ns1
|
||
local ns2
|
||
local ns_marks = {}
|
||
before_each(function()
|
||
clear()
|
||
ns1 = request('nvim_create_namespace', "ns1")
|
||
ns2 = request('nvim_create_namespace', "ns2")
|
||
ns_marks = {[ns1]={}, [ns2]={}}
|
||
local lines = {}
|
||
for i = 1,30 do
|
||
lines[#lines+1] = string.rep("x ",i)
|
||
end
|
||
curbufmeths.set_lines(0, -1, true, lines)
|
||
local ns = ns1
|
||
local q = 0
|
||
for i = 0,29 do
|
||
for j = 0,i do
|
||
local id = set_extmark(ns,0, i,j)
|
||
eq(nil, ns_marks[ns][id])
|
||
ok(id > 0)
|
||
ns_marks[ns][id] = {i,j}
|
||
ns = ns1+ns2-ns
|
||
q = q + 1
|
||
end
|
||
end
|
||
eq(233, #ns_marks[ns1])
|
||
eq(232, #ns_marks[ns2])
|
||
|
||
end)
|
||
|
||
local function get_marks(ns)
|
||
local mark_list = get_extmarks(ns, 0, -1)
|
||
local marks = {}
|
||
for _, mark in ipairs(mark_list) do
|
||
local id, row, col = unpack(mark)
|
||
eq(nil, marks[id], "duplicate mark")
|
||
marks[id] = {row,col}
|
||
end
|
||
return marks
|
||
end
|
||
|
||
it("can get marks", function()
|
||
eq(ns_marks[ns1], get_marks(ns1))
|
||
eq(ns_marks[ns2], get_marks(ns2))
|
||
end)
|
||
|
||
it("can clear all marks in ns", function()
|
||
curbufmeths.clear_namespace(ns1, 0, -1)
|
||
eq({}, get_marks(ns1))
|
||
eq(ns_marks[ns2], get_marks(ns2))
|
||
curbufmeths.clear_namespace(ns2, 0, -1)
|
||
eq({}, get_marks(ns1))
|
||
eq({}, get_marks(ns2))
|
||
end)
|
||
|
||
it("can clear line range", function()
|
||
curbufmeths.clear_namespace(ns1, 10, 20)
|
||
for id, mark in pairs(ns_marks[ns1]) do
|
||
if 10 <= mark[1] and mark[1] < 20 then
|
||
ns_marks[ns1][id] = nil
|
||
end
|
||
end
|
||
eq(ns_marks[ns1], get_marks(ns1))
|
||
eq(ns_marks[ns2], get_marks(ns2))
|
||
end)
|
||
|
||
it("can delete line", function()
|
||
feed('10Gdd')
|
||
for _, marks in pairs(ns_marks) do
|
||
for id, mark in pairs(marks) do
|
||
if mark[1] == 9 then
|
||
marks[id] = {9,0}
|
||
elseif mark[1] >= 10 then
|
||
mark[1] = mark[1] - 1
|
||
end
|
||
end
|
||
end
|
||
eq(ns_marks[ns1], get_marks(ns1))
|
||
eq(ns_marks[ns2], get_marks(ns2))
|
||
end)
|
||
|
||
it("can delete lines", function()
|
||
feed('10G10dd')
|
||
for _, marks in pairs(ns_marks) do
|
||
for id, mark in pairs(marks) do
|
||
if 9 <= mark[1] and mark[1] < 19 then
|
||
marks[id] = {9,0}
|
||
elseif mark[1] >= 19 then
|
||
mark[1] = mark[1] - 10
|
||
end
|
||
end
|
||
end
|
||
eq(ns_marks[ns1], get_marks(ns1))
|
||
eq(ns_marks[ns2], get_marks(ns2))
|
||
end)
|
||
|
||
it("can wipe buffer", function()
|
||
command('bwipe!')
|
||
eq({}, get_marks(ns1))
|
||
eq({}, get_marks(ns2))
|
||
end)
|
||
end)
|