diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2021-01-03 14:38:12 +0100 |
---|---|---|
committer | Björn Linse <bjorn.linse@gmail.com> | 2021-01-03 14:47:29 +0100 |
commit | adb01190c44b355da61469d39d0a51dbf84c4e3f (patch) | |
tree | 7c9a66e3220cc4c47a6bf64bd3ae52278872b8e1 /test/functional/treesitter/parser_spec.lua | |
parent | 7a81a0c073704505b420d02420f0c6b64f031983 (diff) | |
download | rneovim-adb01190c44b355da61469d39d0a51dbf84c4e3f.tar.gz rneovim-adb01190c44b355da61469d39d0a51dbf84c4e3f.tar.bz2 rneovim-adb01190c44b355da61469d39d0a51dbf84c4e3f.zip |
tests: make treesitter its own category
Diffstat (limited to 'test/functional/treesitter/parser_spec.lua')
-rw-r--r-- | test/functional/treesitter/parser_spec.lua | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua new file mode 100644 index 0000000000..520574c08a --- /dev/null +++ b/test/functional/treesitter/parser_spec.lua @@ -0,0 +1,576 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local insert = helpers.insert +local exec_lua = helpers.exec_lua +local feed = helpers.feed +local pending_c_parser = helpers.pending_c_parser + +before_each(clear) + +describe('treesitter parser API', function() + + it('parses buffer', function() + if helpers.pending_win32(pending) or pending_c_parser(pending) then return end + + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + root = tree:root() + lang = vim.treesitter.inspect_language('c') + ]]) + + eq("<tree>", exec_lua("return tostring(tree)")) + eq("<node translation_unit>", exec_lua("return tostring(root)")) + eq({0,0,3,0}, exec_lua("return {root:range()}")) + + eq(1, exec_lua("return root:child_count()")) + exec_lua("child = root:child(0)") + eq("<node function_definition>", exec_lua("return tostring(child)")) + eq({0,0,2,1}, exec_lua("return {child:range()}")) + + eq("function_definition", exec_lua("return child:type()")) + eq(true, exec_lua("return child:named()")) + eq("number", type(exec_lua("return child:symbol()"))) + eq({'function_definition', true}, exec_lua("return lang.symbols[child:symbol()]")) + + exec_lua("anon = root:descendant_for_range(0,8,0,9)") + eq("(", exec_lua("return anon:type()")) + eq(false, exec_lua("return anon:named()")) + eq("number", type(exec_lua("return anon:symbol()"))) + eq({'(', false}, exec_lua("return lang.symbols[anon:symbol()]")) + + exec_lua("descendant = root:descendant_for_range(1,2,1,12)") + eq("<node declaration>", exec_lua("return tostring(descendant)")) + eq({1,2,1,12}, exec_lua("return {descendant:range()}")) + eq("(declaration type: (primitive_type) declarator: (init_declarator declarator: (identifier) value: (number_literal)))", exec_lua("return descendant:sexpr()")) + + feed("2G7|ay") + exec_lua([[ + tree2 = parser:parse()[1] + root2 = tree2:root() + descendant2 = root2:descendant_for_range(1,2,1,13) + ]]) + eq(false, exec_lua("return tree2 == tree1")) + eq(false, exec_lua("return root2 == root")) + eq("<node declaration>", exec_lua("return tostring(descendant2)")) + eq({1,2,1,13}, exec_lua("return {descendant2:range()}")) + + eq(true, exec_lua("return child == child")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child == root:child(0)")) + eq(false, exec_lua("return child == descendant2")) + eq(false, exec_lua("return child == nil")) + eq(false, exec_lua("return child == tree")) + + eq("string", exec_lua("return type(child:id())")) + eq(true, exec_lua("return child:id() == child:id()")) + -- separate lua object, but represents same node + eq(true, exec_lua("return child:id() == root:child(0):id()")) + eq(false, exec_lua("return child:id() == descendant2:id()")) + eq(false, exec_lua("return child:id() == nil")) + eq(false, exec_lua("return child:id() == tree")) + + -- unchanged buffer: return the same tree + eq(true, exec_lua("return parser:parse()[1] == tree2")) + end) + + local test_text = [[ +void ui_refresh(void) +{ + 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); + } + } +}]] + + it('allows to iterate over nodes children', function() + if pending_c_parser(pending) then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse()[1]: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 pending_c_parser(pending) then return end + + insert(test_text); + + local res = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c") + + func_node = parser:parse()[1]: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 + (primitive_type) @type + (field_expression argument: (identifier) @fieldarg) + ]] + + it("supports runtime queries", function() + if pending_c_parser(pending) 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 pending_c_parser(pending) then return end + + insert(test_text) + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", ...) + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for cid, node in cquery:iter_captures(tree:root(), 0, 7, 14) do + -- can't transmit node over RPC. just check the name and range + table.insert(res, {cquery.captures[cid], node:type(), node:range()}) + end + return res + ]], query) + + eq({ + { "type", "primitive_type", 8, 2, 8, 6 }, + { "keyword", "for", 9, 2, 9, 5 }, + { "type", "primitive_type", 9, 7, 9, 13 }, + { "minfunc", "identifier", 11, 12, 11, 15 }, + { "fieldarg", "identifier", 11, 16, 11, 18 }, + { "min_id", "identifier", 11, 27, 11, 32 }, + { "minfunc", "identifier", 12, 13, 12, 16 }, + { "fieldarg", "identifier", 12, 17, 12, 19 }, + { "min_id", "identifier", 12, 29, 12, 35 }, + { "fieldarg", "identifier", 13, 14, 13, 16 } + }, res) + end) + + it('support query and iter by match', function() + if pending_c_parser(pending) then return end + + insert(test_text) + + local res = exec_lua([[ + cquery = vim.treesitter.parse_query("c", ...) + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14) 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 + ]], query) + + eq({ + { 3, { { "type", "primitive_type", 8, 2, 8, 6 } } }, + { 2, { { "keyword", "for", 9, 2, 9, 5 } } }, + { 3, { { "type", "primitive_type", 9, 7, 9, 13 } } }, + { 4, { { "fieldarg", "identifier", 11, 16, 11, 18 } } }, + { 1, { { "minfunc", "identifier", 11, 12, 11, 15 }, { "min_id", "identifier", 11, 27, 11, 32 } } }, + { 4, { { "fieldarg", "identifier", 12, 17, 12, 19 } } }, + { 1, { { "minfunc", "identifier", 12, 13, 12, 16 }, { "min_id", "identifier", 12, 29, 12, 35 } } }, + { 4, { { "fieldarg", "identifier", 13, 14, 13, 16 } } } + }, res) + end) + + it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() + if pending_c_parser(pending) 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()[1] + 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()[1]: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('allows to set simple ranges', function() + if pending_c_parser(pending) then return end + + insert(test_text) + + local res = exec_lua [[ + parser = vim.treesitter.get_parser(0, "c") + return { parser:parse()[1]: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 root = parser:parse()[1]:root() + parser:set_included_regions({{root:child(0)}}) + parser:invalidate() + return { parser:parse()[1]:root():range() } + ]] + + eq({0, 0, 18, 1}, res2) + + local range = exec_lua [[ + local res = {} + for _, region in ipairs(parser:included_regions()) do + for _, node in ipairs(region) do + table.insert(res, {node:range()}) + end + end + return res + ]] + + eq(range, { { 0, 0, 18, 1 } }) + + local range_tbl = exec_lua [[ + parser:set_included_regions { { { 0, 0, 17, 1 } } } + parser:parse() + return parser:included_regions() + ]] + + eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) + end) + it("allows to set complex ranges", function() + if pending_c_parser() then return end + + insert(test_text) + + local res = 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()[1]:root(), 0, 0, 19) do + table.insert(nodes, node) + end + + parser:set_included_regions({nodes}) + + local root = parser:parse()[1]:root() + + local res = {} + for i=0,(root:named_child_count() - 1) do + table.insert(res, { root:named_child(i):range() }) + end + return res + ]] + + eq({ + { 2, 2, 2, 40 }, + { 3, 2, 3, 32 }, + { 4, 7, 4, 25 }, + { 8, 2, 8, 33 }, + { 9, 7, 9, 20 }, + { 10, 4, 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()[1]: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()[1]:root(), str, 0, 2) do + table.insert(nodes, { node:range() }) + end + + return nodes]], txt) + + eq({ {0, 10, 0, 13} }, ret) + end) + + describe("when creating a language tree", function() + local function get_ranges() + return exec_lua([[ + local result = {} + parser:for_each_tree(function(tree) table.insert(result, {tree:root():range()}) end) + return result + ]]) + end + + before_each(function() + insert([[ +int x = INT_MAX; +#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) +#define READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) +#define VALUE 123 +#define VALUE1 123 +#define VALUE2 123 + ]]) + end) + + describe("when parsing regions independently", function() + it("should inject a language", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq(5, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 0, 7, 0}, -- root tree + {3, 14, 3, 17}, -- VALUE 123 + {4, 15, 4, 18}, -- VALUE1 123 + {5, 15, 5, 18}, -- VALUE2 123 + {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + + describe("when parsing regions combined", function() + it("should inject a language", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def (preproc_arg) @c @combined) (preproc_function_def value: (preproc_arg) @c @combined)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq(2, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 0, 7, 0}, -- root tree + {3, 14, 5, 18}, -- VALUE 123 + -- VALUE1 123 + -- VALUE2 123 + {1, 26, 2, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + + describe("when using the offset directive", function() + it("should shift the range by the directive amount", function() + exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { + c = "(preproc_def ((preproc_arg) @c (#offset! @c 0 2 0 -1))) (preproc_function_def value: (preproc_arg) @c)"}}) + ]]) + + eq("table", exec_lua("return type(parser:children().c)")) + eq({ + {0, 0, 7, 0}, -- root tree + {3, 15, 3, 16}, -- VALUE 123 + {4, 16, 4, 17}, -- VALUE1 123 + {5, 16, 5, 17}, -- VALUE2 123 + {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) + end) + end) + end) + + describe("when getting the language for a range", function() + before_each(function() + insert([[ +int x = INT_MAX; +#define VALUE 123456789 + ]]) + end) + + it("should return the correct language tree", function() + local result = exec_lua([[ + parser = vim.treesitter.get_parser(0, "c", { + queries = { c = "(preproc_def (preproc_arg) @c)"}}) + + local sub_tree = parser:language_for_range({1, 18, 1, 19}) + + return sub_tree == parser:children().c + ]]) + + eq(result, true) + end) + end) + + describe("when getting/setting match data", function() + describe("when setting for the whole match", function() + it("should set/get the data correctly", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local result + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! "key" "value"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, 1) do + result = metadata.key + end + + return result + ]]) + + eq(result, "value") + end) + end) + + describe("when setting for a capture match", function() + it("should set/get the data correctly", function() + insert([[ + int x = 3; + ]]) + + local result = exec_lua([[ + local result + + query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value"))') + parser = vim.treesitter.get_parser(0, "c") + + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, 1) do + result = metadata[pattern].key + end + + return result + ]]) + + eq(result, "value") + end) + end) + end) +end) |