diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /test/functional/treesitter/parser_spec.lua | |
parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
download | rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.gz rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.bz2 rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.zip |
Diffstat (limited to 'test/functional/treesitter/parser_spec.lua')
-rw-r--r-- | test/functional/treesitter/parser_spec.lua | 347 |
1 files changed, 321 insertions, 26 deletions
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 2f8d204d36..eb4651a81d 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -1,5 +1,6 @@ local t = require('test.testutil') local n = require('test.functional.testnvim')() +local ts_t = require('test.functional.treesitter.testutil') local clear = n.clear local dedent = t.dedent @@ -8,6 +9,8 @@ local insert = n.insert local exec_lua = n.exec_lua local pcall_err = t.pcall_err local feed = n.feed +local run_query = ts_t.run_query +local assert_alive = n.assert_alive describe('treesitter parser API', function() before_each(function() @@ -88,6 +91,197 @@ describe('treesitter parser API', function() eq(true, exec_lua('return parser:parse()[1] == tree2')) end) + it('parses buffer asynchronously', function() + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua(function() + _G.parser = vim.treesitter.get_parser(0, 'c') + _G.lang = vim.treesitter.language.inspect('c') + _G.parser:parse(nil, function(_, trees) + _G.tree = trees[1] + _G.root = _G.tree:root() + end) + vim.wait(100, function() end) + end) + + 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(true, exec_lua('return lang.symbols[child:type()]')) + + 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[string.format('"%s"', anon:type())]]=])) + + 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(function() + _G.parser:parse(nil, function(_, trees) + _G.tree2 = trees[1] + _G.root2 = _G.tree2:root() + _G.descendant2 = _G.root2:descendant_for_range(1, 2, 1, 13) + end) + vim.wait(100, function() end) + end) + 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) + + it('does not crash when editing large files', function() + insert([[printf("%s", "some text");]]) + feed('yy49999p') + + exec_lua(function() + _G.parser = vim.treesitter.get_parser(0, 'c') + _G.done = false + vim.treesitter.start(0, 'c') + _G.parser:parse(nil, function() + _G.done = true + end) + while not _G.done do + -- Busy wait until async parsing has completed + vim.wait(100, function() end) + end + end) + + eq(true, exec_lua([[return done]])) + exec_lua(function() + vim.api.nvim_input('Lxj') + end) + exec_lua(function() + vim.api.nvim_input('xj') + end) + exec_lua(function() + vim.api.nvim_input('xj') + end) + assert_alive() + end) + + it('resets parsing state on tree changes', function() + insert([[vim.api.nvim_set_hl(0, 'test2', { bg = 'green' })]]) + feed('yy1000p') + + exec_lua(function() + vim.cmd('set ft=lua') + + vim.treesitter.start(0) + local parser = assert(vim.treesitter.get_parser(0)) + + parser:parse(true, function() end) + vim.api.nvim_buf_set_lines(0, 1, -1, false, {}) + parser:parse(true) + end) + end) + + it('resets when buffer was editing during an async parse', function() + insert([[printf("%s", "some text");]]) + feed('yy49999p') + feed('gg4jO// Comment<Esc>') + + exec_lua(function() + _G.parser = vim.treesitter.get_parser(0, 'c') + _G.done = false + vim.treesitter.start(0, 'c') + _G.parser:parse(nil, function() + _G.done = true + end) + end) + + exec_lua(function() + vim.api.nvim_input('ggdj') + end) + + eq(false, exec_lua([[return done]])) + exec_lua(function() + while not _G.done do + -- Busy wait until async parsing finishes + vim.wait(100, function() end) + end + end) + eq(true, exec_lua([[return done]])) + eq('comment', exec_lua([[return parser:parse()[1]:root():named_child(2):type()]])) + eq({ 2, 0, 2, 10 }, exec_lua([[return {parser:parse()[1]:root():named_child(2):range()}]])) + end) + + it('handles multiple async parse calls', function() + insert([[printf("%s", "some text");]]) + feed('yy49999p') + + exec_lua(function() + -- Spy on vim.schedule + local schedule = vim.schedule + vim.schedule = function(fn) + _G.schedules = _G.schedules + 1 + schedule(fn) + end + _G.schedules = 0 + _G.parser = vim.treesitter.get_parser(0, 'c') + for i = 1, 5 do + _G['done' .. i] = false + _G.parser:parse(nil, function() + _G['done' .. i] = true + end) + end + schedule(function() + _G.schedules_snapshot = _G.schedules + end) + end) + + eq(2, exec_lua([[return schedules_snapshot]])) + eq( + { false, false, false, false, false }, + exec_lua([[return { done1, done2, done3, done4, done5 }]]) + ) + exec_lua(function() + while not _G.done1 do + -- Busy wait until async parsing finishes + vim.wait(100, function() end) + end + end) + eq({ true, true, true, true, true }, exec_lua([[return { done1, done2, done3, done4, done5 }]])) + end) + local test_text = [[ void ui_refresh(void) { @@ -310,6 +504,15 @@ end]] eq({ 0, 0, 0, 13 }, ret) end) + it('can run async parses with string parsers', function() + local ret = exec_lua(function() + local parser = vim.treesitter.get_string_parser('int foo = 42;', 'c') + return { parser:parse(nil, function() end)[1]:root():range() } + end) + + eq({ 0, 0, 0, 13 }, ret) + end) + it('allows to run queries with string parsers', function() local txt = [[ int foo = 42; @@ -430,7 +633,7 @@ int x = INT_MAX; }, get_ranges()) n.feed('7ggI//<esc>') - exec_lua([[parser:parse({6, 7})]]) + exec_lua([[parser:parse({5, 6})]]) eq('table', exec_lua('return type(parser:children().c)')) eq(2, exec_lua('return #parser:children().c:trees()')) eq({ @@ -644,6 +847,109 @@ print() end) end) + describe('trim! directive', function() + it('can trim all whitespace', function() + -- luacheck: push ignore 611 613 + insert([=[ + print([[ + + f + helllo + there + asdf + asdfassd + + + + ]]) + print([[ + + + + ]]) + + print([[]]) + + print([[ + ]]) + + print([[ hello 😃 ]]) + ]=]) + -- luacheck: pop + + local query_text = [[ + ; query + ((string_content) @str + (#trim! @str 1 1 1 1)) + ]] + + exec_lua(function() + vim.treesitter.start(0, 'lua') + end) + + eq({ + { 'str', { 2, 12, 6, 10 } }, + { 'str', { 11, 10, 11, 10 } }, + { 'str', { 17, 10, 17, 10 } }, + { 'str', { 19, 10, 19, 10 } }, + { 'str', { 22, 15, 22, 25 } }, + }, run_query('lua', query_text)) + end) + + it('trims only empty lines by default (backwards compatible)', function() + insert(dedent [[ + ## Heading + + With some text + + ## And another + + With some more here]]) + + local query_text = [[ + ; query + ((section) @fold + (#trim! @fold)) + ]] + + exec_lua(function() + vim.treesitter.start(0, 'markdown') + end) + + eq({ + { 'fold', { 0, 0, 2, 14 } }, + { 'fold', { 4, 0, 6, 19 } }, + }, run_query('markdown', query_text)) + end) + + it('can trim lines', function() + insert(dedent [[ + - Fold list + - Fold list + - Fold list + - Fold list + - Fold list + - Fold list + ]]) + + local query_text = [[ + ; query + ((list_item + (list)) @fold + (#trim! @fold 1 1 1 1)) + ]] + + exec_lua(function() + vim.treesitter.start(0, 'markdown') + end) + + eq({ + { 'fold', { 0, 0, 4, 13 } }, + { 'fold', { 1, 2, 3, 15 } }, + }, run_query('markdown', query_text)) + end) + end) + it('tracks the root range properly (#22911)', function() insert([[ int main() { @@ -659,32 +965,19 @@ print() vim.treesitter.start(0, 'c') end) - local function run_query() - return exec_lua(function() - local query = vim.treesitter.query.parse('c', query0) - local parser = vim.treesitter.get_parser() - local tree = parser:parse()[1] - local res = {} - for id, node in query:iter_captures(tree:root()) do - table.insert(res, { query.captures[id], node:range() }) - end - return res - end) - end - eq({ - { 'function', 0, 0, 2, 1 }, - { 'declaration', 1, 2, 1, 12 }, - }, run_query()) + { 'function', { 0, 0, 2, 1 } }, + { 'declaration', { 1, 2, 1, 12 } }, + }, run_query('c', query0)) n.command 'normal ggO' insert('int a;') eq({ - { 'declaration', 0, 0, 0, 6 }, - { 'function', 1, 0, 3, 1 }, - { 'declaration', 2, 2, 2, 12 }, - }, run_query()) + { 'declaration', { 0, 0, 0, 6 } }, + { 'function', { 1, 0, 3, 1 } }, + { 'declaration', { 2, 2, 2, 12 } }, + }, run_query('c', query0)) end) it('handles ranges when source is a multiline string (#20419)', function() @@ -858,11 +1151,13 @@ print() feed(':set ft=help<cr>') exec_lua(function() - vim.treesitter.get_parser(0, 'vimdoc', { - injections = { - vimdoc = '((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))', - }, - }) + vim.treesitter + .get_parser(0, 'vimdoc', { + injections = { + vimdoc = '((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))', + }, + }) + :parse() end) end) |