diff options
Diffstat (limited to 'test/functional/lua')
-rw-r--r-- | test/functional/lua/buffer_updates_spec.lua | 195 | ||||
-rw-r--r-- | test/functional/lua/treesitter_spec.lua | 123 |
2 files changed, 293 insertions, 25 deletions
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 77f8189bb9..5be47070a7 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -1,12 +1,16 @@ -- Test suite for testing interactions with API bindings local helpers = require('test.functional.helpers')(after_each) +local inspect = require'vim.inspect' + local command = helpers.command local meths = helpers.meths local clear = helpers.clear local eq = helpers.eq +local fail = helpers.fail local exec_lua = helpers.exec_lua local feed = helpers.feed +local deepcopy = helpers.deepcopy local origlines = {"original line 1", "original line 2", @@ -16,32 +20,37 @@ local origlines = {"original line 1", "original line 6", " indented line"} -describe('lua: buffer event callbacks', function() - before_each(function() - clear() - exec_lua([[ - local events = {} +local function attach_buffer(evname) + exec_lua([[ + local evname = ... + local events = {} - function test_register(bufnr, id, changedtick, utf_sizes) - local function callback(...) - table.insert(events, {id, ...}) - if test_unreg == id then - return true - end - end - local opts = {on_lines=callback, on_detach=callback, utf_sizes=utf_sizes} - if changedtick then - opts.on_changedtick = callback + function test_register(bufnr, id, changedtick, utf_sizes) + local function callback(...) + table.insert(events, {id, ...}) + if test_unreg == id then + return true end - vim.api.nvim_buf_attach(bufnr, false, opts) end - - function get_events() - local ret_events = events - events = {} - return ret_events + local opts = {[evname]=callback, on_detach=callback, utf_sizes=utf_sizes} + if changedtick then + opts.on_changedtick = callback end - ]]) + vim.api.nvim_buf_attach(bufnr, false, opts) + end + + function get_events() + local ret_events = events + events = {} + return ret_events + end + ]], evname) +end + +describe('lua buffer event callbacks: on_lines', function() + before_each(function() + clear() + attach_buffer('on_lines') end) @@ -62,7 +71,7 @@ describe('lua: buffer event callbacks', function() local function check_events(expected) local events = exec_lua("return get_events(...)" ) if utf_sizes then - -- this test case uses ASCII only, so sizes sshould be the same. + -- this test case uses ASCII only, so sizes should be the same. -- Unicode is tested below. for _, event in ipairs(expected) do event[9] = event[8] @@ -216,4 +225,144 @@ describe('lua: buffer event callbacks', function() eq(1, meths.get_var('listener_cursor_line')) end) + it('does not SEGFAULT when calling win_findbuf in on_detach', function() + + exec_lua[[ + local buf = vim.api.nvim_create_buf(false, false) + + vim.cmd"split" + vim.api.nvim_win_set_buf(0, buf) + + vim.api.nvim_buf_attach(buf, false, { + on_detach = function(_, buf) + vim.fn.win_findbuf(buf) + end + }) + ]] + + command("q!") + helpers.assert_alive() + end) + +end) + +describe('lua: nvim_buf_attach on_bytes', function() + before_each(function() + clear() + attach_buffer('on_bytes') + end) + + -- verifying the sizes with nvim_buf_get_offset is nice (checks we cannot + -- assert the wrong thing), but masks errors with unflushed lines (as + -- nvim_buf_get_offset forces a flush of the memline). To be safe run the + -- test both ways. + local function setup_eventcheck(verify) + meths.buf_set_lines(0, 0, -1, true, origlines) + local shadow = deepcopy(origlines) + local shadowbytes = table.concat(shadow, '\n') .. '\n' + -- TODO: while we are brewing the real strong coffe, + -- verify should check buf_get_offset after every check_events + if verify then + meths.buf_get_offset(0, meths.buf_line_count(0)) + end + exec_lua("return test_register(...)", 0, "test1",false, nil) + meths.buf_get_changedtick(0) + + local verify_name = "test1" + local function check_events(expected) + local events = exec_lua("return get_events(...)" ) + + if not pcall(eq, expected, events) then + local msg = 'unexpected byte updates received.\n\nBABBLA MER \n\n' + + msg = msg .. 'received events:\n' + for _, e in ipairs(events) do + msg = msg .. ' ' .. inspect(e) .. ';\n' + end + msg = msg .. '\nexpected events:\n' + for _, e in ipairs(expected) do + msg = msg .. ' ' .. inspect(e) .. ';\n' + end + fail(msg) + end + + if verify then + for _, event in ipairs(events) do + if event[1] == verify_name and event[2] == "bytes" then + local _, _, _, _, _, _, start_byte, _, _, old_byte, _, _, new_byte = unpack(event) + local before = string.sub(shadowbytes, 1, start_byte) + -- no text in the tests will contain 0xff bytes (invalid UTF-8) + -- so we can use it as marker for unknown bytes + local unknown = string.rep('\255', new_byte) + local after = string.sub(shadowbytes, start_byte + old_byte + 1) + shadowbytes = before .. unknown .. after + end + end + local text = meths.buf_get_lines(0, 0, -1, true) + local bytes = table.concat(text, '\n') .. '\n' + eq(string.len(bytes), string.len(shadowbytes), shadowbytes) + for i = 1, string.len(shadowbytes) do + local shadowbyte = string.sub(shadowbytes, i, i) + if shadowbyte ~= '\255' then + eq(string.sub(bytes, i, i), shadowbyte, i) + end + end + end + end + + return check_events + end + + -- Yes, we can do both + local function do_both(verify) + it('single and multiple join', function() + local check_events = setup_eventcheck(verify) + feed 'ggJ' + check_events { + {'test1', 'bytes', 1, 3, 0, 15, 15, 1, 0, 1, 0, 1, 1}; + } + + feed '3J' + check_events { + {'test1', 'bytes', 1, 5, 0, 31, 31, 1, 0, 1, 0, 1, 1}; + {'test1', 'bytes', 1, 5, 0, 47, 47, 1, 0, 1, 0, 1, 1}; + } + end) + + it('opening lines', function() + local check_events = setup_eventcheck(verify) + -- meths.buf_set_option(0, 'autoindent', true) + feed 'Go' + check_events { + { "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 1 }; + } + feed '<cr>' + check_events { + { "test1", "bytes", 1, 5, 7, 0, 114, 0, 0, 0, 1, 0, 1 }; + } + end) + + it('opening lines with autoindent', function() + local check_events = setup_eventcheck(verify) + meths.buf_set_option(0, 'autoindent', true) + feed 'Go' + check_events { + { "test1", "bytes", 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 5 }; + } + feed '<cr>' + check_events { + { "test1", "bytes", 1, 4, 8, 0, 115, 0, 4, 4, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 7, 4, 118, 0, 0, 0, 1, 4, 5 }; + } + end) + end + + describe('(with verify) handles', function() + do_both(true) + end) + + describe('(without verify) handles', function() + do_both(false) + end) end) + diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua index b0ac9e079a..2c9107a65a 100644 --- a/test/functional/lua/treesitter_spec.lua +++ b/test/functional/lua/treesitter_spec.lua @@ -127,6 +127,58 @@ void ui_refresh(void) } }]] + it('allows to iterate over nodes children', function() + if not check_parser() then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse():root():child(0) + + res = {} + for node, field in func_node:iter_children() do + table.insert(res, {node:type(), field}) + end + return res + ]]) + + eq({ + {"primitive_type", "type"}, + {"function_declarator", "declarator"}, + {"compound_statement", "body"} + }, res) + end) + + it('allows to get a child by field', function() + if not check_parser() then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse():root():child(0) + + local res = {} + for _, node in ipairs(func_node:field("type")) do + table.insert(res, {node:type(), node:range()}) + end + return res + ]]) + + eq({{ "primitive_type", 0, 0, 0, 4 }}, res) + + local res_fail = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + return #func_node:field("foo") == 0 + ]]) + + assert(res_fail) + end) + local query = [[ ((call_expression function: (identifier) @minfunc (argument_list (identifier) @min_id)) (eq? @minfunc "MIN")) "for" @keyword @@ -198,6 +250,35 @@ void ui_refresh(void) }, res) end) + it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() + if not check_parser() then return end + + insert('char* astring = "Hello World!";') + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", '((_) @quote (vim-match? @quote "^\\"$")) ((_) @quote (lua-match? @quote "^\\"$"))') + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse() + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0, 0, 1) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid,node in pairs(match) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end + table.insert(res, {pattern, mrepr}) + end + return res + ]]) + + eq({ + { 1, { { "quote", '"', 0, 16, 0, 17 } } }, + { 2, { { "quote", '"', 0, 16, 0, 17 } } }, + { 1, { { "quote", '"', 0, 29, 0, 30 } } }, + { 2, { { "quote", '"', 0, 29, 0, 30 } } }, + }, res) + end) + it('allows to add predicates', function() insert([[ int main(void) { @@ -231,6 +312,18 @@ void ui_refresh(void) ]], custom_query) eq({{0, 4, 0, 8}}, res) + + local res_list = exec_lua[[ + local query = require'vim.treesitter.query' + + local list = query.list_predicates() + + table.sort(list) + + return list + ]] + + eq({ 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) end) it('supports highlighting', function() @@ -280,7 +373,7 @@ static int nlua_schedule(lua_State *const lstate) ; Use lua regexes ((identifier) @Identifier (#contains? @Identifier "lua_")) -((identifier) @Constant (#match? @Constant "^[A-Z_]+$")) +((identifier) @Constant (#lua-match? @Constant "^[A-Z_]+$")) ((identifier) @Normal (#vim-match? @Constant "^lstate$")) ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (#eq? @WarningMsg.left @WarningMsg.right)) @@ -352,6 +445,32 @@ static int nlua_schedule(lua_State *const lstate) | ]]} + feed("5Goc<esc>dd") + if true == true then + pending('reenable this check in luahl PR') + return + end + screen:expect{grid=[[ + {2:/// Schedule Lua callback on main loop's event queue} | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | + { | + {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} | + || {6:lstate} != {6:lstate}) { | + {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | + {4:return} {11:lua_error}(lstate); | + } | + | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | + | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | + {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | + {4:return} {5:0}; | + } | + {1:~ }| + {1:~ }| + | + ]]} + feed('7Go*/<esc>') screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | @@ -361,7 +480,7 @@ static int nlua_schedule(lua_State *const lstate) || {6:lstate} != {6:lstate}) { | {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); | {4:return} {11:lua_error}(lstate); | - {8:*^/} | + *^/ | } | | {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | |