aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/functional/api/mark_extended_spec.lua1375
-rw-r--r--test/functional/fixtures/lsp-test-rpc-server.lua424
-rw-r--r--test/functional/lua/api_spec.lua30
-rw-r--r--test/functional/lua/commands_spec.lua99
-rw-r--r--test/functional/lua/luaeval_spec.lua96
-rw-r--r--test/functional/lua/overrides_spec.lua27
-rw-r--r--test/functional/lua/uri_spec.lua107
-rw-r--r--test/functional/lua/vim_spec.lua (renamed from test/functional/lua/utility_functions_spec.lua)238
-rw-r--r--test/functional/plugin/lsp/lsp_spec.lua634
-rw-r--r--test/functional/ui/bufhl_spec.lua16
-rw-r--r--test/functional/ui/messages_spec.lua137
11 files changed, 3023 insertions, 160 deletions
diff --git a/test/functional/api/mark_extended_spec.lua b/test/functional/api/mark_extended_spec.lua
new file mode 100644
index 0000000000..76db9f9d81
--- /dev/null
+++ b/test/functional/api/mark_extended_spec.lua
@@ -0,0 +1,1375 @@
+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 pcall_err = helpers.pcall_err
+local insert = helpers.insert
+local feed = helpers.feed
+local clear = helpers.clear
+local command = helpers.command
+
+local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end
+ local rv = curbufmeths.get_extmark_by_id(ns, mark)
+ eq({er, ec}, rv)
+ feed("u")
+ rv = curbufmeths.get_extmark_by_id(ns, mark)
+ eq({sr, sc}, rv)
+ feed("<c-r>")
+ rv = curbufmeths.get_extmark_by_id(ns, mark)
+ eq({er, ec}, rv)
+end
+
+local function set_extmark(ns_id, id, line, col, opts)
+ if opts == nil then
+ opts = {}
+ end
+ return curbufmeths.set_extmark(ns_id, id, line, col, opts)
+end
+
+local function get_extmarks(ns_id, start, end_, opts)
+ if opts == nil then
+ opts = {}
+ end
+ return curbufmeths.get_extmarks(ns_id, start, end_, opts)
+end
+
+describe('Extmarks buffer api', function()
+ local screen
+ local marks, positions, ns_string2, ns_string, 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}}
+
+ ns_string = "my-fancy-plugin"
+ ns_string2 = "my-fancy-plugin2"
+ init_text = "12345"
+ row = 0
+ col = 2
+
+ clear()
+ screen = Screen.new(15, 10)
+ screen:attach()
+
+ insert(init_text)
+ ns = request('nvim_create_namespace', ns_string)
+ ns2 = request('nvim_create_namespace', ns_string2)
+ end)
+
+ it('adds, updates and deletes marks #extmarks', function()
+ local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
+ eq(marks[1], rv)
+ rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({positions[1][1], positions[1][2]}, rv)
+ -- Test adding a second mark on same row works
+ rv = set_extmark(ns, marks[2], positions[2][1], positions[2][2])
+ eq(marks[2], rv)
+
+ -- Test an update, (same pos)
+ rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
+ eq(marks[1], rv)
+ rv = curbufmeths.get_extmark_by_id(ns, marks[2])
+ eq({positions[2][1], positions[2][2]}, rv)
+ -- Test an update, (new pos)
+ row = positions[1][1]
+ col = positions[1][2] + 1
+ rv = set_extmark(ns, marks[1], row, col)
+ eq(marks[1], rv)
+ rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({row, col}, rv)
+
+ -- remove the test marks
+ eq(true, curbufmeths.del_extmark(ns, marks[1]))
+ eq(false, curbufmeths.del_extmark(ns, marks[1]))
+ eq(true, curbufmeths.del_extmark(ns, marks[2]))
+ eq(false, curbufmeths.del_extmark(ns, marks[3]))
+ eq(false, curbufmeths.del_extmark(ns, 1000))
+ end)
+
+ it('can clear a specific namespace range #extmarks', 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({{1, 0, 1}}, 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 #extmarks', 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({{1, 0, 1}}, get_extmarks(ns, {0, 0}, {-1, -1}))
+ eq({{1, 0, 1}}, 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 #extmarks', 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
+
+ -- {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}, {amount=1})
+ eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
+ rv = get_extmarks(ns, marks[2], {-1, -1}, {amount=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}, {amount=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}, {amount=1})
+ eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
+ -- next with Extremity index
+ rv = get_extmarks(ns, {0,0}, {-1, -1}, {amount=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 amount
+ rv = get_extmarks(ns, marks[1], marks[3], {amount=2})
+ eq(2, table.getn(rv))
+ -- nextrange with positional when mark exists at position
+ rv = get_extmarks(ns, positions[1], positions[3])
+ eq({marks[1], positions[1][1], positions[1][2]}, rv[1])
+ eq({marks[2], positions[2][1], positions[2][2]}, rv[2])
+ rv = get_extmarks(ns, positions[2], positions[3])
+ eq(2, table.getn(rv))
+ -- nextrange with positional index (no mark at position)
+ local lower = {positions[1][1], positions[2][2] -1}
+ local upper = {positions[2][1], positions[3][2] - 1}
+ rv = get_extmarks(ns, lower, upper)
+ eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
+ lower = {positions[3][1], positions[3][2] + 1}
+ upper = {positions[3][1], positions[3][2] + 2}
+ rv = get_extmarks(ns, lower, upper)
+ eq({}, rv)
+ -- nextrange with extremity index
+ lower = {positions[2][1], positions[2][2]+1}
+ upper = {-1, -1}
+ rv = get_extmarks(ns, lower, upper)
+ eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
+
+ -- prev with mark id
+ rv = get_extmarks(ns, marks[3], {0, 0}, {amount=1})
+ eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
+ rv = get_extmarks(ns, marks[2], {0, 0}, {amount=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}, {amount=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}, {amount=1})
+ eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
+ -- prev with Extremity index
+ rv = get_extmarks(ns, {-1,-1}, {0,0}, {amount=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 amount
+ rv = get_extmarks(ns, marks[3], marks[1], {amount=2})
+ eq(2, table.getn(rv))
+ -- prevrange with positional when mark exists at position
+ rv = get_extmarks(ns, positions[3], positions[1])
+ eq({{marks[3], positions[3][1], positions[3][2]},
+ {marks[2], positions[2][1], positions[2][2]},
+ {marks[1], positions[1][1], positions[1][2]}}, rv)
+ rv = get_extmarks(ns, positions[2], positions[1])
+ eq(2, table.getn(rv))
+ -- prevrange with positional index (no mark at position)
+ lower = {positions[2][1], positions[2][2] + 1}
+ upper = {positions[3][1], positions[3][2] + 1}
+ rv = get_extmarks(ns, upper, lower)
+ eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
+ lower = {positions[3][1], positions[3][2] + 1}
+ upper = {positions[3][1], positions[3][2] + 2}
+ rv = get_extmarks(ns, upper, lower)
+ eq({}, rv)
+ -- prevrange with extremity index
+ lower = {0,0}
+ upper = {positions[2][1], positions[2][2] - 1}
+ rv = get_extmarks(ns, upper, lower)
+ eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
+ end)
+
+ it('querying for information with amount #extmarks', 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}, {amount=1})
+ eq(1, table.getn(rv))
+ rv = get_extmarks(ns, {0, 0}, {-1, -1}, {amount=2})
+ eq(2, table.getn(rv))
+ rv = get_extmarks(ns, {0, 0}, {-1, -1}, {amount=3})
+ eq(3, table.getn(rv))
+
+ -- now in reverse
+ rv = get_extmarks(ns, {0, 0}, {-1, -1}, {amount=1})
+ eq(1, table.getn(rv))
+ rv = get_extmarks(ns, {0, 0}, {-1, -1}, {amount=2})
+ eq(2, table.getn(rv))
+ rv = get_extmarks(ns, {0, 0}, {-1, -1}, {amount=3})
+ eq(3, table.getn(rv))
+ end)
+
+ it('get_marks works when mark col > upper col #extmarks', 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 #extmarks', 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 amount 0 returns nothing #extmarks', function()
+ set_extmark(ns, marks[1], positions[1][1], positions[1][2])
+ local rv = get_extmarks(ns, {-1, -1}, {-1, -1}, {amount=0})
+ eq({}, rv)
+ end)
+
+
+ it('marks move with line insertations #extmarks', 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 #extmarks', 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 #extmarks', 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 #extmarks', function()
+ feed("a<cr>1<esc>")
+ feed('kJ')
+ -- This shouldn't seg fault
+ screen:expect([[
+ 12345^ 1 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+
+ it('marks move with multiline join #extmarks', 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 #extmarks', 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 #extmarks', 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 #extmarks', function()
+ -- open_line in misc1.c
+ -- testing marks below are also moved
+ feed("yyP")
+ set_extmark(ns, marks[1], 0, 4)
+ set_extmark(ns, marks[2], 1, 4)
+ feed('1G<s-o><esc>')
+ check_undo_redo(ns, marks[1], 0, 4, 1, 4)
+ check_undo_redo(ns, marks[2], 1, 4, 2, 4)
+ feed('2Go<esc>')
+ check_undo_redo(ns, marks[1], 1, 4, 1, 4)
+ check_undo_redo(ns, marks[2], 2, 4, 3, 4)
+ end)
+
+ it('marks move with char inserts #extmarks', function()
+ -- insertchar in edit.c (the ins_str branch)
+ set_extmark(ns, marks[1], 0, 3)
+ feed('0')
+ insert('abc')
+ screen:expect([[
+ ab^c12345 |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({0, 6}, rv)
+ -- check_undo_redo(ns, marks[1], 0, 2, 0, 5)
+ end)
+
+ -- gravity right as definted in tk library
+ it('marks have gravity right #extmarks', 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 #extmarks', 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, 3)
+ end)
+
+ it('marks move with blockwise inserts #extmarks', 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) #extmarks', function()
+ -- open_line in misc1.c
+ -- testing marks below are also moved
+ feed("yyP")
+ set_extmark(ns, marks[1], 0, 4)
+ set_extmark(ns, marks[2], 1, 4)
+ feed('1Gla<cr><esc>')
+ check_undo_redo(ns, marks[1], 0, 4, 1, 2)
+ check_undo_redo(ns, marks[2], 1, 4, 2, 4)
+ end)
+
+ it('marks at last line move on insert new line #extmarks', function()
+ -- open_line in misc1.c
+ set_extmark(ns, marks[1], 0, 4)
+ feed('0i<cr><esc>')
+ check_undo_redo(ns, marks[1], 0, 4, 1, 4)
+ end)
+
+ it('yet again marks move with line splits #extmarks', 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... #extmarks', 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 #extmarks', 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 on a mark works #extmarks', 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 #extmarks', 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 #extmarks', 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 happend 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 #extmarks', function()
+ -- mark_extended.c/extmark_col_adjust_delete
+ set_extmark(ns, marks[1], 0, 4)
+ feed('$x')
+ check_undo_redo(ns, marks[1], 0, 4, 0, 4)
+ -- check the copy happened correctly on delete at eol
+ feed('$x')
+ check_undo_redo(ns, marks[1], 0, 4, 0, 3)
+ feed('u')
+ check_undo_redo(ns, marks[1], 0, 4, 0, 4)
+ end)
+
+ it('marks move with blockwise deletes #extmarks', 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 #extmarks', 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 happend 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 #extmarks', 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 #extmarks', 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 #extmarks', 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 #extmarks', 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 #extmarks', 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, 14)
+ end)
+
+ it('marks move with blockwise P(backward) paste #extmarks', 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 #extmarks', 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, 6)
+ end)
+
+ it('replace works #extmarks', 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 #extmarks', 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, 2)
+ end)
+
+ it('shift line #extmarks', 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)
+
+ feed('>>')
+ check_undo_redo(ns, marks[1], 0, 6, 0, 10)
+
+ feed('<LT><LT>') -- have to escape, same as <<
+ check_undo_redo(ns, marks[1], 0, 10, 0, 6)
+ end)
+
+ it('blockwise shift #extmarks', 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>')
+ check_undo_redo(ns, marks[1], 1, 6, 1, 10)
+
+ feed('<c-v>j<LT>')
+ check_undo_redo(ns, marks[1], 1, 10, 1, 6)
+ end)
+
+ it('tab works with expandtab #extmarks', 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 #extmarks', 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 #extmarks', 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 #extmarks', 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 #extmarks', 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(1, table.getn(rv))
+
+ feed("<c-r>")
+ rv = get_extmarks(ns, {0, 0}, {-1, -1})
+ eq(3, table.getn(rv))
+
+ -- Test updates
+ feed('o<esc>')
+ set_extmark(ns, marks[1], positions[1][1], positions[1][2])
+ rv = get_extmarks(ns, marks[1], marks[1], {amount=1})
+ eq(1, table.getn(rv))
+ feed("u")
+ feed("<c-r>")
+ check_undo_redo(ns, marks[1], 0, 1, 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})
+ eq(3, table.getn(rv))
+ feed("<c-r>")
+ rv = get_extmarks(ns, {0, 0}, {-1, -1})
+ eq(2, table.getn(rv))
+ end)
+
+ it('undo and redo of marks deleted during edits #extmarks', 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 #extmarks', function()
+ local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
+ eq(1, rv)
+ rv = set_extmark(ns2, marks[1], positions[1][1], positions[1][2])
+ eq(1, rv)
+ rv = get_extmarks(ns, {0, 0}, {-1, -1})
+ eq(1, table.getn(rv))
+ rv = get_extmarks(ns2, {0, 0}, {-1, -1})
+ eq(1, table.getn(rv))
+
+ -- Set more marks for testing the ranges
+ set_extmark(ns, marks[2], positions[2][1], positions[2][2])
+ set_extmark(ns, marks[3], positions[3][1], positions[3][2])
+ set_extmark(ns2, marks[2], positions[2][1], positions[2][2])
+ set_extmark(ns2, marks[3], positions[3][1], positions[3][2])
+
+ -- get_next (amount set)
+ rv = get_extmarks(ns, {0, 0}, positions[2], {amount=1})
+ eq(1, table.getn(rv))
+ rv = get_extmarks(ns2, {0, 0}, positions[2], {amount=1})
+ eq(1, table.getn(rv))
+ -- get_prev (amount set)
+ rv = get_extmarks(ns, positions[1], {0, 0}, {amount=1})
+ eq(1, table.getn(rv))
+ rv = get_extmarks(ns2, positions[1], {0, 0}, {amount=1})
+ eq(1, table.getn(rv))
+
+ -- get_next (amount not set)
+ rv = get_extmarks(ns, positions[1], positions[2])
+ eq(2, table.getn(rv))
+ rv = get_extmarks(ns2, positions[1], positions[2])
+ eq(2, table.getn(rv))
+ -- get_prev (amount not set)
+ rv = get_extmarks(ns, positions[2], positions[1])
+ eq(2, table.getn(rv))
+ rv = get_extmarks(ns2, positions[2], positions[1])
+ eq(2, table.getn(rv))
+
+ curbufmeths.del_extmark(ns, marks[1])
+ rv = get_extmarks(ns, {0, 0}, {-1, -1})
+ eq(2, table.getn(rv))
+ curbufmeths.del_extmark(ns2, marks[1])
+ rv = get_extmarks(ns2, {0, 0}, {-1, -1})
+ eq(2, table.getn(rv))
+ end)
+
+ it('mark set can create unique identifiers #extmarks', function()
+ -- create mark with id 1
+ eq(1, set_extmark(ns, 1, positions[1][1], positions[1][2]))
+ -- ask for unique id, it should be the next one, i e 2
+ eq(2, set_extmark(ns, 0, positions[1][1], positions[1][2]))
+ eq(3, set_extmark(ns, 3, positions[2][1], positions[2][2]))
+ eq(4, set_extmark(ns, 0, positions[1][1], positions[1][2]))
+
+ -- mixing manual and allocated id:s are not recommened, but it should
+ -- do something reasonable
+ eq(6, set_extmark(ns, 6, positions[2][1], positions[2][2]))
+ eq(7, set_extmark(ns, 0, positions[1][1], positions[1][2]))
+ eq(8, set_extmark(ns, 0, positions[1][1], positions[1][2]))
+ end)
+
+ it('auto indenting with enter works #extmarks', function()
+ -- op_reindent in ops.c
+ feed(':set cindent<cr><esc>')
+ feed(':set autoindent<cr><esc>')
+ feed(':set shiftwidth=2<cr><esc>')
+ feed("0iint <esc>A {1M1<esc>b<esc>")
+ -- Set the mark on the M, should move..
+ set_extmark(ns, marks[1], 0, 12)
+ -- Set the mark before the cursor, should stay there
+ set_extmark(ns, marks[2], 0, 10)
+ feed("i<cr><esc>")
+ local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({1, 3}, rv)
+ rv = curbufmeths.get_extmark_by_id(ns, marks[2])
+ eq({0, 10}, rv)
+ check_undo_redo(ns, marks[1], 0, 12, 1, 3)
+ end)
+
+ it('auto indenting entire line works #extmarks', function()
+ feed(':set cindent<cr><esc>')
+ feed(':set autoindent<cr><esc>')
+ feed(':set shiftwidth=2<cr><esc>')
+ -- <c-f> will force an indent of 2
+ feed("0iint <esc>A {<cr><esc>0i1M1<esc>")
+ set_extmark(ns, marks[1], 1, 1)
+ feed("0i<c-f><esc>")
+ local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({1, 3}, rv)
+ check_undo_redo(ns, marks[1], 1, 1, 1, 3)
+ -- now check when cursor at eol
+ feed("uA<c-f><esc>")
+ rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({1, 3}, rv)
+ end)
+
+ it('removing auto indenting with <C-D> works #extmarks', function()
+ feed(':set cindent<cr><esc>')
+ feed(':set autoindent<cr><esc>')
+ feed(':set shiftwidth=2<cr><esc>')
+ feed("0i<tab><esc>")
+ set_extmark(ns, marks[1], 0, 3)
+ feed("bi<c-d><esc>")
+ local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({0, 1}, rv)
+ check_undo_redo(ns, marks[1], 0, 3, 0, 1)
+ -- check when cursor at eol
+ feed("uA<c-d><esc>")
+ rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({0, 1}, rv)
+ end)
+
+ it('indenting multiple lines with = works #extmarks', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', function()
+ feed('A<cr>67890<cr>xx<esc>')
+ set_extmark(ns, marks[1], 0, 1)
+ set_extmark(ns, marks[2], 0, 2)
+ set_extmark(ns, marks[3], 0, 4)
+ set_extmark(ns, marks[4], 1, 0)
+ set_extmark(ns, marks[5], 2, 0)
+ feed([[:1,2s:3:\r<cr>]])
+ check_undo_redo(ns, marks[1], 0, 1, 0, 1)
+ check_undo_redo(ns, marks[2], 0, 2, 1, 0)
+ check_undo_redo(ns, marks[3], 0, 4, 1, 1)
+ check_undo_redo(ns, marks[4], 1, 0, 2, 0)
+ check_undo_redo(ns, marks[5], 2, 0, 3, 0)
+ feed('u')
+ feed([[:1,2s:3:\rxx<cr>]])
+ eq({1, 3}, curbufmeths.get_extmark_by_id(ns, marks[3]))
+ end)
+
+ it('substitions over multiple lines with replace in substition #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_sub', 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 #extmarks_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, 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 #extmarks_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 ^ #extmarks_sub', 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 #extmarks_inc_dec', 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 #extmarks_inc_dec', 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 #extmarks_inc_dec', 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 #extmarks_inc_dec', 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 #extmarks_inc_dec', 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 #extmarks_inc_dec', 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 #extmarks_inc_dec', 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 #extmarks_inc_dec', function()
+ feed('ddiabc-999xxx<esc>T-')
+ set_extmark(ns, marks[1], 0, 2)
+ set_extmark(ns, marks[2], 0, 3)
+ set_extmark(ns, marks[3], 0, 6)
+ set_extmark(ns, marks[4], 0, 7)
+ set_extmark(ns, marks[5], 0, 8)
+ feed('<c-x>')
+ check_undo_redo(ns, marks[1], 0, 2, 0, 2)
+ check_undo_redo(ns, marks[2], 0, 3, 0, 8)
+ check_undo_redo(ns, marks[3], 0, 6, 0, 8)
+ check_undo_redo(ns, marks[4], 0, 7, 0, 8)
+ check_undo_redo(ns, marks[5], 0, 8, 0, 9)
+ end)
+
+ it('throws consistent error codes', function()
+ local ns_invalid = ns2 + 1
+ eq("Invalid ns_id", pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2]))
+ eq("Invalid ns_id", pcall_err(curbufmeths.del_extmark, ns_invalid, marks[1]))
+ eq("Invalid ns_id", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
+ eq("Invalid ns_id", pcall_err(curbufmeths.get_extmark_by_id, ns_invalid, marks[1]))
+ end)
+
+ it('when col = line-length, set the mark on eol #extmarks', function()
+ set_extmark(ns, marks[1], 0, -1)
+ local rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({0, init_text:len()}, rv)
+ -- Test another
+ set_extmark(ns, marks[1], 0, -1)
+ rv = curbufmeths.get_extmark_by_id(ns, marks[1])
+ eq({0, init_text:len()}, rv)
+ end)
+
+ it('when col = line-length, set the mark on eol #extmarks', 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('when line > line_count, throw error #extmarks', function()
+ local invalid_col = init_text:len() + 1
+ local invalid_lnum = 3
+ eq('line value outside range', pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col))
+ eq({}, curbufmeths.get_extmark_by_id(ns, marks[1]))
+ end)
+
+ it('bug from check_col in extmark_set #extmarks_sub', 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)
+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 #extmarks", function()
+ eq(ns_marks[ns1], get_marks(ns1))
+ eq(ns_marks[ns2], get_marks(ns2))
+ end)
+
+ it("can clear all marks in ns #extmarks", 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 #extmarks", 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 #extmarks", 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 #extmarks", 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 #extmarks", function()
+ command('bwipe!')
+ eq({}, get_marks(ns1))
+ eq({}, get_marks(ns2))
+ end)
+end)
diff --git a/test/functional/fixtures/lsp-test-rpc-server.lua b/test/functional/fixtures/lsp-test-rpc-server.lua
new file mode 100644
index 0000000000..971e61b072
--- /dev/null
+++ b/test/functional/fixtures/lsp-test-rpc-server.lua
@@ -0,0 +1,424 @@
+local protocol = require 'vim.lsp.protocol'
+
+-- Internal utility methods.
+
+-- TODO replace with a better implementation.
+local function json_encode(data)
+ local status, result = pcall(vim.fn.json_encode, data)
+ if status then
+ return result
+ else
+ return nil, result
+ end
+end
+local function json_decode(data)
+ local status, result = pcall(vim.fn.json_decode, data)
+ if status then
+ return result
+ else
+ return nil, result
+ end
+end
+
+local function message_parts(sep, ...)
+ local parts = {}
+ for i = 1, select("#", ...) do
+ local arg = select(i, ...)
+ if arg ~= nil then
+ table.insert(parts, arg)
+ end
+ end
+ return table.concat(parts, sep)
+end
+
+-- Assert utility methods
+
+local function assert_eq(a, b, ...)
+ if not vim.deep_equal(a, b) then
+ error(message_parts(": ",
+ ..., "assert_eq failed",
+ string.format("left == %q, right == %q", vim.inspect(a), vim.inspect(b))
+ ))
+ end
+end
+
+local function format_message_with_content_length(encoded_message)
+ return table.concat {
+ 'Content-Length: '; tostring(#encoded_message); '\r\n\r\n';
+ encoded_message;
+ }
+end
+
+-- Server utility methods.
+
+local function read_message()
+ local line = io.read("*l")
+ local length = line:lower():match("content%-length:%s*(%d+)")
+ return assert(json_decode(io.read(2 + length):sub(2)), "read_message.json_decode")
+end
+
+local function send(payload)
+ io.stdout:write(format_message_with_content_length(json_encode(payload)))
+end
+
+local function respond(id, err, result)
+ assert(type(id) == 'number', "id must be a number")
+ send { jsonrpc = "2.0"; id = id, error = err, result = result }
+end
+
+local function notify(method, params)
+ assert(type(method) == 'string', "method must be a string")
+ send { method = method, params = params or {} }
+end
+
+local function expect_notification(method, params, ...)
+ local message = read_message()
+ assert_eq(method, message.method,
+ ..., "expect_notification", "method")
+ assert_eq(params, message.params,
+ ..., "expect_notification", method, "params")
+ assert_eq({jsonrpc = "2.0"; method=method, params=params}, message,
+ ..., "expect_notification", "message")
+end
+
+local function expect_request(method, callback, ...)
+ local req = read_message()
+ assert_eq(method, req.method,
+ ..., "expect_request", "method")
+ local err, result = callback(req.params)
+ respond(req.id, err, result)
+end
+
+io.stderr:setvbuf("no")
+
+local function skeleton(config)
+ local on_init = assert(config.on_init)
+ local body = assert(config.body)
+ expect_request("initialize", function(params)
+ return nil, on_init(params)
+ end)
+ expect_notification("initialized", {})
+ body()
+ expect_request("shutdown", function()
+ return nil, {}
+ end)
+ expect_notification("exit", nil)
+end
+
+-- The actual tests.
+
+local tests = {}
+
+function tests.basic_init()
+ skeleton {
+ on_init = function(_params)
+ return { capabilities = {} }
+ end;
+ body = function()
+ notify('test')
+ end;
+ }
+end
+
+function tests.basic_check_capabilities()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ }
+ }
+ end;
+ body = function()
+ end;
+ }
+end
+
+function tests.basic_finish()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ }
+ }
+ end;
+ body = function()
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
+function tests.basic_check_buffer_open()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didOpen', {
+ textDocument = {
+ languageId = "";
+ text = table.concat({"testing"; "123"}, "\n");
+ uri = "file://";
+ version = 0;
+ };
+ })
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
+function tests.basic_check_buffer_open_and_change()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didOpen', {
+ textDocument = {
+ languageId = "";
+ text = table.concat({"testing"; "123"}, "\n");
+ uri = "file://";
+ version = 0;
+ };
+ })
+ expect_notification('textDocument/didChange', {
+ textDocument = {
+ uri = "file://";
+ version = 3;
+ };
+ contentChanges = {
+ { text = table.concat({"testing"; "boop"}, "\n"); };
+ }
+ })
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
+function tests.basic_check_buffer_open_and_change_multi()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didOpen', {
+ textDocument = {
+ languageId = "";
+ text = table.concat({"testing"; "123"}, "\n");
+ uri = "file://";
+ version = 0;
+ };
+ })
+ expect_notification('textDocument/didChange', {
+ textDocument = {
+ uri = "file://";
+ version = 3;
+ };
+ contentChanges = {
+ { text = table.concat({"testing"; "321"}, "\n"); };
+ }
+ })
+ expect_notification('textDocument/didChange', {
+ textDocument = {
+ uri = "file://";
+ version = 4;
+ };
+ contentChanges = {
+ { text = table.concat({"testing"; "boop"}, "\n"); };
+ }
+ })
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
+function tests.basic_check_buffer_open_and_change_multi_and_close()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Full;
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didOpen', {
+ textDocument = {
+ languageId = "";
+ text = table.concat({"testing"; "123"}, "\n");
+ uri = "file://";
+ version = 0;
+ };
+ })
+ expect_notification('textDocument/didChange', {
+ textDocument = {
+ uri = "file://";
+ version = 3;
+ };
+ contentChanges = {
+ { text = table.concat({"testing"; "321"}, "\n"); };
+ }
+ })
+ expect_notification('textDocument/didChange', {
+ textDocument = {
+ uri = "file://";
+ version = 4;
+ };
+ contentChanges = {
+ { text = table.concat({"testing"; "boop"}, "\n"); };
+ }
+ })
+ expect_notification('textDocument/didClose', {
+ textDocument = {
+ uri = "file://";
+ };
+ })
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
+function tests.basic_check_buffer_open_and_change_incremental()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Incremental;
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didOpen', {
+ textDocument = {
+ languageId = "";
+ text = table.concat({"testing"; "123"}, "\n");
+ uri = "file://";
+ version = 0;
+ };
+ })
+ expect_notification('textDocument/didChange', {
+ textDocument = {
+ uri = "file://";
+ version = 3;
+ };
+ contentChanges = {
+ {
+ range = {
+ start = { line = 1; character = 0; };
+ ["end"] = { line = 2; character = 0; };
+ };
+ rangeLength = 4;
+ text = "boop\n";
+ };
+ }
+ })
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
+function tests.basic_check_buffer_open_and_change_incremental_editting()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ textDocumentSync = protocol.TextDocumentSyncKind.Incremental;
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_notification('textDocument/didOpen', {
+ textDocument = {
+ languageId = "";
+ text = table.concat({"testing"; "123"}, "\n");
+ uri = "file://";
+ version = 0;
+ };
+ })
+ expect_notification('textDocument/didChange', {
+ textDocument = {
+ uri = "file://";
+ version = 3;
+ };
+ contentChanges = {
+ {
+ range = {
+ start = { line = 0; character = 0; };
+ ["end"] = { line = 1; character = 0; };
+ };
+ rangeLength = 4;
+ text = "testing\n\n";
+ };
+ }
+ })
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
+function tests.invalid_header()
+ io.stdout:write("Content-length: \r\n")
+end
+
+-- Tests will be indexed by TEST_NAME
+
+local kill_timer = vim.loop.new_timer()
+kill_timer:start(_G.TIMEOUT or 1e3, 0, function()
+ kill_timer:stop()
+ kill_timer:close()
+ io.stderr:write("TIMEOUT")
+ os.exit(100)
+end)
+
+local test_name = _G.TEST_NAME -- lualint workaround
+assert(type(test_name) == 'string', 'TEST_NAME must be specified.')
+local status, err = pcall(assert(tests[test_name], "Test not found"))
+kill_timer:stop()
+kill_timer:close()
+if not status then
+ io.stderr:write(err)
+ os.exit(1)
+end
+os.exit(0)
diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua
index b1dc5c07fd..23167d3ed9 100644
--- a/test/functional/lua/api_spec.lua
+++ b/test/functional/lua/api_spec.lua
@@ -155,41 +155,41 @@ describe('luaeval(vim.api.…)', function()
it('errors out correctly when working with API', function()
-- Conversion errors
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Cannot convert given lua type',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
exc_exec([[call luaeval("vim.api.nvim__id(vim.api.nvim__id)")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Cannot convert given lua table',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table',
exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Cannot convert given lua type',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
exc_exec([[call luaeval("vim.api.nvim__id({42, vim.api.nvim__id})")]]))
-- Errors in number of arguments
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected 1 argument',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
exc_exec([[call luaeval("vim.api.nvim__id()")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected 1 argument',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
exc_exec([[call luaeval("vim.api.nvim__id(1, 2)")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected 2 arguments',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 2 arguments',
exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]]))
-- Error in argument types
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua string',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua string',
exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2)")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua number',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua number',
exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Number is not integral',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Number is not integral',
exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua table',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Unexpected type',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
exc_exec([[call luaeval("vim.api.nvim__id_float({[vim.type_idx]=vim.types.dictionary})")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua table',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
exc_exec([[call luaeval("vim.api.nvim__id_array(1)")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Unexpected type',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
exc_exec([[call luaeval("vim.api.nvim__id_array({[vim.type_idx]=vim.types.dictionary})")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Expected lua table',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table',
exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: Unexpected type',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type',
exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]]))
-- TODO: check for errors with Tabpage argument
-- TODO: check for errors with Window argument
diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua
index 26dcbe0534..96eaa7991b 100644
--- a/test/functional/lua/commands_spec.lua
+++ b/test/functional/lua/commands_spec.lua
@@ -13,6 +13,7 @@ local source = helpers.source
local dedent = helpers.dedent
local command = helpers.command
local exc_exec = helpers.exc_exec
+local pcall_err = helpers.pcall_err
local write_file = helpers.write_file
local redir_exec = helpers.redir_exec
local curbufmeths = helpers.curbufmeths
@@ -42,16 +43,16 @@ describe(':lua command', function()
eq({'', 'ETTS', 'TTSE', 'STTE'}, curbufmeths.get_lines(0, 100, false))
end)
it('throws catchable errors', function()
- eq([[Vim(lua):E5104: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unexpected symbol near ')']],
- exc_exec('lua ()'))
- eq([[Vim(lua):E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: TEST]],
+ eq([[Vim(lua):E5107: Error loading lua [string ":lua"]:1: unexpected symbol near ')']],
+ pcall_err(command, 'lua ()'))
+ eq([[Vim(lua):E5108: Error executing lua [string ":lua"]:1: TEST]],
exc_exec('lua error("TEST")'))
- eq([[Vim(lua):E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: Invalid buffer id]],
+ eq([[Vim(lua):E5108: Error executing lua [string ":lua"]:1: Invalid buffer id]],
exc_exec('lua vim.api.nvim_buf_set_lines(-10, 1, 1, false, {"TEST"})'))
eq({''}, curbufmeths.get_lines(0, 100, false))
end)
it('works with NULL errors', function()
- eq([=[Vim(lua):E5105: Error while calling lua chunk: [NULL]]=],
+ eq([=[Vim(lua):E5108: Error executing lua [NULL]]=],
exc_exec('lua error(nil)'))
end)
it('accepts embedded NLs without heredoc', function()
@@ -74,7 +75,7 @@ describe(':lua command', function()
it('works with long strings', function()
local s = ('x'):rep(100500)
- eq('\nE5104: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unfinished string near \'<eof>\'', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s})'):format(s)))
+ eq('\nE5107: Error loading lua [string ":lua"]:1: unfinished string near \'<eof>\'', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s})'):format(s)))
eq({''}, curbufmeths.get_lines(0, -1, false))
eq('', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s)))
@@ -82,7 +83,7 @@ describe(':lua command', function()
end)
it('can show multiline error messages', function()
- local screen = Screen.new(50,10)
+ local screen = Screen.new(40,10)
screen:attach()
screen:set_default_attr_ids({
[1] = {bold = true, foreground = Screen.colors.Blue1},
@@ -92,51 +93,51 @@ describe(':lua command', function()
})
feed(':lua error("fail\\nmuch error\\nsuch details")<cr>')
- screen:expect([[
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {2: }|
- {3:E5105: Error while calling lua chunk: [string "<Vi}|
- {3:mL compiled string>"]:1: fail} |
- {3:much error} |
- {3:such details} |
- {4:Press ENTER or type command to continue}^ |
- ]])
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ {3:E5108: Error executing lua [string ":lua}|
+ {3:"]:1: fail} |
+ {3:much error} |
+ {3:such details} |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
feed('<cr>')
- screen:expect([[
- ^ |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- |
- ]])
- eq('E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: fail\nmuch error\nsuch details', eval('v:errmsg'))
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ eq('E5108: Error executing lua [string ":lua"]:1: fail\nmuch error\nsuch details', eval('v:errmsg'))
local status, err = pcall(command,'lua error("some error\\nin a\\nAPI command")')
- local expected = 'Vim(lua):E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: some error\nin a\nAPI command'
+ local expected = 'Vim(lua):E5108: Error executing lua [string ":lua"]:1: some error\nin a\nAPI command'
eq(false, status)
eq(expected, string.sub(err, -string.len(expected)))
feed(':messages<cr>')
- screen:expect([[
- |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {2: }|
- {3:E5105: Error while calling lua chunk: [string "<Vi}|
- {3:mL compiled string>"]:1: fail} |
- {3:much error} |
- {3:such details} |
- {4:Press ENTER or type command to continue}^ |
- ]])
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ {3:E5108: Error executing lua [string ":lua}|
+ {3:"]:1: fail} |
+ {3:much error} |
+ {3:such details} |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
end)
end)
@@ -167,13 +168,13 @@ describe(':luado command', function()
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
it('fails on errors', function()
- eq([[Vim(luado):E5109: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unexpected symbol near ')']],
+ eq([[Vim(luado):E5109: Error loading lua: [string ":luado"]:1: unexpected symbol near ')']],
exc_exec('luado ()'))
- eq([[Vim(luado):E5111: Error while calling lua function: [string "<VimL compiled string>"]:1: attempt to perform arithmetic on global 'liness' (a nil value)]],
+ eq([[Vim(luado):E5111: Error calling lua: [string ":luado"]:1: attempt to perform arithmetic on global 'liness' (a nil value)]],
exc_exec('luado return liness + 1'))
end)
it('works with NULL errors', function()
- eq([=[Vim(luado):E5111: Error while calling lua function: [NULL]]=],
+ eq([=[Vim(luado):E5111: Error calling lua: [NULL]]=],
exc_exec('luado error(nil)'))
end)
it('fails in sandbox when needed', function()
@@ -185,7 +186,7 @@ describe(':luado command', function()
it('works with long strings', function()
local s = ('x'):rep(100500)
- eq('\nE5109: Error while creating lua chunk: [string "<VimL compiled string>"]:1: unfinished string near \'<eof>\'', redir_exec(('luado return "%s'):format(s)))
+ eq('\nE5109: Error loading lua: [string ":luado"]:1: unfinished string near \'<eof>\'', redir_exec(('luado return "%s'):format(s)))
eq({''}, curbufmeths.get_lines(0, -1, false))
eq('', redir_exec(('luado return "%s"'):format(s)))
diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua
index 760105df6b..61c8e5c02e 100644
--- a/test/functional/lua/luaeval_spec.lua
+++ b/test/functional/lua/luaeval_spec.lua
@@ -1,13 +1,17 @@
-- Test suite for testing luaeval() function
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local redir_exec = helpers.redir_exec
+local pcall_err = helpers.pcall_err
local exc_exec = helpers.exc_exec
+local exec_lua = helpers.exec_lua
local command = helpers.command
local meths = helpers.meths
local funcs = helpers.funcs
local clear = helpers.clear
local eval = helpers.eval
+local feed = helpers.feed
local NIL = helpers.NIL
local eq = helpers.eq
@@ -186,9 +190,9 @@ describe('luaeval()', function()
exc_exec('call luaeval("{1, foo=2}")'))
eq("Vim(call):E5101: Cannot convert given lua type",
exc_exec('call luaeval("vim.api.nvim_buf_get_lines")'))
- startswith("Vim(call):E5107: Error while creating lua chunk for luaeval(): ",
+ startswith("Vim(call):E5107: Error loading lua [string \"luaeval()\"]:",
exc_exec('call luaeval("1, 2, 3")'))
- startswith("Vim(call):E5108: Error while calling lua chunk for luaeval(): ",
+ startswith("Vim(call):E5108: Error executing lua [string \"luaeval()\"]:",
exc_exec('call luaeval("(nil)()")'))
eq("Vim(call):E5101: Cannot convert given lua type",
exc_exec('call luaeval("{42, vim.api}")'))
@@ -237,19 +241,99 @@ describe('luaeval()', function()
it('errors out correctly when doing incorrect things in lua', function()
-- Conversion errors
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: attempt to call field \'xxx_nonexistent_key_xxx\' (a nil value)',
exc_exec([[call luaeval("vim.xxx_nonexistent_key_xxx()")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [string "<VimL compiled string>"]:1: ERROR',
+ eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: ERROR',
exc_exec([[call luaeval("error('ERROR')")]]))
- eq('Vim(call):E5108: Error while calling lua chunk for luaeval(): [NULL]',
+ eq('Vim(call):E5108: Error executing lua [NULL]',
exc_exec([[call luaeval("error(nil)")]]))
end)
it('does not leak memory when called with too long line',
function()
local s = ('x'):rep(65536)
- eq('Vim(call):E5107: Error while creating lua chunk for luaeval(): [string "<VimL compiled string>"]:1: unexpected symbol near \')\'',
+ eq('Vim(call):E5107: Error loading lua [string "luaeval()"]:1: unexpected symbol near \')\'',
exc_exec([[call luaeval("(']] .. s ..[[' + )")]]))
eq(s, funcs.luaeval('"' .. s .. '"'))
end)
end)
+
+describe('v:lua', function()
+ before_each(function()
+ exec_lua([[
+ function _G.foo(a,b,n)
+ _G.val = n
+ return a+b
+ end
+ mymod = {}
+ function mymod.noisy(name)
+ vim.api.nvim_set_current_line("hey "..name)
+ end
+ function mymod.crashy()
+ nonexistent()
+ end
+ function mymod.omni(findstart, base)
+ if findstart == 1 then
+ return 5
+ else
+ if base == 'st' then
+ return {'stuff', 'steam', 'strange things'}
+ end
+ end
+ end
+ vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omni')
+ ]])
+ end)
+
+ it('works in expressions', function()
+ eq(7, eval('v:lua.foo(3,4,v:null)'))
+ eq(true, exec_lua([[return _G.val == vim.NIL]]))
+ eq(NIL, eval('v:lua.mymod.noisy("eval")'))
+ eq("hey eval", meths.get_current_line())
+
+ eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:10: attempt to call global 'nonexistent' (a nil value)",
+ pcall_err(eval, 'v:lua.mymod.crashy()'))
+ end)
+
+ it('works in :call', function()
+ command(":call v:lua.mymod.noisy('command')")
+ eq("hey command", meths.get_current_line())
+ eq("Vim(call):E5108: Error executing lua [string \"<nvim>\"]:10: attempt to call global 'nonexistent' (a nil value)",
+ pcall_err(command, 'call v:lua.mymod.crashy()'))
+ end)
+
+ it('works in func options', function()
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {background = Screen.colors.WebGray},
+ [3] = {background = Screen.colors.LightMagenta},
+ [4] = {bold = true},
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ })
+ screen:attach()
+ feed('isome st<c-x><c-o>')
+ screen:expect{grid=[[
+ some stuff^ |
+ {1:~ }{2: stuff }{1: }|
+ {1:~ }{3: steam }{1: }|
+ {1:~ }{3: strange things }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- Omni completion (^O^N^P) }{5:match 1 of 3} |
+ ]]}
+ end)
+
+ it('throw errors for invalid use', function()
+ eq('Vim(let):E15: Invalid expression: v:lua.func', pcall_err(command, "let g:Func = v:lua.func"))
+ eq('Vim(let):E15: Invalid expression: v:lua', pcall_err(command, "let g:Func = v:lua"))
+ eq("Vim(let):E15: Invalid expression: v:['lua']", pcall_err(command, "let g:Func = v:['lua']"))
+
+ eq("Vim:E15: Invalid expression: v:['lua'].foo()", pcall_err(eval, "v:['lua'].foo()"))
+ eq("Vim(call):E461: Illegal variable name: v:['lua']", pcall_err(command, "call v:['lua'].baar()"))
+
+ eq("Vim(let):E46: Cannot change read-only variable \"v:['lua']\"", pcall_err(command, "let v:['lua'] = 'xx'"))
+ eq("Vim(let):E46: Cannot change read-only variable \"v:lua\"", pcall_err(command, "let v:lua = 'xx'"))
+ end)
+end)
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index f6439001ac..8c260632d9 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -54,11 +54,12 @@ describe('print', function()
v_tblout = setmetatable({}, meta_tblout)
]])
eq('', redir_exec('luafile ' .. fname))
- eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: [NULL]',
+ -- TODO(bfredl): these look weird, print() should not use "E5114:" style errors..
+ eq('\nE5108: Error executing lua E5114: Error while converting print argument #2: [NULL]',
redir_exec('lua print("foo", v_nilerr, "bar")'))
- eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
+ eq('\nE5108: Error executing lua E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
redir_exec('lua print("foo", v_abcerr, "bar")'))
- eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
+ eq('\nE5108: Error executing lua E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
redir_exec('lua print("foo", v_tblout, "bar")'))
end)
it('prints strings with NULs and NLs correctly', function()
@@ -156,7 +157,8 @@ describe('debug.debug', function()
lua_debug> ^ |
]])
feed('<C-c>')
- screen:expect([[
+ screen:expect{grid=[[
+ {0:~ }|
{0:~ }|
{0:~ }|
{0:~ }|
@@ -167,11 +169,10 @@ describe('debug.debug', function()
lua_debug> print("TEST") |
TEST |
|
- {E:E5105: Error while calling lua chunk: [string "<VimL }|
- {E:compiled string>"]:5: attempt to perform arithmetic o}|
- {E:n local 'a' (a nil value)} |
+ {E:E5108: Error executing lua [string ":lua"]:5: attempt}|
+ {E: to perform arithmetic on local 'a' (a nil value)} |
Interrupt: {cr:Press ENTER or type command to continue}^ |
- ]])
+ ]]}
feed('<C-l>:lua Test()\n')
screen:expect([[
{0:~ }|
@@ -190,7 +191,8 @@ describe('debug.debug', function()
lua_debug> ^ |
]])
feed('\n')
- screen:expect([[
+ screen:expect{grid=[[
+ {0:~ }|
{0:~ }|
{0:~ }|
{0:~ }|
@@ -201,11 +203,10 @@ describe('debug.debug', function()
{0:~ }|
nil |
lua_debug> |
- {E:E5105: Error while calling lua chunk: [string "<VimL }|
- {E:compiled string>"]:5: attempt to perform arithmetic o}|
- {E:n local 'a' (a nil value)} |
+ {E:E5108: Error executing lua [string ":lua"]:5: attempt}|
+ {E: to perform arithmetic on local 'a' (a nil value)} |
{cr:Press ENTER or type command to continue}^ |
- ]])
+ ]]}
end)
it("can be safely exited with 'cont'", function()
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
new file mode 100644
index 0000000000..19b1eb1f61
--- /dev/null
+++ b/test/functional/lua/uri_spec.lua
@@ -0,0 +1,107 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+
+describe('URI methods', function()
+ before_each(function()
+ clear()
+ end)
+
+ describe('file path to uri', function()
+ describe('encode Unix file path', function()
+ it('file path includes only ascii charactors', function()
+ exec_lua("filepath = '/Foo/Bar/Baz.txt'")
+
+ eq('file:///Foo/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
+ end)
+
+ it('file path including white space', function()
+ exec_lua("filepath = '/Foo /Bar/Baz.txt'")
+
+ eq('file:///Foo%20/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
+ end)
+
+ it('file path including Unicode charactors', function()
+ exec_lua("filepath = '/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt'")
+
+ -- The URI encoding should be case-insensitive
+ eq('file:///xy/%c3%a5%c3%a4%c3%b6/%c9%a7/%e6%b1%89%e8%af%ad/%e2%86%a5/%f0%9f%a4%a6/%f0%9f%a6%84/a%cc%8a/%d8%a8%d9%90%d9%8a%d9%8e%d9%91.txt', exec_lua("return vim.uri_from_fname(filepath)"))
+ end)
+ end)
+
+ describe('encode Windows filepath', function()
+ it('file path includes only ascii charactors', function()
+ exec_lua([[filepath = 'C:\\Foo\\Bar\\Baz.txt']])
+
+ eq('file:///C:/Foo/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
+ end)
+
+ it('file path including white space', function()
+ exec_lua([[filepath = 'C:\\Foo \\Bar\\Baz.txt']])
+
+ eq('file:///C:/Foo%20/Bar/Baz.txt', exec_lua("return vim.uri_from_fname(filepath)"))
+ end)
+
+ it('file path including Unicode charactors', function()
+ exec_lua([[filepath = 'C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt']])
+
+ eq('file:///C:/xy/%c3%a5%c3%a4%c3%b6/%c9%a7/%e6%b1%89%e8%af%ad/%e2%86%a5/%f0%9f%a4%a6/%f0%9f%a6%84/a%cc%8a/%d8%a8%d9%90%d9%8a%d9%8e%d9%91.txt', exec_lua("return vim.uri_from_fname(filepath)"))
+ end)
+ end)
+ end)
+
+ describe('uri to filepath', function()
+ describe('decode Unix file path', function()
+ it('file path includes only ascii charactors', function()
+ exec_lua("uri = 'file:///Foo/Bar/Baz.txt'")
+
+ eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)"))
+ end)
+
+ it('file path including white space', function()
+ exec_lua("uri = 'file:///Foo%20/Bar/Baz.txt'")
+
+ eq('/Foo /Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)"))
+ end)
+
+ it('file path including Unicode charactors', function()
+ local test_case = [[
+ local uri = 'file:///xy/%C3%A5%C3%A4%C3%B6/%C9%A7/%E6%B1%89%E8%AF%AD/%E2%86%A5/%F0%9F%A4%A6/%F0%9F%A6%84/a%CC%8A/%D8%A8%D9%90%D9%8A%D9%8E%D9%91.txt'
+ return vim.uri_to_fname(uri)
+ ]]
+
+ eq('/xy/åäö/ɧ/汉语/↥/🤦/🦄/å/بِيَّ.txt', exec_lua(test_case))
+ end)
+ end)
+
+ describe('decode Windows filepath', function()
+ it('file path includes only ascii charactors', function()
+ local test_case = [[
+ local uri = 'file:///C:/Foo/Bar/Baz.txt'
+ return vim.uri_to_fname(uri)
+ ]]
+
+ eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case))
+ end)
+
+ it('file path including white space', function()
+ local test_case = [[
+ local uri = 'file:///C:/Foo%20/Bar/Baz.txt'
+ return vim.uri_to_fname(uri)
+ ]]
+
+ eq('C:\\Foo \\Bar\\Baz.txt', exec_lua(test_case))
+ end)
+
+ it('file path including Unicode charactors', function()
+ local test_case = [[
+ local uri = 'file:///C:/xy/%C3%A5%C3%A4%C3%B6/%C9%A7/%E6%B1%89%E8%AF%AD/%E2%86%A5/%F0%9F%A4%A6/%F0%9F%A6%84/a%CC%8A/%D8%A8%D9%90%D9%8A%D9%8E%D9%91.txt'
+ return vim.uri_to_fname(uri)
+ ]]
+
+ eq('C:\\xy\\åäö\\ɧ\\汉语\\↥\\🤦\\🦄\\å\\بِيَّ.txt', exec_lua(test_case))
+ end)
+ end)
+ end)
+end)
diff --git a/test/functional/lua/utility_functions_spec.lua b/test/functional/lua/vim_spec.lua
index 6aeea5fc4f..028f2dcd52 100644
--- a/test/functional/lua/utility_functions_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -3,6 +3,8 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local funcs = helpers.funcs
+local meths = helpers.meths
+local command = helpers.command
local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
@@ -11,6 +13,8 @@ local pcall_err = helpers.pcall_err
local exec_lua = helpers.exec_lua
local matches = helpers.matches
local source = helpers.source
+local NIL = helpers.NIL
+local retry = helpers.retry
before_each(clear)
@@ -226,12 +230,12 @@ describe('lua stdlib', function()
end
-- Validates args.
- eq(true, pcall(split, 'string', 'string', nil))
- eq('Error executing lua: .../shared.lua: Expected string, got number',
- pcall_err(split, 1, 'string', nil))
- eq('Error executing lua: .../shared.lua: Expected string, got number',
- pcall_err(split, 'string', 1, nil))
- eq('Error executing lua: .../shared.lua: Expected boolean or nil, got number',
+ eq(true, pcall(split, 'string', 'string'))
+ eq('Error executing lua: .../shared.lua: s: expected string, got number',
+ pcall_err(split, 1, 'string'))
+ eq('Error executing lua: .../shared.lua: sep: expected string, got number',
+ pcall_err(split, 'string', 1))
+ eq('Error executing lua: .../shared.lua: plain: expected boolean, got number',
pcall_err(split, 'string', 'string', 1))
end)
@@ -252,7 +256,7 @@ describe('lua stdlib', function()
end
-- Validates args.
- eq('Error executing lua: .../shared.lua: Expected string, got number',
+ eq('Error executing lua: .../shared.lua: s: expected string, got number',
pcall_err(trim, 2))
end)
@@ -297,10 +301,82 @@ describe('lua stdlib', function()
eq('foo%%%-bar', exec_lua([[return vim.pesc(vim.pesc('foo-bar'))]]))
-- Validates args.
- eq("Error executing lua: .../shared.lua: Expected string, got number",
+ eq('Error executing lua: .../shared.lua: s: expected string, got number',
pcall_err(exec_lua, [[return vim.pesc(2)]]))
end)
+ it('vim.tbl_keys', function()
+ eq({}, exec_lua("return vim.tbl_keys({})"))
+ for _, v in pairs(exec_lua("return vim.tbl_keys({'a', 'b', 'c'})")) do
+ eq(true, exec_lua("return vim.tbl_contains({ 1, 2, 3 }, ...)", v))
+ end
+ for _, v in pairs(exec_lua("return vim.tbl_keys({a=1, b=2, c=3})")) do
+ eq(true, exec_lua("return vim.tbl_contains({ 'a', 'b', 'c' }, ...)", v))
+ end
+ end)
+
+ it('vim.tbl_values', function()
+ eq({}, exec_lua("return vim.tbl_values({})"))
+ for _, v in pairs(exec_lua("return vim.tbl_values({'a', 'b', 'c'})")) do
+ eq(true, exec_lua("return vim.tbl_contains({ 'a', 'b', 'c' }, ...)", v))
+ end
+ for _, v in pairs(exec_lua("return vim.tbl_values({a=1, b=2, c=3})")) do
+ eq(true, exec_lua("return vim.tbl_contains({ 1, 2, 3 }, ...)", v))
+ end
+ end)
+
+ it('vim.tbl_islist', function()
+ eq(NIL, exec_lua("return vim.tbl_islist({})"))
+ eq(true, exec_lua("return vim.tbl_islist({'a', 'b', 'c'})"))
+ eq(false, exec_lua("return vim.tbl_islist({'a', '32', a='hello', b='baz'})"))
+ eq(false, exec_lua("return vim.tbl_islist({1, a='hello', b='baz'})"))
+ eq(false, exec_lua("return vim.tbl_islist({a='hello', b='baz', 1})"))
+ eq(false, exec_lua("return vim.tbl_islist({1, 2, nil, a='hello'})"))
+ end)
+
+ it('vim.tbl_isempty', function()
+ eq(true, exec_lua("return vim.tbl_isempty({})"))
+ eq(false, exec_lua("return vim.tbl_isempty({ 1, 2, 3 })"))
+ eq(false, exec_lua("return vim.tbl_isempty({a=1, b=2, c=3})"))
+ end)
+
+ it('vim.deep_equal', function()
+ eq(true, exec_lua [[ return vim.deep_equal({a=1}, {a=1}) ]])
+ eq(true, exec_lua [[ return vim.deep_equal({a={b=1}}, {a={b=1}}) ]])
+ eq(true, exec_lua [[ return vim.deep_equal({a={b={nil}}}, {a={b={}}}) ]])
+ eq(true, exec_lua [[ return vim.deep_equal({a=1, [5]=5}, {nil,nil,nil,nil,5,a=1}) ]])
+ eq(false, exec_lua [[ return vim.deep_equal(1, {nil,nil,nil,nil,5,a=1}) ]])
+ eq(false, exec_lua [[ return vim.deep_equal(1, 3) ]])
+ eq(false, exec_lua [[ return vim.deep_equal(nil, 3) ]])
+ eq(false, exec_lua [[ return vim.deep_equal({a=1}, {a=2}) ]])
+ end)
+
+ it('vim.list_extend', function()
+ eq({1,2,3}, exec_lua [[ return vim.list_extend({1}, {2,3}) ]])
+ eq('Error executing lua: .../shared.lua: src must be a table',
+ pcall_err(exec_lua, [[ return vim.list_extend({1}, nil) ]]))
+ eq({1,2}, exec_lua [[ return vim.list_extend({1}, {2;a=1}) ]])
+ eq(true, exec_lua [[ local a = {1} return vim.list_extend(a, {2;a=1}) == a ]])
+ end)
+
+ it('vim.tbl_add_reverse_lookup', function()
+ eq(true, exec_lua [[
+ local a = { A = 1 }
+ vim.tbl_add_reverse_lookup(a)
+ return vim.deep_equal(a, { A = 1; [1] = 'A'; })
+ ]])
+ -- Throw an error for trying to do it twice (run into an existing key)
+ local code = [[
+ local res = {}
+ local a = { A = 1 }
+ vim.tbl_add_reverse_lookup(a)
+ assert(vim.deep_equal(a, { A = 1; [1] = 'A'; }))
+ vim.tbl_add_reverse_lookup(a)
+ ]]
+ matches('Error executing lua: .../shared.lua: The reverse lookup found an existing value for "[1A]" while processing key "[1A]"',
+ pcall_err(exec_lua, code))
+ end)
+
it('vim.call, vim.fn', function()
eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]]))
eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]]))
@@ -315,6 +391,9 @@ describe('lua stdlib', function()
func! VarArg(...)
return a:000
endfunc
+ func! Nilly()
+ return [v:null, v:null]
+ endfunc
]])
eq(true, exec_lua([[return next(vim.fn.FooFunc(3)) == nil ]]))
eq(3, eval("g:test"))
@@ -324,7 +403,150 @@ describe('lua stdlib', function()
eq({2, "foo", true}, exec_lua([[return vim.fn.VarArg(2, "foo", true)]]))
+ eq(true, exec_lua([[
+ local x = vim.fn.Nilly()
+ return #x == 2 and x[1] == vim.NIL and x[2] == vim.NIL
+ ]]))
+ eq({NIL, NIL}, exec_lua([[return vim.fn.Nilly()]]))
+
-- error handling
eq({false, 'Vim:E714: List required'}, exec_lua([[return {pcall(vim.fn.add, "aa", "bb")}]]))
end)
+
+ it('vim.rpcrequest and vim.rpcnotify', function()
+ exec_lua([[
+ chan = vim.fn.jobstart({'cat'}, {rpc=true})
+ vim.rpcrequest(chan, 'nvim_set_current_line', 'meow')
+ ]])
+ eq('meow', meths.get_current_line())
+ command("let x = [3, 'aa', v:true, v:null]")
+ eq(true, exec_lua([[
+ ret = vim.rpcrequest(chan, 'nvim_get_var', 'x')
+ return #ret == 4 and ret[1] == 3 and ret[2] == 'aa' and ret[3] == true and ret[4] == vim.NIL
+ ]]))
+ eq({3, 'aa', true, NIL}, exec_lua([[return ret]]))
+
+ -- error handling
+ eq({false, 'Invalid channel: 23'},
+ exec_lua([[return {pcall(vim.rpcrequest, 23, 'foo')}]]))
+ eq({false, 'Invalid channel: 23'},
+ exec_lua([[return {pcall(vim.rpcnotify, 23, 'foo')}]]))
+
+ eq({false, 'Vim:E121: Undefined variable: foobar'},
+ exec_lua([[return {pcall(vim.rpcrequest, chan, 'nvim_eval', "foobar")}]]))
+
+
+ -- rpcnotify doesn't wait on request
+ eq('meow', exec_lua([[
+ vim.rpcnotify(chan, 'nvim_set_current_line', 'foo')
+ return vim.api.nvim_get_current_line()
+ ]]))
+ retry(10, nil, function()
+ eq('foo', meths.get_current_line())
+ end)
+
+ local screen = Screen.new(50,7)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {bold = true, reverse = true},
+ [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ })
+ screen:attach()
+ exec_lua([[
+ local timer = vim.loop.new_timer()
+ timer:start(20, 0, function ()
+ -- notify ok (executed later when safe)
+ vim.rpcnotify(chan, 'nvim_set_var', 'yy', {3, vim.NIL})
+ -- rpcrequest an error
+ vim.rpcrequest(chan, 'nvim_set_current_line', 'bork')
+ end)
+ ]])
+ screen:expect{grid=[[
+ foo |
+ {1:~ }|
+ {2: }|
+ {3:Error executing luv callback:} |
+ {3:[string "<nvim>"]:6: E5560: rpcrequest must not be}|
+ {3: called in a lua loop callback} |
+ {4:Press ENTER or type command to continue}^ |
+ ]]}
+ feed('<cr>')
+ eq({3, NIL}, meths.get_var('yy'))
+ end)
+
+ it('vim.validate', function()
+ exec_lua("vim.validate{arg1={{}, 'table' }}")
+ exec_lua("vim.validate{arg1={{}, 't' }}")
+ exec_lua("vim.validate{arg1={nil, 't', true }}")
+ exec_lua("vim.validate{arg1={{ foo='foo' }, 't' }}")
+ exec_lua("vim.validate{arg1={{ 'foo' }, 't' }}")
+ exec_lua("vim.validate{arg1={'foo', 'string' }}")
+ exec_lua("vim.validate{arg1={'foo', 's' }}")
+ exec_lua("vim.validate{arg1={'', 's' }}")
+ exec_lua("vim.validate{arg1={nil, 's', true }}")
+ exec_lua("vim.validate{arg1={1, 'number' }}")
+ exec_lua("vim.validate{arg1={1, 'n' }}")
+ exec_lua("vim.validate{arg1={0, 'n' }}")
+ exec_lua("vim.validate{arg1={0.1, 'n' }}")
+ exec_lua("vim.validate{arg1={nil, 'n', true }}")
+ exec_lua("vim.validate{arg1={true, 'boolean' }}")
+ exec_lua("vim.validate{arg1={true, 'b' }}")
+ exec_lua("vim.validate{arg1={false, 'b' }}")
+ exec_lua("vim.validate{arg1={nil, 'b', true }}")
+ exec_lua("vim.validate{arg1={function()end, 'function' }}")
+ exec_lua("vim.validate{arg1={function()end, 'f' }}")
+ exec_lua("vim.validate{arg1={nil, 'f', true }}")
+ exec_lua("vim.validate{arg1={nil, 'nil' }}")
+ exec_lua("vim.validate{arg1={nil, 'nil', true }}")
+ exec_lua("vim.validate{arg1={coroutine.create(function()end), 'thread' }}")
+ exec_lua("vim.validate{arg1={nil, 'thread', true }}")
+ exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}")
+ exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}")
+
+ eq("Error executing lua: .../shared.lua: 1: expected table, got number",
+ pcall_err(exec_lua, "vim.validate{ 1, 'x' }"))
+ eq("Error executing lua: .../shared.lua: invalid type name: x",
+ pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}"))
+ eq("Error executing lua: .../shared.lua: invalid type name: 1",
+ pcall_err(exec_lua, "vim.validate{ arg1={ 1, 1 }}"))
+ eq("Error executing lua: .../shared.lua: invalid type name: nil",
+ pcall_err(exec_lua, "vim.validate{ arg1={ 1 }}"))
+
+ -- Validated parameters are required by default.
+ eq("Error executing lua: .../shared.lua: arg1: expected string, got nil",
+ pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's' }}"))
+ -- Explicitly required.
+ eq("Error executing lua: .../shared.lua: arg1: expected string, got nil",
+ pcall_err(exec_lua, "vim.validate{ arg1={ nil, 's', false }}"))
+
+ eq("Error executing lua: .../shared.lua: arg1: expected table, got number",
+ pcall_err(exec_lua, "vim.validate{arg1={1, 't'}}"))
+ eq("Error executing lua: .../shared.lua: arg2: expected string, got number",
+ pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={1, 's'}}"))
+ eq("Error executing lua: .../shared.lua: arg2: expected string, got nil",
+ pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}"))
+ eq("Error executing lua: .../shared.lua: arg2: expected string, got nil",
+ pcall_err(exec_lua, "vim.validate{arg1={{}, 't'}, arg2={nil, 's'}}"))
+ eq("Error executing lua: .../shared.lua: arg1: expected even number, got 3",
+ pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end, 'even number'}}"))
+ eq("Error executing lua: .../shared.lua: arg1: expected ?, got 3",
+ pcall_err(exec_lua, "vim.validate{arg1={3, function(a) return a == 1 end}}"))
+ end)
+
+ it('vim.is_callable', function()
+ eq(true, exec_lua("return vim.is_callable(function()end)"))
+ eq(true, exec_lua([[
+ local meta = { __call = function()end }
+ local function new_callable()
+ return setmetatable({}, meta)
+ end
+ local callable = new_callable()
+ return vim.is_callable(callable)
+ ]]))
+
+ eq(false, exec_lua("return vim.is_callable(1)"))
+ eq(false, exec_lua("return vim.is_callable('foo')"))
+ eq(false, exec_lua("return vim.is_callable({})"))
+ end)
end)
diff --git a/test/functional/plugin/lsp/lsp_spec.lua b/test/functional/plugin/lsp/lsp_spec.lua
new file mode 100644
index 0000000000..cd0974b81c
--- /dev/null
+++ b/test/functional/plugin/lsp/lsp_spec.lua
@@ -0,0 +1,634 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local exec_lua = helpers.exec_lua
+local eq = helpers.eq
+local NIL = helpers.NIL
+
+-- Use these to get access to a coroutine so that I can run async tests and use
+-- yield.
+local run, stop = helpers.run, helpers.stop
+
+if helpers.pending_win32(pending) then return end
+
+local is_windows = require'luv'.os_uname().sysname == "Windows"
+local lsp_test_rpc_server_file = "test/functional/fixtures/lsp-test-rpc-server.lua"
+if is_windows then
+ lsp_test_rpc_server_file = lsp_test_rpc_server_file:gsub("/", "\\")
+end
+
+local function test_rpc_server_setup(test_name, timeout_ms)
+ exec_lua([=[
+ lsp = require('vim.lsp')
+ local test_name, fixture_filename, timeout = ...
+ TEST_RPC_CLIENT_ID = lsp.start_client {
+ cmd = {
+ vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless',
+ "-c", string.format("lua TEST_NAME = %q", test_name),
+ "-c", string.format("lua TIMEOUT = %d", timeout),
+ "-c", "luafile "..fixture_filename,
+ };
+ callbacks = setmetatable({}, {
+ __index = function(t, method)
+ return function(...)
+ return vim.rpcrequest(1, 'callback', ...)
+ end
+ end;
+ });
+ root_dir = vim.loop.cwd();
+ on_init = function(client, result)
+ TEST_RPC_CLIENT = client
+ vim.rpcrequest(1, "init", result)
+ end;
+ on_exit = function(...)
+ vim.rpcnotify(1, "exit", ...)
+ end;
+ }
+ ]=], test_name, lsp_test_rpc_server_file, timeout_ms or 1e3)
+end
+
+local function test_rpc_server(config)
+ if config.test_name then
+ clear()
+ test_rpc_server_setup(config.test_name, config.timeout_ms or 1e3)
+ end
+ local client = setmetatable({}, {
+ __index = function(_, name)
+ -- Workaround for not being able to yield() inside __index for Lua 5.1 :(
+ -- Otherwise I would just return the value here.
+ return function(...)
+ return exec_lua([=[
+ local name = ...
+ if type(TEST_RPC_CLIENT[name]) == 'function' then
+ return TEST_RPC_CLIENT[name](select(2, ...))
+ else
+ return TEST_RPC_CLIENT[name]
+ end
+ ]=], name, ...)
+ end
+ end;
+ })
+ local code, signal
+ local function on_request(method, args)
+ if method == "init" then
+ if config.on_init then
+ config.on_init(client, unpack(args))
+ end
+ return NIL
+ end
+ if method == 'callback' then
+ if config.on_callback then
+ config.on_callback(unpack(args))
+ end
+ end
+ return NIL
+ end
+ local function on_notify(method, args)
+ if method == 'exit' then
+ code, signal = unpack(args)
+ return stop()
+ end
+ end
+ -- TODO specify timeout?
+ -- run(on_request, on_notify, config.on_setup, 1000)
+ run(on_request, on_notify, config.on_setup)
+ if config.on_exit then
+ config.on_exit(code, signal)
+ end
+ stop()
+ if config.test_name then
+ exec_lua("lsp._vim_exit_handler()")
+ end
+end
+
+describe('Language Client API', function()
+ describe('server_name is specified', function()
+ before_each(function()
+ clear()
+ -- Run an instance of nvim on the file which contains our "scripts".
+ -- Pass TEST_NAME to pick the script.
+ local test_name = "basic_init"
+ exec_lua([=[
+ lsp = require('vim.lsp')
+ local test_name, fixture_filename = ...
+ TEST_RPC_CLIENT_ID = lsp.start_client {
+ cmd = {
+ vim.api.nvim_get_vvar("progpath"), '-Es', '-u', 'NONE', '--headless',
+ "-c", string.format("lua TEST_NAME = %q", test_name),
+ "-c", "luafile "..fixture_filename;
+ };
+ root_dir = vim.loop.cwd();
+ }
+ ]=], test_name, lsp_test_rpc_server_file)
+ end)
+
+ after_each(function()
+ exec_lua("lsp._vim_exit_handler()")
+ -- exec_lua("lsp.stop_all_clients(true)")
+ end)
+
+ describe('start_client and stop_client', function()
+ it('should return true', function()
+ for _ = 1, 20 do
+ helpers.sleep(10)
+ if exec_lua("return #lsp.get_active_clients()") > 0 then
+ break
+ end
+ end
+ eq(1, exec_lua("return #lsp.get_active_clients()"))
+ eq(false, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID) == nil"))
+ eq(false, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID).is_stopped()"))
+ exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID).stop()")
+ eq(false, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID).is_stopped()"))
+ for _ = 1, 20 do
+ helpers.sleep(10)
+ if exec_lua("return #lsp.get_active_clients()") == 0 then
+ break
+ end
+ end
+ eq(true, exec_lua("return lsp.get_client_by_id(TEST_RPC_CLIENT_ID) == nil"))
+ end)
+ end)
+ end)
+
+ describe('basic_init test', function()
+ it('should run correctly', function()
+ local expected_callbacks = {
+ {NIL, "test", {}, 1};
+ }
+ test_rpc_server {
+ test_name = "basic_init";
+ on_init = function(client, _init_result)
+ -- client is a dummy object which will queue up commands to be run
+ -- once the server initializes. It can't accept lua callbacks or
+ -- other types that may be unserializable for now.
+ client.stop()
+ end;
+ -- If the program timed out, then code will be nil.
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ -- Note that NIL must be used here.
+ -- on_callback(err, method, result, client_id)
+ on_callback = function(...)
+ eq(table.remove(expected_callbacks), {...})
+ end;
+ }
+ end)
+
+ it('should fail', function()
+ local expected_callbacks = {
+ {NIL, "test", {}, 1};
+ }
+ test_rpc_server {
+ test_name = "basic_init";
+ on_init = function(client)
+ client.notify('test')
+ client.stop()
+ end;
+ on_exit = function(code, signal)
+ eq(1, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(...)
+ eq(table.remove(expected_callbacks), {...}, "expected callback")
+ end;
+ }
+ end)
+
+ it('should succeed with manual shutdown', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "test", {}, 1};
+ }
+ test_rpc_server {
+ test_name = "basic_init";
+ on_init = function(client)
+ eq(0, client.resolved_capabilities().text_document_did_change)
+ client.request('shutdown')
+ client.notify('exit')
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(...)
+ eq(table.remove(expected_callbacks), {...}, "expected callback")
+ end;
+ }
+ end)
+
+ it('should verify capabilities sent', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ }
+ test_rpc_server {
+ test_name = "basic_check_capabilities";
+ on_init = function(client)
+ client.stop()
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(...)
+ eq(table.remove(expected_callbacks), {...}, "expected callback")
+ end;
+ }
+ end)
+
+ it('should not send didOpen if the buffer closes before init', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_finish";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ eq(1, exec_lua("return TEST_RPC_CLIENT_ID"))
+ eq(true, exec_lua("return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)"))
+ eq(true, exec_lua("return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)"))
+ exec_lua [[
+ vim.api.nvim_command(BUFFER.."bwipeout")
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
+ eq(full_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ client.notify('finish')
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ it('should check the body sent attaching before init', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
+ eq(full_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(not lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID), "Shouldn't attach twice")
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ if method == 'start' then
+ client.notify('finish')
+ end
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ it('should check the body sent attaching after init', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
+ eq(full_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ if method == 'start' then
+ client.notify('finish')
+ end
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ it('should check the body and didChange full', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open_and_change";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
+ eq(full_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ if method == 'start' then
+ exec_lua [[
+ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
+ "boop";
+ })
+ ]]
+ client.notify('finish')
+ end
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ -- TODO(askhan) we don't support full for now, so we can disable these tests.
+ pending('should check the body and didChange incremental', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open_and_change_incremental";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental")
+ eq(sync_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ if method == 'start' then
+ exec_lua [[
+ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
+ "boop";
+ })
+ ]]
+ client.notify('finish')
+ end
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ -- TODO(askhan) we don't support full for now, so we can disable these tests.
+ pending('should check the body and didChange incremental normal mode editting', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open_and_change_incremental_editting";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Incremental")
+ eq(sync_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ if method == 'start' then
+ helpers.command("normal! 1Go")
+ client.notify('finish')
+ end
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ it('should check the body and didChange full with 2 changes', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open_and_change_multi";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
+ eq(sync_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ if method == 'start' then
+ exec_lua [[
+ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
+ "321";
+ })
+ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
+ "boop";
+ })
+ ]]
+ client.notify('finish')
+ end
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ it('should check the body and didChange full lifecycle', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "basic_check_buffer_open_and_change_multi_and_close";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ local sync_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
+ eq(sync_kind, client.resolved_capabilities().text_document_did_change)
+ eq(true, client.resolved_capabilities().text_document_open_close)
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ if method == 'start' then
+ exec_lua [[
+ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
+ "321";
+ })
+ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
+ "boop";
+ })
+ vim.api.nvim_command(BUFFER.."bwipeout")
+ ]]
+ client.notify('finish')
+ end
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ if method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
+ end)
+
+ describe("parsing tests", function()
+ it('should handle invalid content-length correctly', function()
+ local expected_callbacks = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "invalid_header";
+ on_setup = function()
+ end;
+ on_init = function(_client)
+ client = _client
+ client.stop(true)
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code") eq(0, signal, "exit signal")
+ end;
+ on_callback = function(err, method, params, client_id)
+ eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ end;
+ }
+ end)
+
+ end)
+end)
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
index 5df909f79c..65c5f67726 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -386,6 +386,22 @@ describe('Buffer highlighting', function()
]])
end)
+ it('can be retrieved', function()
+ local get_virtual_text = curbufmeths.get_virtual_text
+ local line_count = curbufmeths.line_count
+
+ local s1 = {{'Köttbullar', 'Comment'}, {'Kräuterbutter'}}
+ local s2 = {{'こんにちは', 'Comment'}}
+
+ set_virtual_text(-1, 0, s1, {})
+ eq(s1, get_virtual_text(0))
+
+ set_virtual_text(-1, line_count(), s2, {})
+ eq(s2, get_virtual_text(line_count()))
+
+ eq({}, get_virtual_text(line_count() + 9000))
+ end)
+
it('is not highlighted by visual selection', function()
feed("ggVG")
screen:expect([[
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 8ad3aff21f..40ea030f73 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -747,7 +747,7 @@ describe('ui/ext_messages', function()
{1:~ }|
{1:~ }|
]], messages={{
- content = {{'E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: such\nmultiline\nerror', 2}},
+ content = {{'E5108: Error executing lua [string ":lua"]:1: such\nmultiline\nerror', 2}},
kind = "lua_error"
}}}
end)
@@ -1146,97 +1146,96 @@ aliquip ex ea commodo consequat.]])
it('handles wrapped lines with line scroll', function()
feed(':lua error(_G.x)<cr>')
screen:expect{grid=[[
- {2:E5105: Error while calling lua chun}|
- {2:k: [string "<VimL compiled string>"}|
- {2:]:1: Lorem ipsum dolor sit amet, co}|
- {2:nsectetur} |
+ {2:E5108: Error executing lua [string }|
+ {2:":lua"]:1: Lorem ipsum dolor sit am}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
+ {2:a aliqua.} |
{4:-- More --}^ |
]]}
feed('j')
screen:expect{grid=[[
- {2:k: [string "<VimL compiled string>"}|
- {2:]:1: Lorem ipsum dolor sit amet, co}|
- {2:nsectetur} |
+ {2:":lua"]:1: Lorem ipsum dolor sit am}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
{2:a aliqua.} |
+ {2:Ut enim ad minim veniam, quis nostr}|
{4:-- More --}^ |
]]}
feed('k')
screen:expect{grid=[[
- {2:E5105: Error while calling lua chun}|
- {2:k: [string "<VimL compiled string>"}|
- {2:]:1: Lorem ipsum dolor sit amet, co}|
- {2:nsectetur} |
+ {2:E5108: Error executing lua [string }|
+ {2:":lua"]:1: Lorem ipsum dolor sit am}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
+ {2:a aliqua.} |
{4:-- More --}^ |
]]}
feed('j')
screen:expect{grid=[[
- {2:k: [string "<VimL compiled string>"}|
- {2:]:1: Lorem ipsum dolor sit amet, co}|
- {2:nsectetur} |
+ {2:":lua"]:1: Lorem ipsum dolor sit am}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
{2:a aliqua.} |
+ {2:Ut enim ad minim veniam, quis nostr}|
{4:-- More --}^ |
]]}
-
end)
it('handles wrapped lines with page scroll', function()
feed(':lua error(_G.x)<cr>')
screen:expect{grid=[[
- {2:E5105: Error while calling lua chun}|
- {2:k: [string "<VimL compiled string>"}|
- {2:]:1: Lorem ipsum dolor sit amet, co}|
- {2:nsectetur} |
+ {2:E5108: Error executing lua [string }|
+ {2:":lua"]:1: Lorem ipsum dolor sit am}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
+ {2:a aliqua.} |
{4:-- More --}^ |
]]}
feed('d')
screen:expect{grid=[[
- {2:adipisicing elit, sed do eiusmod te}|
- {2:mpor} |
{2:incididunt ut labore et dolore magn}|
{2:a aliqua.} |
{2:Ut enim ad minim veniam, quis nostr}|
{2:ud xercitation} |
{2:ullamco laboris nisi ut} |
- {4:-- More --}^ |
+ {2:aliquip ex ea commodo consequat.} |
+ {4:Press ENTER or type command to cont}|
+ {4:inue}^ |
]]}
feed('u')
screen:expect{grid=[[
- {2:E5105: Error while calling lua chun}|
- {2:k: [string "<VimL compiled string>"}|
- {2:]:1: Lorem ipsum dolor sit amet, co}|
- {2:nsectetur} |
+ {2:E5108: Error executing lua [string }|
+ {2:":lua"]:1: Lorem ipsum dolor sit am}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
+ {2:a aliqua.} |
{4:-- More --}^ |
]]}
feed('d')
screen:expect{grid=[[
- {2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
{2:a aliqua.} |
{2:Ut enim ad minim veniam, quis nostr}|
{2:ud xercitation} |
{2:ullamco laboris nisi ut} |
+ {2:aliquip ex ea commodo consequat.} |
{4:-- More --}^ |
]]}
end)
@@ -1246,49 +1245,49 @@ aliquip ex ea commodo consequat.]])
feed(':lua error(_G.x)<cr>')
screen:expect{grid=[[
- {3:E5105: Error while calling lua chun}|
- {3:k: [string "<VimL compiled string>"}|
- {3:]:1: Lorem ipsum dolor sit amet, co}|
- {3:nsectetur}{5: }|
+ {3:E5108: Error executing lua [string }|
+ {3:":lua"]:1: Lorem ipsum dolor sit am}|
+ {3:et, consectetur}{5: }|
{3:adipisicing elit, sed do eiusmod te}|
{3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
+ {3:a aliqua.}{5: }|
{6:-- More --}{5:^ }|
]]}
feed('j')
screen:expect{grid=[[
- {3:k: [string "<VimL compiled string>"}|
- {3:]:1: Lorem ipsum dolor sit amet, co}|
- {3:nsectetur}{5: }|
+ {3:":lua"]:1: Lorem ipsum dolor sit am}|
+ {3:et, consectetur}{5: }|
{3:adipisicing elit, sed do eiusmod te}|
{3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
{3:a aliqua.}{5: }|
+ {3:Ut enim ad minim veniam, quis nostr}|
{6:-- More --}{5:^ }|
]]}
feed('k')
screen:expect{grid=[[
- {3:E5105: Error while calling lua chun}|
- {3:k: [string "<VimL compiled string>"}|
- {3:]:1: Lorem ipsum dolor sit amet, co}|
- {3:nsectetur}{5: }|
+ {3:E5108: Error executing lua [string }|
+ {3:":lua"]:1: Lorem ipsum dolor sit am}|
+ {3:et, consectetur}{5: }|
{3:adipisicing elit, sed do eiusmod te}|
{3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
+ {3:a aliqua.}{5: }|
{6:-- More --}{5:^ }|
]]}
feed('j')
screen:expect{grid=[[
- {3:k: [string "<VimL compiled string>"}|
- {3:]:1: Lorem ipsum dolor sit amet, co}|
- {3:nsectetur}{5: }|
+ {3:":lua"]:1: Lorem ipsum dolor sit am}|
+ {3:et, consectetur}{5: }|
{3:adipisicing elit, sed do eiusmod te}|
{3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
{3:a aliqua.}{5: }|
+ {3:Ut enim ad minim veniam, quis nostr}|
{6:-- More --}{5:^ }|
]]}
end)
@@ -1297,46 +1296,46 @@ aliquip ex ea commodo consequat.]])
command("hi MsgArea guisp=Yellow")
feed(':lua error(_G.x)<cr>')
screen:expect{grid=[[
- {3:E5105: Error while calling lua chun}|
- {3:k: [string "<VimL compiled string>"}|
- {3:]:1: Lorem ipsum dolor sit amet, co}|
- {3:nsectetur}{5: }|
+ {3:E5108: Error executing lua [string }|
+ {3:":lua"]:1: Lorem ipsum dolor sit am}|
+ {3:et, consectetur}{5: }|
{3:adipisicing elit, sed do eiusmod te}|
{3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
+ {3:a aliqua.}{5: }|
{6:-- More --}{5:^ }|
]]}
feed('d')
screen:expect{grid=[[
- {3:adipisicing elit, sed do eiusmod te}|
- {3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
{3:a aliqua.}{5: }|
{3:Ut enim ad minim veniam, quis nostr}|
{3:ud xercitation}{5: }|
{3:ullamco laboris nisi ut}{5: }|
- {6:-- More --}{5:^ }|
+ {3:aliquip ex ea commodo consequat.}{5: }|
+ {6:Press ENTER or type command to cont}|
+ {6:inue}{5:^ }|
]]}
feed('u')
screen:expect{grid=[[
- {3:E5105: Error while calling lua chun}|
- {3:k: [string "<VimL compiled string>"}|
- {3:]:1: Lorem ipsum dolor sit amet, co}|
- {3:nsectetur}{5: }|
+ {3:E5108: Error executing lua [string }|
+ {3:":lua"]:1: Lorem ipsum dolor sit am}|
+ {3:et, consectetur}{5: }|
{3:adipisicing elit, sed do eiusmod te}|
{3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
+ {3:a aliqua.}{5: }|
{6:-- More --}{5:^ }|
]]}
feed('d')
screen:expect{grid=[[
- {3:adipisicing elit, sed do eiusmod te}|
{3:mpor}{5: }|
{3:incididunt ut labore et dolore magn}|
{3:a aliqua.}{5: }|
{3:Ut enim ad minim veniam, quis nostr}|
{3:ud xercitation}{5: }|
{3:ullamco laboris nisi ut}{5: }|
+ {3:aliquip ex ea commodo consequat.}{5: }|
{6:-- More --}{5:^ }|
]]}
end)
@@ -1473,23 +1472,23 @@ aliquip ex ea commodo consequat.]])
it('can be resized', function()
feed(':lua error(_G.x)<cr>')
screen:expect{grid=[[
- {2:E5105: Error while calling lua chun}|
- {2:k: [string "<VimL compiled string>"}|
- {2:]:1: Lorem ipsum dolor sit amet, co}|
- {2:nsectetur} |
+ {2:E5108: Error executing lua [string }|
+ {2:":lua"]:1: Lorem ipsum dolor sit am}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusmod te}|
{2:mpor} |
{2:incididunt ut labore et dolore magn}|
+ {2:a aliqua.} |
{4:-- More --}^ |
]]}
-- responds to resize, but text is not reflown
screen:try_resize(45, 5)
screen:expect{grid=[[
- {2:nsectetur} |
{2:adipisicing elit, sed do eiusmod te} |
{2:mpor} |
{2:incididunt ut labore et dolore magn} |
+ {2:a aliqua.} |
{4:-- More --}^ |
]]}
@@ -1497,14 +1496,14 @@ aliquip ex ea commodo consequat.]])
-- text is not reflown; existing lines get cut
screen:try_resize(30, 12)
screen:expect{grid=[[
- {2:E5105: Error while calling lua}|
- {2:k: [string "<VimL compiled str}|
- {2:]:1: Lorem ipsum dolor sit ame}|
- {2:nsectetur} |
+ {2:E5108: Error executing lua [st}|
+ {2:":lua"]:1: Lorem ipsum dolor s}|
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusm}|
{2:mpore} |
{2:incididunt ut labore et dolore}|
- {2: magn} |
+ {2:a aliqua.} |
+ |
|
|
|
@@ -1515,18 +1514,18 @@ aliquip ex ea commodo consequat.]])
-- wrapped at the new screen size.
feed('<cr>')
screen:expect{grid=[[
- {2:k: [string "<VimL compiled str}|
- {2:]:1: Lorem ipsum dolor sit ame}|
- {2:nsectetur} |
+ {2:et, consectetur} |
{2:adipisicing elit, sed do eiusm}|
{2:mpore} |
{2:incididunt ut labore et dolore}|
- {2: magna aliqua.} |
+ {2:a aliqua.} |
{2:Ut enim ad minim veniam, quis }|
{2:nostrud xercitation} |
{2:ullamco laboris nisi ut} |
{2:aliquip ex ea commodo consequa}|
- {4:-- More --}^ |
+ {2:t.} |
+ {4:Press ENTER or type command to}|
+ {4: continue}^ |
]]}
feed('q')