mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	 e63e5d1dbd
			
		
	
	e63e5d1dbd
	
	
	
		
			
			Co-authored-by: Elias Alves Moura <eliamoura.alves@gmail.com> Co-authored-by: venkatesh <shariharanvenkatesh@gmail.com> Co-authored-by: zeertzjq <zeertzjq@outlook.com> Co-authored-by: Vikas Raj <24727447+numToStr@users.noreply.github.com> Co-authored-by: Steve Vermeulen <sfvermeulen@gmail.com> Co-authored-by: Evgeni Chasnovski <evgeni.chasnovski@gmail.com> Co-authored-by: rwxd <rwxd@pm.me> Co-authored-by: casswedson <58050969+casswedson@users.noreply.github.com>
		
			
				
	
	
		
			1609 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			1609 lines
		
	
	
		
			50 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 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("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("end_col value outside 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('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)
 | ||
| 
 | ||
|     -- 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, #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('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}, 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(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("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({}, 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, 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, {})
 | ||
|       call append(0, '')
 | ||
|       %delete
 | ||
|       undo]],false)
 | ||
|     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
 | ||
|     meths.exec([[let @a='asdfasdf']], false)
 | ||
|     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, {
 | ||
|       end_col = 0,
 | ||
|       end_row = 1,
 | ||
|       right_gravity = true,
 | ||
|       end_right_gravity = false,
 | ||
|     }} }, get_extmarks(ns, 0, -1, {details=true}))
 | ||
|   end)
 | ||
| 
 | ||
|   it('can get details', function()
 | ||
|     set_extmark(ns, marks[1], 0, 0, {
 | ||
|       end_col = 0,
 | ||
|       end_row = 1,
 | ||
|       right_gravity = false,
 | ||
|       end_right_gravity = true,
 | ||
|       priority = 0,
 | ||
|       hl_eol = true,
 | ||
|       hl_mode = "blend",
 | ||
|       hl_group = "String",
 | ||
|       virt_text = { { "text", "Statement" } },
 | ||
|       virt_text_pos = "right_align",
 | ||
|       virt_text_hide = true,
 | ||
|       virt_lines = { { { "lines", "Statement" } }},
 | ||
|       virt_lines_above = true,
 | ||
|       virt_lines_leftcol = true,
 | ||
|     })
 | ||
|     set_extmark(ns, marks[2], 0, 0, {
 | ||
|       priority = 0,
 | ||
|       virt_text = { { "text", "Statement" } },
 | ||
|       virt_text_win_col = 1,
 | ||
|     })
 | ||
|     eq({0, 0, {
 | ||
|       end_col = 0,
 | ||
|       end_row = 1,
 | ||
|       right_gravity = false,
 | ||
|       end_right_gravity = true,
 | ||
|       priority = 0,
 | ||
|       hl_eol = true,
 | ||
|       hl_mode = "blend",
 | ||
|       hl_group = "String",
 | ||
|       virt_text = { { "text", "Statement" } },
 | ||
|       virt_text_pos = "right_align",
 | ||
|       virt_text_hide = true,
 | ||
|       virt_lines = { { { "lines", "Statement" } }},
 | ||
|       virt_lines_above = true,
 | ||
|       virt_lines_leftcol = true,
 | ||
|     } }, get_extmark_by_id(ns, marks[1], { details = true }))
 | ||
|     eq({0, 0, {
 | ||
|       right_gravity = true,
 | ||
|       priority = 0,
 | ||
|       virt_text = { { "text", "Statement" } },
 | ||
|       virt_text_hide = false,
 | ||
|       virt_text_pos = "win_col",
 | ||
|       virt_text_win_col = 1,
 | ||
|     } }, get_extmark_by_id(ns, marks[2], { details = true }))
 | ||
|   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)
 |