mirror of
https://github.com/neovim/neovim.git
synced 2024-12-29 14:41:06 -07:00
4e6f559b8c
Problem: No way to have extmarks automatically removed when the range it is attached to is deleted. Solution: Add new 'invalidate' property that will hide a mark when the entirety of its range is deleted. When "undo_restore" is set to false, delete the mark from the buffer instead.
1977 lines
63 KiB
Lua
1977 lines
63 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 exec = helpers.exec
|
||
local meths = helpers.meths
|
||
local assert_alive = helpers.assert_alive
|
||
|
||
local function expect(contents)
|
||
return eq(contents, helpers.curbuf_contents())
|
||
end
|
||
|
||
local function set_extmark(ns_id, id, line, col, opts)
|
||
if opts == nil then
|
||
opts = {}
|
||
end
|
||
if id ~= nil and id ~= 0 then
|
||
opts.id = id
|
||
end
|
||
return curbufmeths.set_extmark(ns_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 get_extmark_by_id(ns_id, id, opts)
|
||
if opts == nil then
|
||
opts = {}
|
||
end
|
||
return curbufmeths.get_extmark_by_id(ns_id, id, opts)
|
||
end
|
||
|
||
local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end
|
||
local rv = get_extmark_by_id(ns, mark)
|
||
eq({er, ec}, rv)
|
||
feed("u")
|
||
rv = get_extmark_by_id(ns, mark)
|
||
eq({sr, sc}, rv)
|
||
feed("<c-r>")
|
||
rv = get_extmark_by_id(ns, mark)
|
||
eq({er, ec}, rv)
|
||
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('validation', function()
|
||
eq("Invalid 'end_col': expected Integer, got Array", pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = {}, end_row = 1 }))
|
||
eq("Invalid 'end_row': expected Integer, got Array", pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = {} }))
|
||
eq("Invalid 'virt_text_pos': expected String, got Integer", pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 0 }))
|
||
eq("Invalid 'virt_text_pos': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 'foo' }))
|
||
eq("Invalid 'hl_mode': expected String, got Integer", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 0 }))
|
||
eq("Invalid 'hl_mode': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 'foo' }))
|
||
eq("Invalid 'id': expected Integer, got Array", pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 }))
|
||
eq("Invalid mark position: expected 2 Integer items", pcall_err(get_extmarks, ns, {}, {-1, -1}))
|
||
eq("Invalid mark position: expected mark id Integer or 2-item Array", pcall_err(get_extmarks, ns, true, {-1, -1}))
|
||
-- No memory leak with virt_text, virt_lines, sign_text
|
||
eq("right_gravity is not a boolean", pcall_err(set_extmark, ns, marks[2], 0, 0, {
|
||
virt_text = {{'foo', 'Normal'}},
|
||
virt_lines = {{{'bar', 'Normal'}}},
|
||
sign_text = 'a',
|
||
right_gravity = 'baz',
|
||
}))
|
||
end)
|
||
|
||
it("can end extranges past final newline using end_col = 0", function()
|
||
set_extmark(ns, marks[1], 0, 0, {
|
||
end_col = 0,
|
||
end_row = 1
|
||
})
|
||
eq("Invalid 'end_col': out of range",
|
||
pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }))
|
||
end)
|
||
|
||
it("can end extranges past final newline when strict mode is false", function()
|
||
set_extmark(ns, marks[1], 0, 0, {
|
||
end_col = 1,
|
||
end_row = 1,
|
||
strict = false,
|
||
})
|
||
end)
|
||
|
||
it("can end extranges past final column when strict mode is false", function()
|
||
set_extmark(ns, marks[1], 0, 0, {
|
||
end_col = 6,
|
||
end_row = 0,
|
||
strict = false,
|
||
})
|
||
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 = 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 = 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 = 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('can undo with extmarks (#25147)', function()
|
||
feed('itest<esc>')
|
||
set_extmark(ns, 1, 0, 0)
|
||
set_extmark(ns, 2, 1, 0)
|
||
eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
feed('dd')
|
||
eq({ { 1, 1, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
curbufmeths.clear_namespace(ns, 0, -1)
|
||
eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
set_extmark(ns, 1, 0, 0, { right_gravity = false })
|
||
set_extmark(ns, 2, 1, 0, { right_gravity = false })
|
||
eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
feed('u')
|
||
eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, {0, 0}, {-1, -1}))
|
||
curbufmeths.clear_namespace(ns, 0, -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, #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, #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, #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, #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, #rv)
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=2})
|
||
eq(2, #rv)
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=3})
|
||
eq(3, #rv)
|
||
|
||
-- now in reverse
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=1})
|
||
eq(1, #rv)
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=2})
|
||
eq(2, #rv)
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=3})
|
||
eq(3, #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 change.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 = 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 change.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 change.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()
|
||
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)
|
||
|
||
it('can get overlapping extmarks', function()
|
||
set_extmark(ns, 1, 0, 0, {end_row = 5, end_col=0})
|
||
set_extmark(ns, 2, 2, 5, {end_row = 2, end_col=30})
|
||
set_extmark(ns, 3, 0, 5, {end_row = 2, end_col=10})
|
||
set_extmark(ns, 4, 0, 0, {end_row = 1, end_col=0})
|
||
eq({{ 2, 2, 5 }}, get_extmarks(ns, {2, 0}, {2, -1}, { overlap=false }))
|
||
eq({{ 1, 0, 0 }, { 3, 0, 5}, {2, 2, 5}}, get_extmarks(ns, {2, 0}, {2, -1}, { overlap=true }))
|
||
end)
|
||
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, #rv)
|
||
|
||
feed("<c-r>")
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(3, #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, #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, #rv)
|
||
feed("<c-r>")
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(2, #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, #rv)
|
||
rv = get_extmarks(ns2, {0, 0}, {-1, -1})
|
||
eq(1, #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, #rv)
|
||
rv = get_extmarks(ns2, {0, 0}, positions[2], {limit=1})
|
||
eq(1, #rv)
|
||
-- get_prev (limit set)
|
||
rv = get_extmarks(ns, positions[1], {0, 0}, {limit=1})
|
||
eq(1, #rv)
|
||
rv = get_extmarks(ns2, positions[1], {0, 0}, {limit=1})
|
||
eq(1, #rv)
|
||
|
||
-- get_next (no limit)
|
||
rv = get_extmarks(ns, positions[1], positions[2])
|
||
eq(2, #rv)
|
||
rv = get_extmarks(ns2, positions[1], positions[2])
|
||
eq(2, #rv)
|
||
-- get_prev (no limit)
|
||
rv = get_extmarks(ns, positions[2], positions[1])
|
||
eq(2, #rv)
|
||
rv = get_extmarks(ns2, positions[2], positions[1])
|
||
eq(2, #rv)
|
||
|
||
curbufmeths.del_extmark(ns, marks[1])
|
||
rv = get_extmarks(ns, {0, 0}, {-1, -1})
|
||
eq(2, #rv)
|
||
curbufmeths.del_extmark(ns2, marks[1])
|
||
rv = get_extmarks(ns2, {0, 0}, {-1, -1})
|
||
eq(2, #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 recommended, 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 = get_extmark_by_id(ns, marks[1])
|
||
eq({1, 3}, rv)
|
||
rv = 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 = 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 = 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 = 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 = 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('substitutes 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('substitutes 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('substitutes over multiple lines with replace in substitution', 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}, get_extmark_by_id(ns, marks[3]))
|
||
end)
|
||
|
||
it('substitutes over multiple lines with replace in substitution', 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('substitutes over multiple lines with replace in substitution', 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('substitutes 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('substitutes 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('substitutes 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('substitutes 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('substitutes 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': 3", pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2]))
|
||
eq("Invalid 'ns_id': 3", pcall_err(curbufmeths.del_extmark, ns_invalid, marks[1]))
|
||
eq("Invalid 'ns_id': 3", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
|
||
eq("Invalid 'ns_id': 3", pcall_err(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 = get_extmark_by_id(ns, marks[1])
|
||
eq({0, init_text:len()}, rv)
|
||
-- Test another
|
||
set_extmark(ns, marks[1], 0, -1)
|
||
rv = 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("Invalid 'col': out of 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("Invalid 'line': out of range", pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col))
|
||
eq({}, 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, meths.get_option_value('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, 1, 0, {})
|
||
eq({{id, 1, 0}}, bufmeths.get_extmarks(buf, ns, 0, -1, {}))
|
||
end)
|
||
|
||
it('does not crash with append/delete/undo sequence', function()
|
||
exec([[
|
||
let ns = nvim_create_namespace('myplugin')
|
||
call nvim_buf_set_extmark(0, ns, 0, 0, {})
|
||
call append(0, '')
|
||
%delete
|
||
undo]])
|
||
assert_alive()
|
||
end)
|
||
|
||
it('works with left and right gravity', function()
|
||
-- right gravity should move with inserted text, while
|
||
-- left gravity should stay in place.
|
||
curbufmeths.set_extmark(ns, 0, 5, {right_gravity = false})
|
||
curbufmeths.set_extmark(ns, 0, 5, {right_gravity = true})
|
||
feed([[Aasdfasdf]])
|
||
|
||
eq({ {1, 0, 5}, {2, 0, 13} },
|
||
curbufmeths.get_extmarks(ns, 0, -1, {}))
|
||
|
||
-- but both move when text is inserted before
|
||
feed([[<esc>Iasdf<esc>]])
|
||
-- eq({}, curbufmeths.get_lines(0, -1, true))
|
||
eq({ {1, 0, 9}, {2, 0, 17} },
|
||
curbufmeths.get_extmarks(ns, 0, -1, {}))
|
||
|
||
-- clear text
|
||
curbufmeths.set_text(0, 0, 0, 17, {})
|
||
|
||
-- handles set_text correctly as well
|
||
eq({ {1, 0, 0}, {2, 0, 0} },
|
||
meths.buf_get_extmarks(0, ns, 0, -1, {}))
|
||
curbufmeths.set_text(0, 0, 0, 0, {'asdfasdf'})
|
||
eq({ {1, 0, 0}, {2, 0, 8} },
|
||
curbufmeths.get_extmarks(ns, 0, -1, {}))
|
||
|
||
feed('u')
|
||
-- handles pasting
|
||
exec([[let @a='asdfasdf']])
|
||
feed([["ap]])
|
||
eq({ {1, 0, 0}, {2, 0, 8} },
|
||
meths.buf_get_extmarks(0, ns, 0, -1, {}))
|
||
end)
|
||
|
||
it('can accept "end_row" or "end_line" #16548', function()
|
||
set_extmark(ns, marks[1], 0, 0, {
|
||
end_col = 0,
|
||
end_line = 1
|
||
})
|
||
eq({ {1, 0, 0, {
|
||
ns_id = 1,
|
||
end_col = 0,
|
||
end_row = 1,
|
||
right_gravity = true,
|
||
end_right_gravity = false,
|
||
}} }, get_extmarks(ns, 0, -1, {details=true}))
|
||
end)
|
||
|
||
it('in prompt buffer', function()
|
||
feed('dd')
|
||
local id = set_extmark(ns, marks[1], 0, 0, {})
|
||
meths.set_option_value('buftype', 'prompt', {})
|
||
feed('i<esc>')
|
||
eq({{id, 0, 2}}, get_extmarks(ns, 0, -1))
|
||
end)
|
||
|
||
it('can get details', function()
|
||
set_extmark(ns, marks[1], 0, 0, {
|
||
conceal = "c",
|
||
cursorline_hl_group = "Statement",
|
||
end_col = 0,
|
||
end_right_gravity = true,
|
||
end_row = 1,
|
||
hl_eol = true,
|
||
hl_group = "String",
|
||
hl_mode = "blend",
|
||
line_hl_group = "Statement",
|
||
number_hl_group = "Statement",
|
||
priority = 0,
|
||
right_gravity = false,
|
||
sign_hl_group = "Statement",
|
||
sign_text = ">>",
|
||
spell = true,
|
||
virt_lines = {
|
||
{ { "lines", "Macro" }, { "???" } },
|
||
{ { "stack", { "Type", "Search" } }, { "!!!" } },
|
||
},
|
||
virt_lines_above = true,
|
||
virt_lines_leftcol = true,
|
||
virt_text = { { "text", "Macro" }, { "???" }, { "stack", { "Type", "Search" } } },
|
||
virt_text_hide = true,
|
||
virt_text_pos = "right_align",
|
||
})
|
||
set_extmark(ns, marks[2], 0, 0, {
|
||
priority = 0,
|
||
virt_text = { { "", "Macro" }, { "", { "Type", "Search" } }, { "" } },
|
||
virt_text_win_col = 1,
|
||
})
|
||
eq({0, 0, {
|
||
conceal = "c",
|
||
cursorline_hl_group = "Statement",
|
||
end_col = 0,
|
||
end_right_gravity = true,
|
||
end_row = 1,
|
||
hl_eol = true,
|
||
hl_group = "String",
|
||
hl_mode = "blend",
|
||
line_hl_group = "Statement",
|
||
ns_id = 1,
|
||
number_hl_group = "Statement",
|
||
priority = 0,
|
||
right_gravity = false,
|
||
sign_hl_group = "Statement",
|
||
sign_text = ">>",
|
||
spell = true,
|
||
virt_lines = {
|
||
{ { "lines", "Macro" }, { "???" } },
|
||
{ { "stack", { "Type", "Search" } }, { "!!!" } },
|
||
},
|
||
virt_lines_above = true,
|
||
virt_lines_leftcol = true,
|
||
virt_text = { { "text", "Macro" }, { "???" }, { "stack", { "Type", "Search" } } },
|
||
virt_text_hide = true,
|
||
virt_text_pos = "right_align",
|
||
} }, get_extmark_by_id(ns, marks[1], { details = true }))
|
||
eq({0, 0, {
|
||
ns_id = 1,
|
||
right_gravity = true,
|
||
priority = 0,
|
||
virt_text = { { "", "Macro" }, { "", { "Type", "Search" } }, { "" } },
|
||
virt_text_hide = false,
|
||
virt_text_pos = "win_col",
|
||
virt_text_win_col = 1,
|
||
} }, get_extmark_by_id(ns, marks[2], { details = true }))
|
||
set_extmark(ns, marks[3], 0, 0, { cursorline_hl_group = "Statement" })
|
||
eq({0, 0, {
|
||
ns_id = 1,
|
||
cursorline_hl_group = "Statement",
|
||
right_gravity = true,
|
||
} }, get_extmark_by_id(ns, marks[3], { details = true }))
|
||
end)
|
||
|
||
it('can get marks from anonymous namespaces', function()
|
||
ns = request('nvim_create_namespace', "")
|
||
ns2 = request('nvim_create_namespace', "")
|
||
set_extmark(ns, 1, 0, 0, {})
|
||
set_extmark(ns2, 2, 1, 0, {})
|
||
eq({{ 1, 0, 0, { ns_id = ns, right_gravity = true }},
|
||
{ 2, 1, 0, { ns_id = ns2, right_gravity = true }}},
|
||
get_extmarks(-1, 0, -1, { details = true }))
|
||
end)
|
||
|
||
it('can filter by extmark properties', function()
|
||
set_extmark(ns, 1, 0, 0, {})
|
||
set_extmark(ns, 2, 0, 0, { hl_group = 'Normal' })
|
||
set_extmark(ns, 3, 0, 0, { sign_text = '>>' })
|
||
set_extmark(ns, 4, 0, 0, { virt_text = {{'text', 'Normal'}}})
|
||
set_extmark(ns, 5, 0, 0, { virt_lines = {{{ 'line', 'Normal' }}}})
|
||
eq(5, #get_extmarks(-1, 0, -1, {}))
|
||
eq({{ 2, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'highlight' }))
|
||
eq({{ 3, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'sign' }))
|
||
eq({{ 4, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'virt_text' }))
|
||
eq({{ 5, 0, 0 }}, get_extmarks(-1, 0, -1, { type = 'virt_lines' }))
|
||
end)
|
||
|
||
it("invalidated marks are deleted", function()
|
||
screen = Screen.new(40, 6)
|
||
screen:attach()
|
||
feed('dd6iaaa bbb ccc<CR><ESC>gg')
|
||
set_extmark(ns, 1, 0, 0, { invalidate = true, sign_text = 'S1' })
|
||
set_extmark(ns, 2, 1, 0, { invalidate = true, sign_text = 'S2' })
|
||
-- mark with invalidate is removed
|
||
command('d')
|
||
screen:expect([[
|
||
S2^aaa bbb ccc |
|
||
aaa bbb ccc |
|
||
aaa bbb ccc |
|
||
aaa bbb ccc |
|
||
aaa bbb ccc |
|
||
|
|
||
]])
|
||
-- mark is restored with undo_restore == true
|
||
command('silent undo')
|
||
screen:expect([[
|
||
S1^aaa bbb ccc |
|
||
S2aaa bbb ccc |
|
||
aaa bbb ccc |
|
||
aaa bbb ccc |
|
||
aaa bbb ccc |
|
||
|
|
||
]])
|
||
-- mark is deleted with undo_restore == false
|
||
set_extmark(ns, 1, 0, 0, { invalidate = true, undo_restore = false, sign_text = 'S1' })
|
||
set_extmark(ns, 2, 1, 0, { invalidate = true, undo_restore = false, sign_text = 'S2' })
|
||
command('1d 2')
|
||
eq(0, #get_extmarks(-1, 0, -1, {}))
|
||
-- mark is not removed when deleting bytes before the range
|
||
set_extmark(ns, 3, 0, 4, { invalidate = true, undo_restore = false,
|
||
hl_group = 'Error', end_col = 7 })
|
||
feed('dw')
|
||
eq(3, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
|
||
-- mark is not removed when deleting bytes at the start of the range
|
||
feed('x')
|
||
eq(2, get_extmark_by_id(ns, 3, { details = true })[3].end_col)
|
||
-- mark is not removed when deleting bytes from the end of the range
|
||
feed('lx')
|
||
eq(1, get_extmark_by_id(ns, 3, { details = true})[3].end_col)
|
||
-- mark is not removed when deleting bytes beyond end of the range
|
||
feed('x')
|
||
eq(1, get_extmark_by_id(ns, 3, { details = true})[3].end_col)
|
||
-- mark is removed when all bytes in the range are deleted
|
||
feed('hx')
|
||
eq({}, get_extmark_by_id(ns, 3, {}))
|
||
-- multiline mark is not removed when start of its range is deleted
|
||
set_extmark(ns, 4, 1, 4, { undo_restore = false, invalidate = true,
|
||
hl_group = 'Error', end_col = 7, end_row = 3 })
|
||
feed('ddDdd')
|
||
eq({0, 0}, get_extmark_by_id(ns, 4, {}))
|
||
-- multiline mark is removed when entirety of its range is deleted
|
||
feed('vj2ed')
|
||
eq({}, get_extmark_by_id(ns, 4, {}))
|
||
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)
|
||
|
||
describe('API/win_extmark', function()
|
||
local screen
|
||
local marks, line1, line2
|
||
local ns
|
||
|
||
before_each(function()
|
||
-- Initialize some namespaces and insert text into a buffer
|
||
marks = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
||
|
||
line1 = "non ui-watched line"
|
||
line2 = "ui-watched line"
|
||
|
||
clear()
|
||
|
||
insert(line1)
|
||
feed("o<esc>")
|
||
insert(line2)
|
||
ns = request('nvim_create_namespace', "extmark-ui")
|
||
end)
|
||
|
||
it('sends and only sends ui-watched marks to ui', function()
|
||
screen = Screen.new(20, 4)
|
||
screen:attach()
|
||
-- should send this
|
||
set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
|
||
-- should not send this
|
||
set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
|
||
screen:expect({
|
||
grid = [[
|
||
non ui-watched line |
|
||
ui-watched lin^e |
|
||
~ |
|
||
|
|
||
]],
|
||
extmarks = {
|
||
[2] = {
|
||
-- positioned at the end of the 2nd line
|
||
{ {id = 1000}, 1, 1, 1, 16 },
|
||
}
|
||
},
|
||
})
|
||
end)
|
||
|
||
it('sends multiple ui-watched marks to ui', function()
|
||
screen = Screen.new(20, 4)
|
||
screen:attach()
|
||
-- should send all of these
|
||
set_extmark(ns, marks[1], 1, 0, { ui_watched = true, virt_text_pos = "overlay" })
|
||
set_extmark(ns, marks[2], 1, 2, { ui_watched = true, virt_text_pos = "overlay" })
|
||
set_extmark(ns, marks[3], 1, 4, { ui_watched = true, virt_text_pos = "overlay" })
|
||
set_extmark(ns, marks[4], 1, 6, { ui_watched = true, virt_text_pos = "overlay" })
|
||
set_extmark(ns, marks[5], 1, 8, { ui_watched = true })
|
||
screen:expect({
|
||
grid = [[
|
||
non ui-watched line |
|
||
ui-watched lin^e |
|
||
~ |
|
||
|
|
||
]],
|
||
extmarks = {
|
||
[2] = {
|
||
-- earlier notifications
|
||
{ {id = 1000}, 1, 1, 1, 0 },
|
||
{ {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 },
|
||
{ {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 },
|
||
{ {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 }, { {id = 1000}, 1, 4, 1, 6 },
|
||
-- final
|
||
-- overlay
|
||
{ {id = 1000}, 1, 1, 1, 0 },
|
||
{ {id = 1000}, 1, 2, 1, 2 },
|
||
{ {id = 1000}, 1, 3, 1, 4 },
|
||
{ {id = 1000}, 1, 4, 1, 6 },
|
||
-- eol
|
||
{ {id = 1000}, 1, 5, 1, 16 },
|
||
}
|
||
},
|
||
})
|
||
end)
|
||
|
||
it('updates ui-watched marks', function()
|
||
screen = Screen.new(20, 4)
|
||
screen:attach()
|
||
-- should send this
|
||
set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
|
||
-- should not send this
|
||
set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
|
||
-- make some changes
|
||
insert(" update")
|
||
screen:expect({
|
||
grid = [[
|
||
non ui-watched line |
|
||
ui-watched linupdat^e|
|
||
e |
|
||
|
|
||
]],
|
||
extmarks = {
|
||
[2] = {
|
||
-- positioned at the end of the 2nd line
|
||
{ {id = 1000}, 1, 1, 1, 16 },
|
||
-- updated and wrapped to 3rd line
|
||
{ {id = 1000}, 1, 1, 2, 2 },
|
||
}
|
||
}
|
||
})
|
||
feed("<c-e>")
|
||
screen:expect({
|
||
grid = [[
|
||
ui-watched linupdat^e|
|
||
e |
|
||
~ |
|
||
|
|
||
]],
|
||
extmarks = {
|
||
[2] = {
|
||
-- positioned at the end of the 2nd line
|
||
{ {id = 1000}, 1, 1, 1, 16 },
|
||
-- updated and wrapped to 3rd line
|
||
{ {id = 1000}, 1, 1, 2, 2 },
|
||
-- scrolled up one line, should be handled by grid scroll
|
||
}
|
||
}
|
||
})
|
||
end)
|
||
|
||
it('sends ui-watched to splits', function()
|
||
screen = Screen.new(20, 8)
|
||
screen:attach({ext_multigrid=true})
|
||
-- should send this
|
||
set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
|
||
-- should not send this
|
||
set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
|
||
command('split')
|
||
screen:expect({
|
||
grid = [[
|
||
## grid 1
|
||
[4:--------------------]|
|
||
[4:--------------------]|
|
||
[4:--------------------]|
|
||
[No Name] [+] |
|
||
[2:--------------------]|
|
||
[2:--------------------]|
|
||
[No Name] [+] |
|
||
[3:--------------------]|
|
||
## grid 2
|
||
non ui-watched line |
|
||
ui-watched line |
|
||
## grid 3
|
||
|
|
||
## grid 4
|
||
non ui-watched line |
|
||
ui-watched lin^e |
|
||
~ |
|
||
]],
|
||
extmarks = {
|
||
[2] = {
|
||
-- positioned at the end of the 2nd line
|
||
{ {id = 1000}, 1, 1, 1, 16 },
|
||
-- updated after split
|
||
{ {id = 1000}, 1, 1, 1, 16 },
|
||
},
|
||
[4] = {
|
||
-- only after split
|
||
{ {id = 1001}, 1, 1, 1, 16 },
|
||
}
|
||
}
|
||
})
|
||
-- make some changes
|
||
insert(" update")
|
||
screen:expect({
|
||
grid = [[
|
||
## grid 1
|
||
[4:--------------------]|
|
||
[4:--------------------]|
|
||
[4:--------------------]|
|
||
[No Name] [+] |
|
||
[2:--------------------]|
|
||
[2:--------------------]|
|
||
[No Name] [+] |
|
||
[3:--------------------]|
|
||
## grid 2
|
||
non ui-watched line |
|
||
ui-watched linupd@@@|
|
||
## grid 3
|
||
|
|
||
## grid 4
|
||
non ui-watched line |
|
||
ui-watched linupdat^e|
|
||
e |
|
||
]],
|
||
extmarks = {
|
||
[2] = {
|
||
-- positioned at the end of the 2nd line
|
||
{ {id = 1000}, 1, 1, 1, 16 },
|
||
-- updated after split
|
||
{ {id = 1000}, 1, 1, 1, 16 },
|
||
},
|
||
[4] = {
|
||
{ {id = 1001}, 1, 1, 1, 16 },
|
||
-- updated
|
||
{ {id = 1001}, 1, 1, 2, 2 },
|
||
}
|
||
}
|
||
})
|
||
end)
|
||
end)
|