aboutsummaryrefslogtreecommitdiff
path: root/test/functional/lua/treesitter_spec.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/lua/treesitter_spec.lua')
-rw-r--r--test/functional/lua/treesitter_spec.lua326
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)