diff options
Diffstat (limited to 'test/functional/lua/treesitter_spec.lua')
-rw-r--r-- | test/functional/lua/treesitter_spec.lua | 326 |
1 files changed, 295 insertions, 31 deletions
diff --git a/test/functional/lua/treesitter_spec.lua b/test/functional/lua/treesitter_spec.lua index aa3d55b06d..3526b64395 100644 --- a/test/functional/lua/treesitter_spec.lua +++ b/test/functional/lua/treesitter_spec.lua @@ -15,17 +15,16 @@ before_each(clear) describe('treesitter API', function() -- error tests not requiring a parser library it('handles missing language', function() - eq("Error executing lua: .../treesitter.lua: no parser for 'borklang' language", - pcall_err(exec_lua, "parser = vim.treesitter.create_parser(0, 'borklang')")) + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", + pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")) -- actual message depends on platform matches("Error executing lua: Failed to load parser: uv_dlopen: .+", pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')")) - eq("Error executing lua: .../treesitter.lua: no parser for 'borklang' language", + eq("Error executing lua: .../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')")) end) - end) describe('treesitter API with C parser', function() @@ -127,6 +126,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 @@ -134,6 +185,16 @@ void ui_refresh(void) (field_expression argument: (identifier) @fieldarg) ]] + it("supports runtime queries", function() + if not check_parser() then return end + + local ret = exec_lua [[ + return require"vim.treesitter.query".get_query("c", "highlights").captures[1] + ]] + + eq('variable', ret) + end) + it('support query and iter by capture', function() if not check_parser() then return end @@ -198,6 +259,82 @@ 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) { + return 0; + } + ]]) + + local custom_query = "((identifier) @main (#is-main? @main))" + + local res = exec_lua([[ + local query = require"vim.treesitter.query" + + local function is_main(match, pattern, bufnr, predicate) + local node = match[ predicate[2] ] + + return query.get_node_text(node, bufnr) + end + + local parser = vim.treesitter.get_parser(0, "c") + + query.add_predicate("is-main?", is_main) + + local query = query.parse_query("c", ...) + + local nodes = {} + for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do + table.insert(nodes, {node:range()}) + end + + return nodes + ]], 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() if not check_parser() then return end @@ -243,10 +380,10 @@ static int nlua_schedule(lua_State *const lstate) (primitive_type) @type (sized_type_specifier) @type -; defaults to very magic syntax, for best compatibility -((identifier) @Identifier (#match? @Identifier "^l(u)a_")) -; still support \M etc prefixes -((identifier) @Constant (#match? @Constant "\M^\[A-Z_]\+$")) +; Use lua regexes +((identifier) @Identifier (#contains? @Identifier "lua_")) +((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)) @@ -292,13 +429,14 @@ static int nlua_schedule(lua_State *const lstate) ]]} exec_lua([[ - local TSHighlighter = vim.treesitter.TSHighlighter + local parser = vim.treesitter.get_parser(0, "c") + local highlighter = vim.treesitter.highlighter local query = ... - test_hl = TSHighlighter.new(query, 0, "c") + test_hl = highlighter.new(parser, query) ]], hl_query) screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {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}) { | @@ -306,9 +444,9 @@ static int nlua_schedule(lua_State *const lstate) {4:return} {11:lua_error}(lstate); | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | ^} | @@ -317,10 +455,33 @@ static int nlua_schedule(lua_State *const lstate) | ]]} + feed("5Goc<esc>dd") + + 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} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {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}) { | @@ -329,9 +490,9 @@ static int nlua_schedule(lua_State *const lstate) {8:*^/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | } | @@ -342,7 +503,7 @@ static int nlua_schedule(lua_State *const lstate) feed('3Go/*<esc>') screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queue} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {2:/^*} | {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | @@ -352,9 +513,9 @@ static int nlua_schedule(lua_State *const lstate) {2:*/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | {8:}} | @@ -365,7 +526,7 @@ static int nlua_schedule(lua_State *const lstate) feed("~") screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queu^E} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {2:/*} | {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | @@ -375,9 +536,9 @@ static int nlua_schedule(lua_State *const lstate) {2:*/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | {8:}} | @@ -388,7 +549,7 @@ static int nlua_schedule(lua_State *const lstate) feed("re") screen:expect{grid=[[ {2:/// Schedule Lua callback on main loop's event queu^e} | - {3:static} {3:int} nlua_schedule({3:lua_State} *{3:const} lstate) | + {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | {2:/*} | {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} | @@ -398,9 +559,9 @@ static int nlua_schedule(lua_State *const lstate) {2:*/} | } | | - {7:LuaRef} cb = nlua_ref(lstate, {5:1}); | + {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + multiqueue_put(main_loop.events, {11:nlua_schedule_event}, | {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | {8:}} | @@ -408,6 +569,72 @@ static int nlua_schedule(lua_State *const lstate) ]]} end) + it("supports highlighting with custom parser", function() + if not check_parser() then return end + + local screen = Screen.new(65, 18) + screen:attach() + screen:set_default_attr_ids({ {bold = true, foreground = Screen.colors.SeaGreen4} }) + + insert(test_text) + + screen:expect{ grid= [[ + int width = INT_MAX, height = INT_MAX; | + bool ext_widgets[kUIExtCount]; | + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool inclusive = ui_override(); | + for (size_t i = 0; i < ui_count; i++) { | + UI *ui = uis[i]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + query = vim.treesitter.parse_query("c", "(declaration) @decl") + + local nodes = {} + for _, node in query:iter_captures(parser:parse():root(), 0, 0, 19) do + table.insert(nodes, node) + end + + parser:set_included_ranges(nodes) + + local hl = vim.treesitter.highlighter.new(parser, "(identifier) @type") + ]]) + + screen:expect{ grid = [[ + int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; | + bool {1:ext_widgets}[{1:kUIExtCount}]; | + for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { | + ext_widgets[i] = true; | + } | + | + bool {1:inclusive} = {1:ui_override}(); | + for (size_t {1:i} = 0; i < ui_count; i++) { | + UI *{1:ui} = {1:uis}[{1:i}]; | + width = MIN(ui->width, width); | + height = MIN(ui->height, height); | + foo = BAR(ui->bazaar, bazaar); | + for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { | + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | + } | + } | + ^} | + | + ]] } + end) + it('inspects language', function() if not check_parser() then return end @@ -453,23 +680,29 @@ static int nlua_schedule(lua_State *const lstate) insert(test_text) - local res = exec_lua([[ + local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") return { parser:parse():root():range() } - ]]) + ]] eq({0, 0, 19, 0}, res) -- The following sets the included ranges for the current parser -- As stated here, this only includes the function (thus the whole buffer, without the last line) - local res2 = exec_lua([[ + local res2 = exec_lua [[ local root = parser:parse():root() parser:set_included_ranges({root:child(0)}) parser.valid = false return { parser:parse():root():range() } - ]]) + ]] eq({0, 0, 18, 1}, res2) + + local range = exec_lua [[ + return parser:included_ranges() + ]] + + eq(range, { { 0, 0, 18, 1 } }) end) it("allows to set complex ranges", function() if not check_parser() then return end @@ -477,7 +710,7 @@ static int nlua_schedule(lua_State *const lstate) insert(test_text) - local res = exec_lua([[ + local res = exec_lua [[ parser = vim.treesitter.get_parser(0, "c") query = vim.treesitter.parse_query("c", "(declaration) @decl") @@ -495,7 +728,7 @@ static int nlua_schedule(lua_State *const lstate) table.insert(res, { root:named_child(i):range() }) end return res - ]]) + ]] eq({ { 2, 2, 2, 40 }, @@ -509,4 +742,35 @@ static int nlua_schedule(lua_State *const lstate) { 10, 5, 10, 20 }, { 14, 9, 14, 27 } }, res) end) + + it("allows to create string parsers", function() + local ret = exec_lua [[ + local parser = vim.treesitter.get_string_parser("int foo = 42;", "c") + return { parser:parse():root():range() } + ]] + + eq({ 0, 0, 0, 13 }, ret) + end) + + it("allows to run queries with string parsers", function() + local txt = [[ + int foo = 42; + int bar = 13; + ]] + + local ret = exec_lua([[ + local str = ... + local parser = vim.treesitter.get_string_parser(str, "c") + + local nodes = {} + local query = vim.treesitter.parse_query("c", '((identifier) @id (eq? @id "foo"))') + + for _, node in query:iter_captures(parser:parse():root(), str, 0, 2) do + table.insert(nodes, { node:range() }) + end + + return nodes]], txt) + + eq({ {0, 10, 0, 13} }, ret) + end) end) |