diff options
Diffstat (limited to 'test/functional/treesitter')
-rw-r--r-- | test/functional/treesitter/fold_spec.lua | 553 | ||||
-rw-r--r-- | test/functional/treesitter/highlight_spec.lua | 475 | ||||
-rw-r--r-- | test/functional/treesitter/inspect_tree_spec.lua | 115 | ||||
-rw-r--r-- | test/functional/treesitter/language_spec.lua | 88 | ||||
-rw-r--r-- | test/functional/treesitter/node_spec.lua | 19 | ||||
-rw-r--r-- | test/functional/treesitter/parser_spec.lua | 961 | ||||
-rw-r--r-- | test/functional/treesitter/utils_spec.lua | 1 |
7 files changed, 1456 insertions, 756 deletions
diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua index a8abbc002b..9428432f66 100644 --- a/test/functional/treesitter/fold_spec.lua +++ b/test/functional/treesitter/fold_spec.lua @@ -5,6 +5,7 @@ local insert = helpers.insert local exec_lua = helpers.exec_lua local command = helpers.command local feed = helpers.feed +local poke_eventloop = helpers.poke_eventloop local Screen = require('test.functional.ui.screen') before_each(clear) @@ -12,6 +13,11 @@ before_each(clear) describe('treesitter foldexpr', function() clear() + before_each(function() + -- open folds to avoid deleting entire folded region + exec_lua([[vim.opt.foldlevel = 9]]) + end) + local test_text = [[ void ui_refresh(void) { @@ -33,6 +39,12 @@ void ui_refresh(void) } }]] + local function parse(lang) + exec_lua( + ([[vim.treesitter.get_parser(0, %s):parse()]]):format(lang and '"' .. lang .. '"' or 'nil') + ) + end + local function get_fold_levels() return exec_lua([[ local res = {} @@ -43,10 +55,10 @@ void ui_refresh(void) ]]) end - it("can compute fold levels", function() + it('can compute fold levels', function() insert(test_text) - exec_lua([[vim.treesitter.get_parser(0, "c")]]) + parse('c') eq({ [1] = '>1', @@ -67,16 +79,17 @@ void ui_refresh(void) [16] = '3', [17] = '3', [18] = '2', - [19] = '1' }, get_fold_levels()) - + [19] = '1', + }, get_fold_levels()) end) - it("recomputes fold levels after lines are added/removed", function() + it('recomputes fold levels after lines are added/removed', function() insert(test_text) - exec_lua([[vim.treesitter.get_parser(0, "c")]]) + parse('c') command('1,2d') + poke_eventloop() eq({ [1] = '0', @@ -95,9 +108,11 @@ void ui_refresh(void) [14] = '2', [15] = '2', [16] = '1', - [17] = '0' }, get_fold_levels()) + [17] = '0', + }, get_fold_levels()) command('1put!') + poke_eventloop() eq({ [1] = '>1', @@ -118,26 +133,295 @@ void ui_refresh(void) [16] = '3', [17] = '3', [18] = '2', - [19] = '1' }, get_fold_levels()) + [19] = '1', + }, get_fold_levels()) + end) + + it('handles changes close to start/end of folds', function() + insert([[ +# h1 +t1 +# h2 +t2]]) + + exec_lua([[vim.treesitter.query.set('markdown', 'folds', '(section) @fold')]]) + parse('markdown') + + eq({ + [1] = '>1', + [2] = '1', + [3] = '>1', + [4] = '1', + }, get_fold_levels()) + + feed('2ggo<Esc>') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + [4] = '>1', + [5] = '1', + }, get_fold_levels()) + + feed('dd') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '>1', + [4] = '1', + }, get_fold_levels()) + + feed('2ggdd') + poke_eventloop() + + eq({ + [1] = '0', + [2] = '>1', + [3] = '1', + }, get_fold_levels()) + + feed('u') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '>1', + [4] = '1', + }, get_fold_levels()) + + feed('3ggdd') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + }, get_fold_levels()) + + feed('u') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '>1', + [4] = '1', + }, get_fold_levels()) + + feed('3ggI#<Esc>') + parse() + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '>2', + [4] = '2', + }, get_fold_levels()) + + feed('x') + parse() + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '>1', + [4] = '1', + }, get_fold_levels()) end) - it("updates folds in all windows", function() + it('handles changes that trigger multiple on_bytes', function() + insert([[ +function f() + asdf() + asdf() +end +-- comment]]) + + exec_lua( + [[vim.treesitter.query.set('lua', 'folds', '[(function_declaration) (parameters) (arguments)] @fold')]] + ) + parse('lua') + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + [4] = '1', + [5] = '0', + }, get_fold_levels()) + + command('1,4join') + poke_eventloop() + + eq({ + [1] = '0', + [2] = '0', + }, get_fold_levels()) + + feed('u') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + [4] = '1', + [5] = '0', + }, get_fold_levels()) + end) + + it('handles multiple folds that overlap at the end and start', function() + insert([[ +function f() + g( + function() + asdf() + end, function() + end + ) +end]]) + + exec_lua( + [[vim.treesitter.query.set('lua', 'folds', '[(function_declaration) (function_definition) (parameters) (arguments)] @fold')]] + ) + parse('lua') + + -- If fold1.stop = fold2.start, then move fold1's stop up so that fold2.start gets proper level. + eq({ + [1] = '>1', + [2] = '>2', + [3] = '>3', + [4] = '3', + [5] = '>3', + [6] = '3', + [7] = '2', + [8] = '1', + }, get_fold_levels()) + + command('1,8join') + feed('u') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '>2', + [3] = '>3', + [4] = '3', + [5] = '>3', + [6] = '3', + [7] = '2', + [8] = '1', + }, get_fold_levels()) + end) + + it('handles multiple folds that start at the same line', function() + insert([[ +function f(a) + if #(g({ + k = v, + })) > 0 then + return + end +end]]) + + exec_lua( + [[vim.treesitter.query.set('lua', 'folds', '[(if_statement) (function_declaration) (parameters) (arguments) (table_constructor)] @fold')]] + ) + parse('lua') + + eq({ + [1] = '>1', + [2] = '>3', + [3] = '3', + [4] = '3', + [5] = '2', + [6] = '2', + [7] = '1', + }, get_fold_levels()) + + command('2,6join') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + }, get_fold_levels()) + + feed('u') + poke_eventloop() + + eq({ + [1] = '>1', + [2] = '>3', + [3] = '3', + [4] = '3', + [5] = '2', + [6] = '2', + [7] = '1', + }, get_fold_levels()) + end) + + it('takes account of relevant options', function() + insert([[ +# h1 +t1 +## h2 +t2 +### h3 +t3]]) + + exec_lua([[vim.treesitter.query.set('markdown', 'folds', '(section) @fold')]]) + parse('markdown') + + command([[set foldminlines=2]]) + + eq({ + [1] = '>1', + [2] = '1', + [3] = '>2', + [4] = '2', + [5] = '2', + [6] = '2', + }, get_fold_levels()) + + command([[set foldminlines=1 foldnestmax=1]]) + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + [4] = '1', + [5] = '1', + [6] = '1', + }, get_fold_levels()) + end) + + it('updates folds in all windows', function() local screen = Screen.new(60, 48) screen:attach() screen:set_default_attr_ids({ - [1] = {background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue}; - [2] = {bold = true, foreground = Screen.colors.Blue1}; - [3] = {bold = true, reverse = true}; - [4] = {reverse = true}; + [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue }, + [2] = { bold = true, foreground = Screen.colors.Blue1 }, + [3] = { bold = true, reverse = true }, + [4] = { reverse = true }, }) - exec_lua([[vim.treesitter.get_parser(0, "c")]]) - command([[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=9]]) + parse('c') + command([[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1]]) command('split') insert(test_text) - screen:expect{grid=[[ + screen:expect { + grid = [[ {1:-}void ui_refresh(void) | {1:│}{ | {1:│} int width = INT_MAX, height = INT_MAX; | @@ -157,10 +441,7 @@ void ui_refresh(void) {1:3} } | {1:2} } | {1:│}^} | - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| + {2:~ }|*4 {3:[No Name] [+] }| {1:-}void ui_refresh(void) | {1:│}{ | @@ -181,16 +462,16 @@ void ui_refresh(void) {1:3} } | {1:2} } | {1:│}} | - {2:~ }| - {2:~ }| - {2:~ }| + {2:~ }|*3 {4:[No Name] [+] }| | - ]]} + ]], + } command('1,2d') - screen:expect{grid=[[ + screen:expect { + grid = [[ {1: } ^int width = INT_MAX, height = INT_MAX; | {1: } bool ext_widgets[kUIExtCount]; | {1:-} for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | @@ -208,12 +489,7 @@ void ui_refresh(void) {1:2} } | {1:│} } | {1: }} | - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| + {2:~ }|*6 {3:[No Name] [+] }| {1: } int width = INT_MAX, height = INT_MAX; | {1: } bool ext_widgets[kUIExtCount]; | @@ -232,19 +508,16 @@ void ui_refresh(void) {1:2} } | {1:│} } | {1: }} | - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| + {2:~ }|*5 {4:[No Name] [+] }| | - ]]} - + ]], + } feed([[O<C-u><C-r>"<BS><Esc>]]) - screen:expect{grid=[[ + screen:expect { + grid = [[ {1:-}void ui_refresh(void) | {1:│}^{ | {1:│} int width = INT_MAX, height = INT_MAX; | @@ -264,10 +537,7 @@ void ui_refresh(void) {1:3} } | {1:2} } | {1:│}} | - {2:~ }| - {2:~ }| - {2:~ }| - {2:~ }| + {2:~ }|*4 {3:[No Name] [+] }| {1:-}void ui_refresh(void) | {1:│}{ | @@ -288,21 +558,21 @@ void ui_refresh(void) {1:3} } | {1:2} } | {1:│}} | - {2:~ }| - {2:~ }| - {2:~ }| + {2:~ }|*3 {4:[No Name] [+] }| | - ]]} - + ]], + } end) it("doesn't open folds in diff mode", function() local screen = Screen.new(60, 36) screen:attach() - exec_lua([[vim.treesitter.get_parser(0, "c")]]) - command([[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=9]]) + parse('c') + command( + [[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=9]] + ) insert(test_text) command('16d') @@ -312,7 +582,8 @@ void ui_refresh(void) command('windo diffthis') feed('do') - screen:expect{grid=[[ + screen:expect { + grid = [[ {1:+ }{2:+-- 9 lines: void ui_refresh(void)·······················}| {1: } for (size_t i = 0; i < ui_count; i++) { | {1: } UI *ui = uis[i]; | @@ -324,12 +595,7 @@ void ui_refresh(void) {1: } } | {1: } } | {1: }} | - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| + {3:~ }|*6 {4:[No Name] [+] }| {1:+ }{2:+-- 9 lines: void ui_refresh(void)·······················}| {1: } for (size_t i = 0; i < ui_count; i++) { | @@ -342,133 +608,70 @@ void ui_refresh(void) {1: } ^} | {1: } } | {1: }} | - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| - {3:~ }| + {3:~ }|*5 {5:[No Name] [+] }| | - ]], attr_ids={ - [1] = {background = Screen.colors.Grey, foreground = Screen.colors.Blue4}; - [2] = {background = Screen.colors.LightGrey, foreground = Screen.colors.Blue4}; - [3] = {foreground = Screen.colors.Blue, bold = true}; - [4] = {reverse = true}; - [5] = {reverse = true, bold = true}; - }} - end) - -end) - -describe('treesitter foldtext', function() - local test_text = [[ -void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const 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); + ]], + attr_ids = { + [1] = { background = Screen.colors.Grey, foreground = Screen.colors.Blue4 }, + [2] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Blue4 }, + [3] = { foreground = Screen.colors.Blue, bold = true }, + [4] = { reverse = true }, + [5] = { reverse = true, bold = true }, + }, } - } -}]] - local screen + end) - before_each(function() - screen = Screen.new(60, 5) + it("doesn't open folds that are not touched", function() + local screen = Screen.new(40, 8) screen:set_default_attr_ids({ - [0] = {foreground = Screen.colors.Blue, bold = true}, - [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray}; - [2] = {bold = true, background = Screen.colors.LightGray, foreground = Screen.colors.SeaGreen}; - [3] = {foreground = Screen.colors.DarkCyan, background = Screen.colors.LightGray}; - [4] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightGray}; - [5] = {bold = true, background = Screen.colors.LightGray, foreground = Screen.colors.Brown}; - [6] = {background = Screen.colors.Red1}; - [7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Red}; - [8] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red}; - [9] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.Red}; - [10] = {bold = true}; + [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray }, + [2] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray }, + [3] = { foreground = Screen.colors.Blue1, bold = true }, + [4] = { bold = true }, }) screen:attach() - end) - it('displays highlighted content', function() - command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext() updatetime=50]]) - insert(test_text) - exec_lua([[vim.treesitter.get_parser(0, "c")]]) - - feed('ggVGzf') - screen:expect{grid=[[ - {2:^void}{1: }{3:qsort}{4:(}{2:void}{1: }{5:*}{3:base}{4:,}{1: }{2:size_t}{1: }{3:nel}{4:,}{1: }{2:size_t}{1: }{3:width}{4:,}{1: }{2:int}{1: }{4:(}{5:*}{3:compa}| - {0:~ }| - {0:~ }| - {0:~ }| - | - ]]} - end) - - it('handles deep nested captures', function() - command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext() updatetime=50]]) insert([[ -function FoldInfo.new() - return setmetatable({ - start_counts = {}, - stop_counts = {}, - levels0 = {}, - levels = {}, - }, FoldInfo) -end]]) - exec_lua([[vim.treesitter.get_parser(0, "lua")]]) - - feed('ggjVGkzfgg') - screen:expect{grid=[[ - ^function FoldInfo.new() | - {1: }{5:return}{1: }{4:setmetatable({}{1:·····································}| - end | - {0:~ }| - | - ]]} - - command('hi! Visual guibg=Red') - feed('GVgg') - screen:expect{grid=[[ - ^f{6:unction FoldInfo.new()} | - {7: }{8:return}{7: }{9:setmetatable({}{7:·····································}| - {6:end} | - {0:~ }| - {10:-- VISUAL LINE --} | - ]]} - - feed('10l<C-V>') - screen:expect{grid=[[ - {6:function F}^oldInfo.new() | - {7: }{8:return}{7: }{9:se}{4:tmetatable({}{1:·····································}| - {6:end} | - {0:~ }| - {10:-- VISUAL BLOCK --} | - ]]} - end) - - it('falls back to default', function() - command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext()]]) - insert(test_text) +# h1 +t1 +# h2 +t2]]) + exec_lua([[vim.treesitter.query.set('markdown', 'folds', '(section) @fold')]]) + parse('markdown') + command( + [[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0]] + ) + + feed('ggzojo') + poke_eventloop() + + screen:expect { + grid = [[ + {1:-}# h1 | + {1:│}t1 | + {1:│}^ | + {1:+}{2:+-- 2 lines: # h2·····················}| + {3:~ }|*3 + {4:-- INSERT --} | + ]], + } - feed('ggVGzf') - screen:expect{grid=[[ - {1:^+-- 19 lines: void qsort(void *base, size_t nel, size_t widt}| - {0:~ }| - {0:~ }| - {0:~ }| - | - ]]} + feed('<Esc>u') + -- TODO(tomtomjhj): `u` spuriously opens the fold (#26499). + feed('zMggzo') + + feed('dd') + poke_eventloop() + + screen:expect { + grid = [[ + {1:-}^t1 | + {1:-}# h2 | + {1:│}t2 | + {3:~ }|*4 + 1 line less; before #2 {MATCH:.*}| + ]], + } end) end) diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index e037c9e215..2bf230fe69 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -6,7 +6,7 @@ local insert = helpers.insert local exec_lua = helpers.exec_lua local feed = helpers.feed local command = helpers.command -local meths = helpers.meths +local api = helpers.api local eq = helpers.eq before_each(clear) @@ -100,17 +100,7 @@ local injection_grid_c = [[ return 42; \ | } | ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {1:~ }|*11 | ]] @@ -121,17 +111,7 @@ local injection_grid_expected_c = [[ {4:return} {5:42}; \ | } | ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {1:~ }|*11 | ]] @@ -142,17 +122,17 @@ describe('treesitter highlighting (C)', function() screen = Screen.new(65, 18) screen:attach() screen:set_default_attr_ids { - [1] = {bold = true, foreground = Screen.colors.Blue1}; - [2] = {foreground = Screen.colors.Blue1}; - [3] = {bold = true, foreground = Screen.colors.SeaGreen4}; - [4] = {bold = true, foreground = Screen.colors.Brown}; - [5] = {foreground = Screen.colors.Magenta}; - [6] = {foreground = Screen.colors.Red}; - [7] = {bold = true, foreground = Screen.colors.SlateBlue}; - [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}; - [9] = {foreground = Screen.colors.Magenta, background = Screen.colors.Red}; - [10] = {foreground = Screen.colors.Red, background = Screen.colors.Red}; - [11] = {foreground = Screen.colors.Cyan4}; + [1] = { bold = true, foreground = Screen.colors.Blue1 }, + [2] = { foreground = Screen.colors.Blue1 }, + [3] = { bold = true, foreground = Screen.colors.SeaGreen4 }, + [4] = { bold = true, foreground = Screen.colors.Brown }, + [5] = { foreground = Screen.colors.Magenta }, + [6] = { foreground = Screen.colors.Red }, + [7] = { bold = true, foreground = Screen.colors.SlateBlue }, + [8] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, + [9] = { foreground = Screen.colors.Magenta, background = Screen.colors.Red }, + [10] = { foreground = Screen.colors.Red, background = Screen.colors.Red }, + [11] = { foreground = Screen.colors.Cyan4 }, } exec_lua([[ hl_query = ... ]], hl_query_c) @@ -162,7 +142,8 @@ describe('treesitter highlighting (C)', function() it('is updated with edits', function() insert(hl_text_c) - screen:expect{grid=[[ + screen:expect { + grid = [[ /// Schedule Lua callback on main loop's event queue | static int nlua_schedule(lua_State *const lstate) | { | @@ -178,17 +159,18 @@ describe('treesitter highlighting (C)', function() 1, (void *)(ptrdiff_t)cb); | return 0; | ^} | - {1:~ }| - {1:~ }| + {1:~ }|*2 | - ]]} + ]], + } exec_lua [[ local parser = vim.treesitter.get_parser(0, "c") local highlighter = vim.treesitter.highlighter test_hl = highlighter.new(parser, {queries = {c = hl_query}}) ]] - screen:expect{grid=[[ + 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) | { | @@ -204,14 +186,15 @@ describe('treesitter highlighting (C)', function() {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | ^} | - {1:~ }| - {1:~ }| + {1:~ }|*2 | - ]]} + ]], + } - feed("5Goc<esc>dd") + feed('5Goc<esc>dd') - screen:expect{grid=[[ + 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) | { | @@ -227,13 +210,14 @@ describe('treesitter highlighting (C)', function() {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | } | - {1:~ }| - {1:~ }| + {1:~ }|*2 | - ]]} + ]], + } feed('7Go*/<esc>') - screen:expect{grid=[[ + 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) | { | @@ -252,10 +236,12 @@ describe('treesitter highlighting (C)', function() } | {1:~ }| | - ]]} + ]], + } feed('3Go/*<esc>') - screen:expect{grid=[[ + 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) | { | @@ -274,11 +260,13 @@ describe('treesitter highlighting (C)', function() {4:return} {5:0}; | {8:}} | | - ]]} + ]], + } - feed("gg$") - feed("~") - screen:expect{grid=[[ + feed('gg$') + feed('~') + screen:expect { + grid = [[ {2:/// Schedule Lua callback on main loop's event queu^E} | {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | @@ -297,11 +285,12 @@ describe('treesitter highlighting (C)', function() {4:return} {5:0}; | {8:}} | | - ]]} - + ]], + } - feed("re") - screen:expect{grid=[[ + feed('re') + screen:expect { + grid = [[ {2:/// Schedule Lua callback on main loop's event queu^e} | {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) | { | @@ -320,7 +309,8 @@ describe('treesitter highlighting (C)', function() {4:return} {5:0}; | {8:}} | | - ]]} + ]], + } end) it('is updated with :sort', function() @@ -329,7 +319,8 @@ describe('treesitter highlighting (C)', function() local parser = vim.treesitter.get_parser(0, "c") test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query}}) ]] - screen:expect{grid=[[ + screen:expect { + grid = [[ {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; | {3:bool} ext_widgets[kUIExtCount]; | {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { | @@ -348,10 +339,12 @@ describe('treesitter highlighting (C)', function() } | ^} | | - ]]} + ]], + } - feed ":sort<cr>" - screen:expect{grid=[[ + feed ':sort<cr>' + screen:expect { + grid = [[ ^ | ext_widgets[j] &= (ui->ui_ext[j] || inclusive); | {3:UI} *ui = uis[i]; | @@ -366,15 +359,16 @@ describe('treesitter highlighting (C)', function() {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { | {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { | {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; | - } | - } | + } |*2 {3:void} ui_refresh({3:void}) | :sort | - ]]} + ]], + } - feed "u" + feed 'u' - screen:expect{grid=[[ + screen:expect { + grid = [[ {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; | {3:bool} ext_widgets[kUIExtCount]; | {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { | @@ -393,17 +387,19 @@ describe('treesitter highlighting (C)', function() } | ^} | 19 changes; before #2 {MATCH:.*}| - ]]} + ]], + } end) - it("supports with custom parser", function() + it('supports with custom parser', function() screen:set_default_attr_ids { - [1] = {bold = true, foreground = Screen.colors.SeaGreen4}; + [1] = { bold = true, foreground = Screen.colors.SeaGreen4 }, } insert(test_text_c) - screen:expect{ grid= [[ + screen:expect { + grid = [[ int width = INT_MAX, height = INT_MAX; | bool ext_widgets[kUIExtCount]; | for (UIExtension i = 0; (int)i < kUIExtCount; i++) { | @@ -422,7 +418,8 @@ describe('treesitter highlighting (C)', function() } | ^} | | - ]] } + ]], + } exec_lua [[ parser = vim.treesitter.get_parser(0, "c") @@ -438,7 +435,8 @@ describe('treesitter highlighting (C)', function() local hl = vim.treesitter.highlighter.new(parser, {queries = {c = "(identifier) @type"}}) ]] - screen:expect{ grid = [[ + 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++) { | @@ -457,13 +455,14 @@ describe('treesitter highlighting (C)', function() } | ^} | | - ]] } + ]], + } end) - it("supports injected languages", function() + it('supports injected languages', function() insert(injection_text_c) - screen:expect{grid=injection_grid_c} + screen:expect { grid = injection_grid_c } exec_lua [[ local parser = vim.treesitter.get_parser(0, "c", { @@ -473,13 +472,13 @@ describe('treesitter highlighting (C)', function() test_hl = highlighter.new(parser, {queries = {c = hl_query}}) ]] - screen:expect{grid=injection_grid_expected_c} + screen:expect { grid = injection_grid_expected_c } end) it("supports injecting by ft name in metadata['injection.language']", function() insert(injection_text_c) - screen:expect{grid=injection_grid_c} + screen:expect { grid = injection_grid_c } exec_lua [[ vim.treesitter.language.register("c", "foo") @@ -490,10 +489,10 @@ describe('treesitter highlighting (C)', function() test_hl = highlighter.new(parser, {queries = {c = hl_query}}) ]] - screen:expect{grid=injection_grid_expected_c} + screen:expect { grid = injection_grid_expected_c } end) - it("supports overriding queries, like ", function() + it('supports overriding queries, like ', function() insert([[ int x = INT_MAX; #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) @@ -510,29 +509,21 @@ describe('treesitter highlighting (C)', function() vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, "c")) ]] - screen:expect{grid=[[ + screen:expect { + grid = [[ {3:int} x = {5:INT_MAX}; | #define {5:READ_STRING}(x, y) ({3:char} *)read_string((x), ({3:size_t})(y)) | #define foo {3:void} main() { \ | {4:return} {5:42}; \ | } | ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {1:~ }|*11 | - ]]} + ]], + } end) - it("supports highlighting with custom highlight groups", function() + it('supports highlighting with custom highlight groups', function() insert(hl_text_c) exec_lua [[ @@ -540,7 +531,8 @@ describe('treesitter highlighting (C)', function() test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query}}) ]] - screen:expect{grid=[[ + 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) | { | @@ -556,15 +548,16 @@ describe('treesitter highlighting (C)', function() {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | ^} | - {1:~ }| - {1:~ }| + {1:~ }|*2 | - ]]} + ]], + } -- This will change ONLY the literal strings to look like comments -- The only literal string is the "vim.schedule: expected function" in this test. exec_lua [[vim.cmd("highlight link @string.nonexistent_specializer comment")]] - screen:expect{grid=[[ + 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) | { | @@ -580,14 +573,14 @@ describe('treesitter highlighting (C)', function() {5:1}, ({3:void} *)({3:ptrdiff_t})cb); | {4:return} {5:0}; | ^} | - {1:~ }| - {1:~ }| + {1:~ }|*2 | - ]]} - screen:expect{ unchanged=true } + ]], + } + screen:expect { unchanged = true } end) - it("supports highlighting with priority", function() + it('supports highlighting with priority', function() insert([[ int x = INT_MAX; #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) @@ -601,120 +594,80 @@ describe('treesitter highlighting (C)', function() test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query..'\n((translation_unit) @constant (#set! "priority" 101))\n'}}) ]] -- expect everything to have Constant highlight - screen:expect{grid=[[ + screen:expect { + grid = [[ {12:int}{8: x = INT_MAX;} | {8:#define READ_STRING(x, y) (}{12:char}{8: *)read_string((x), (}{12:size_t}{8:)(y))} | {8:#define foo }{12:void}{8: main() { \} | {8: }{12:return}{8: 42; \} | {8: }} | ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - | - ]], attr_ids={ - [1] = {bold = true, foreground = Screen.colors.Blue1}; - [8] = {foreground = Screen.colors.Magenta1}; - -- bold will not be overwritten at the moment - [12] = {bold = true, foreground = Screen.colors.Magenta1}; - }} + {1:~ }|*11 + | + ]], + attr_ids = { + [1] = { bold = true, foreground = Screen.colors.Blue1 }, + [8] = { foreground = Screen.colors.Magenta1 }, + -- bold will not be overwritten at the moment + [12] = { bold = true, foreground = Screen.colors.Magenta1 }, + }, + } eq({ - {capture='constant', metadata = { priority='101' }, lang='c' }; - {capture='type', metadata = { }, lang='c' }; + { capture = 'constant', metadata = { priority = '101' }, lang = 'c' }, + { capture = 'type', metadata = {}, lang = 'c' }, }, exec_lua [[ return vim.treesitter.get_captures_at_pos(0, 0, 2) ]]) - end) + end) - it("allows to use captures with dots (don't use fallback when specialization of foo exists)", function() - insert([[ + it( + "allows to use captures with dots (don't use fallback when specialization of foo exists)", + function() + insert([[ char* x = "Will somebody ever read this?"; ]]) - screen:expect{grid=[[ + screen:expect { + grid = [[ char* x = "Will somebody ever read this?"; | ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {1:~ }|*15 | - ]]} + ]], + } - command [[ + command [[ hi link @foo.bar Type hi link @foo String ]] - exec_lua [[ + exec_lua [[ local parser = vim.treesitter.get_parser(0, "c", {}) local highlighter = vim.treesitter.highlighter test_hl = highlighter.new(parser, {queries = {c = "(primitive_type) @foo.bar (string_literal) @foo"}}) ]] - screen:expect{grid=[[ + screen:expect { + grid = [[ {3:char}* x = {5:"Will somebody ever read this?"}; | ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {1:~ }|*15 | - ]]} + ]], + } - -- clearing specialization reactivates fallback - command [[ hi clear @foo.bar ]] - screen:expect{grid=[[ + -- clearing specialization reactivates fallback + command [[ hi clear @foo.bar ]] + screen:expect { + grid = [[ {5:char}* x = {5:"Will somebody ever read this?"}; | ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + {1:~ }|*15 | - ]]} - end) + ]], + } + end + ) - it("supports conceal attribute", function() + it('supports conceal attribute', function() insert(hl_text_c) -- conceal can be empty or a single cchar. @@ -723,15 +676,16 @@ describe('treesitter highlighting (C)', function() local parser = vim.treesitter.get_parser(0, "c") test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = [[ ("static" @keyword - (set! conceal "R")) + (#set! conceal "R")) ((identifier) @Identifier - (set! conceal "") - (eq? @Identifier "lstate")) + (#set! conceal "") + (#eq? @Identifier "lstate")) ]]}}) ]=] - screen:expect{grid=[[ + screen:expect { + grid = [[ /// Schedule Lua callback on main loop's event queue | {4:R} int nlua_schedule(lua_State *const ) | { | @@ -747,33 +701,60 @@ describe('treesitter highlighting (C)', function() 1, (void *)(ptrdiff_t)cb); | return 0; | ^} | - {1:~ }| - {1:~ }| + {1:~ }|*2 | - ]]} + ]], + } end) - it("@foo.bar groups has the correct fallback behavior", function() - local get_hl = function(name) return meths.get_hl_by_name(name,1).foreground end - meths.set_hl(0, "@foo", {fg = 1}) - meths.set_hl(0, "@foo.bar", {fg = 2}) - meths.set_hl(0, "@foo.bar.baz", {fg = 3}) - - eq(1, get_hl"@foo") - eq(1, get_hl"@foo.a.b.c.d") - eq(2, get_hl"@foo.bar") - eq(2, get_hl"@foo.bar.a.b.c.d") - eq(3, get_hl"@foo.bar.baz") - eq(3, get_hl"@foo.bar.baz.d") + it('@foo.bar groups has the correct fallback behavior', function() + local get_hl = function(name) + return api.nvim_get_hl_by_name(name, 1).foreground + end + api.nvim_set_hl(0, '@foo', { fg = 1 }) + api.nvim_set_hl(0, '@foo.bar', { fg = 2 }) + api.nvim_set_hl(0, '@foo.bar.baz', { fg = 3 }) + + eq(1, get_hl '@foo') + eq(1, get_hl '@foo.a.b.c.d') + eq(2, get_hl '@foo.bar') + eq(2, get_hl '@foo.bar.a.b.c.d') + eq(3, get_hl '@foo.bar.baz') + eq(3, get_hl '@foo.bar.baz.d') -- lookup is case insensitive - eq(2, get_hl"@FOO.BAR.SPAM") + eq(2, get_hl '@FOO.BAR.SPAM') + + api.nvim_set_hl(0, '@foo.missing.exists', { fg = 3 }) + eq(1, get_hl '@foo.missing') + eq(3, get_hl '@foo.missing.exists') + eq(3, get_hl '@foo.missing.exists.bar') + eq(nil, get_hl '@total.nonsense.but.a.lot.of.dots') + end) - meths.set_hl(0, "@foo.missing.exists", {fg = 3}) - eq(1, get_hl"@foo.missing") - eq(3, get_hl"@foo.missing.exists") - eq(3, get_hl"@foo.missing.exists.bar") - eq(nil, get_hl"@total.nonsense.but.a.lot.of.dots") + it('supports multiple nodes assigned to the same capture #17060', function() + insert([[ + int x = 4; + int y = 5; + int z = 6; + ]]) + + exec_lua([[ + local query = '((declaration)+ @string)' + vim.treesitter.query.set('c', 'highlights', query) + vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c')) + ]]) + + screen:expect { + grid = [[ + {5:int x = 4;} | + {5:int y = 5;} | + {5:int z = 6;} | + ^ | + {1:~ }|*13 + | + ]], + } end) end) @@ -784,16 +765,16 @@ describe('treesitter highlighting (help)', function() screen = Screen.new(40, 6) screen:attach() screen:set_default_attr_ids { - [1] = {foreground = Screen.colors.Blue1}; - [2] = {bold = true, foreground = Screen.colors.Blue1}; - [3] = {bold = true, foreground = Screen.colors.Brown}; - [4] = {foreground = Screen.colors.Cyan4}; - [5] = {foreground = Screen.colors.Magenta1}; + [1] = { foreground = Screen.colors.Blue1 }, + [2] = { bold = true, foreground = Screen.colors.Blue1 }, + [3] = { bold = true, foreground = Screen.colors.Brown }, + [4] = { foreground = Screen.colors.Cyan4 }, + [5] = { foreground = Screen.colors.Magenta1 }, } end) - it("correctly redraws added/removed injections", function() - insert[[ + it('correctly redraws added/removed injections', function() + insert [[ >ruby -- comment local this_is = 'actually_lua' @@ -805,38 +786,43 @@ describe('treesitter highlighting (help)', function() vim.treesitter.start() ]] - screen:expect{grid=[[ + screen:expect { + grid = [[ {1:>ruby} | {1: -- comment} | {1: local this_is = 'actually_lua'} | - < | + {1:<} | ^ | | - ]]} + ]], + } - helpers.curbufmeths.set_text(0, 1, 0, 5, {'lua'}) + helpers.api.nvim_buf_set_text(0, 0, 1, 0, 5, { 'lua' }) - screen:expect{grid=[[ + screen:expect { + grid = [[ {1:>lua} | {1: -- comment} | {1: }{3:local}{1: }{4:this_is}{1: }{3:=}{1: }{5:'actually_lua'} | - < | + {1:<} | ^ | | - ]]} + ]], + } - helpers.curbufmeths.set_text(0, 1, 0, 4, {'ruby'}) + helpers.api.nvim_buf_set_text(0, 0, 1, 0, 4, { 'ruby' }) - screen:expect{grid=[[ + screen:expect { + grid = [[ {1:>ruby} | {1: -- comment} | {1: local this_is = 'actually_lua'} | - < | + {1:<} | ^ | | - ]]} + ]], + } end) - end) describe('treesitter highlighting (nested injections)', function() @@ -846,15 +832,15 @@ describe('treesitter highlighting (nested injections)', function() screen = Screen.new(80, 7) screen:attach() screen:set_default_attr_ids { - [1] = {foreground = Screen.colors.SlateBlue}; - [2] = {bold = true, foreground = Screen.colors.Brown}; - [3] = {foreground = Screen.colors.Cyan4}; - [4] = {foreground = Screen.colors.Fuchsia}; + [1] = { foreground = Screen.colors.SlateBlue }, + [2] = { bold = true, foreground = Screen.colors.Brown }, + [3] = { foreground = Screen.colors.Cyan4 }, + [4] = { foreground = Screen.colors.Fuchsia }, } end) - it("correctly redraws nested injections (GitHub #25252)", function() - insert[=[ + it('correctly redraws nested injections (GitHub #25252)', function() + insert [=[ function foo() print("Lua!") end local lorem = { @@ -875,9 +861,10 @@ vim.cmd([[ ]] -- invalidate the language tree - feed("ggi--[[<ESC>04x") + feed('ggi--[[<ESC>04x') - screen:expect{grid=[[ + screen:expect { + grid = [[ {2:^function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} | | {2:local} {3:lorem} {2:=} {1:{} | @@ -885,12 +872,14 @@ vim.cmd([[ {3:bar} {2:=} {1:{},} | {1:}} | | - ]]} + ]], + } -- spam newline insert/delete to invalidate Lua > Vim > Lua region - feed("3jo<ESC>ddko<ESC>ddko<ESC>ddko<ESC>ddk0") + feed('3jo<ESC>ddko<ESC>ddko<ESC>ddko<ESC>ddk0') - screen:expect{grid=[[ + screen:expect { + grid = [[ {2:function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} | | {2:local} {3:lorem} {2:=} {1:{} | @@ -898,7 +887,7 @@ vim.cmd([[ {3:bar} {2:=} {1:{},} | {1:}} | | - ]]} + ]], + } end) - end) diff --git a/test/functional/treesitter/inspect_tree_spec.lua b/test/functional/treesitter/inspect_tree_spec.lua new file mode 100644 index 0000000000..a3d44ff906 --- /dev/null +++ b/test/functional/treesitter/inspect_tree_spec.lua @@ -0,0 +1,115 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local insert = helpers.insert +local dedent = helpers.dedent +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local feed = helpers.feed + +describe('vim.treesitter.inspect_tree', function() + before_each(clear) + + local expect_tree = function(x) + local expected = vim.split(vim.trim(dedent(x)), '\n') + local actual = helpers.buf_lines(0) ---@type string[] + eq(expected, actual) + end + + it('working', function() + insert([[ + print() + ]]) + + exec_lua([[ + vim.treesitter.start(0, 'lua') + vim.treesitter.inspect_tree() + ]]) + + expect_tree [[ + (chunk ; [0, 0] - [2, 0] + (function_call ; [0, 0] - [0, 7] + name: (identifier) ; [0, 0] - [0, 5] + arguments: (arguments))) ; [0, 5] - [0, 7] + ]] + end) + + it('can toggle to show anonymous nodes', function() + insert([[ + print() + ]]) + + exec_lua([[ + vim.treesitter.start(0, 'lua') + vim.treesitter.inspect_tree() + ]]) + feed('a') + + expect_tree [[ + (chunk ; [0, 0] - [2, 0] + (function_call ; [0, 0] - [0, 7] + name: (identifier) ; [0, 0] - [0, 5] + arguments: (arguments ; [0, 5] - [0, 7] + "(" ; [0, 5] - [0, 6] + ")"))) ; [0, 6] - [0, 7] + ]] + end) + + it('works for injected trees', function() + insert([[ + ```lua + return + ``` + ]]) + + exec_lua([[ + vim.treesitter.start(0, 'markdown') + vim.treesitter.get_parser():parse() + vim.treesitter.inspect_tree() + ]]) + + expect_tree [[ + (document ; [0, 0] - [4, 0] + (section ; [0, 0] - [4, 0] + (fenced_code_block ; [0, 0] - [3, 0] + (fenced_code_block_delimiter) ; [0, 0] - [0, 3] + (info_string ; [0, 3] - [0, 6] + (language)) ; [0, 3] - [0, 6] + (block_continuation) ; [1, 0] - [1, 0] + (code_fence_content ; [1, 0] - [2, 0] + (chunk ; [1, 0] - [2, 0] + (return_statement)) ; [1, 0] - [1, 6] + (block_continuation)) ; [2, 0] - [2, 0] + (fenced_code_block_delimiter)))) ; [2, 0] - [2, 3] + ]] + end) + + it('can toggle to show languages', function() + insert([[ + ```lua + return + ``` + ]]) + + exec_lua([[ + vim.treesitter.start(0, 'markdown') + vim.treesitter.get_parser():parse() + vim.treesitter.inspect_tree() + ]]) + feed('I') + + expect_tree [[ + (document ; [0, 0] - [4, 0] markdown + (section ; [0, 0] - [4, 0] markdown + (fenced_code_block ; [0, 0] - [3, 0] markdown + (fenced_code_block_delimiter) ; [0, 0] - [0, 3] markdown + (info_string ; [0, 3] - [0, 6] markdown + (language)) ; [0, 3] - [0, 6] markdown + (block_continuation) ; [1, 0] - [1, 0] markdown + (code_fence_content ; [1, 0] - [2, 0] markdown + (chunk ; [1, 0] - [2, 0] lua + (return_statement)) ; [1, 0] - [1, 6] lua + (block_continuation)) ; [2, 0] - [2, 0] markdown + (fenced_code_block_delimiter)))) ; [2, 0] - [2, 3] markdown + ]] + end) +end) diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 9b871a72fb..65d9e0e81c 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -13,27 +13,43 @@ before_each(clear) describe('treesitter language API', function() -- error tests not requiring a parser library it('handles missing language', function() - eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", - pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")) + eq( + ".../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("Failed to load parser for language 'borklang': uv_dlopen: .+", - pcall_err(exec_lua, "parser = vim.treesitter.language.add('borklang', { path = 'borkbork.so' })")) + matches( + "Failed to load parser for language 'borklang': uv_dlopen: .+", + pcall_err( + exec_lua, + "parser = vim.treesitter.language.add('borklang', { path = 'borkbork.so' })" + ) + ) eq(false, exec_lua("return pcall(vim.treesitter.language.add, 'borklang')")) - eq(false, exec_lua("return pcall(vim.treesitter.language.add, 'borklang', { path = 'borkbork.so' })")) + eq( + false, + exec_lua("return pcall(vim.treesitter.language.add, 'borklang', { path = 'borkbork.so' })") + ) - eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", - pcall_err(exec_lua, "parser = vim.treesitter.language.inspect('borklang')")) + eq( + ".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", + pcall_err(exec_lua, "parser = vim.treesitter.language.inspect('borklang')") + ) - matches("Failed to load parser: uv_dlsym: .+", - pcall_err(exec_lua, 'vim.treesitter.language.add("c", { symbol_name = "borklang" })')) + matches( + 'Failed to load parser: uv_dlsym: .+', + pcall_err(exec_lua, 'vim.treesitter.language.add("c", { symbol_name = "borklang" })') + ) end) it('shows error for invalid language name', function() - eq(".../language.lua:0: '/foo/' is not a valid language name", - pcall_err(exec_lua, 'vim.treesitter.language.add("/foo/")')) + eq( + ".../language.lua:0: '/foo/' is not a valid language name", + pcall_err(exec_lua, 'vim.treesitter.language.add("/foo/")') + ) end) it('inspects language', function() @@ -52,40 +68,45 @@ describe('treesitter language API', function() return {keys, lang.fields, symbols} ]])) - eq({fields=true, symbols=true, _abi_version=true}, keys) + eq({ fields = true, symbols = true, _abi_version = true }, keys) local fset = {} - for _,f in pairs(fields) do - eq("string", type(f)) + for _, f in pairs(fields) do + eq('string', type(f)) fset[f] = true end - eq(true, fset["directive"]) - eq(true, fset["initializer"]) + eq(true, fset['directive']) + eq(true, fset['initializer']) local has_named, has_anonymous - for _,s in pairs(symbols) do - eq("string", type(s[1])) - eq("boolean", type(s[2])) - if s[1] == "for_statement" and s[2] == true then + for _, s in pairs(symbols) do + eq('string', type(s[1])) + eq('boolean', type(s[2])) + if s[1] == 'for_statement' and s[2] == true then has_named = true - elseif s[1] == "|=" and s[2] == false then + elseif s[1] == '|=' and s[2] == false then has_anonymous = true end end - eq({true,true}, {has_named,has_anonymous}) + eq({ true, true }, { has_named, has_anonymous }) end) - it('checks if vim.treesitter.get_parser tries to create a new parser on filetype change', function () - command("set filetype=c") - -- Should not throw an error when filetype is c - eq('c', exec_lua("return vim.treesitter.get_parser(0):lang()")) - command("set filetype=borklang") - -- Should throw an error when filetype changes to borklang - eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", - pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0, 'borklang')")) - end) + it( + 'checks if vim.treesitter.get_parser tries to create a new parser on filetype change', + function() + command('set filetype=c') + -- Should not throw an error when filetype is c + eq('c', exec_lua('return vim.treesitter.get_parser(0):lang()')) + command('set filetype=borklang') + -- Should throw an error when filetype changes to borklang + eq( + ".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", + pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0, 'borklang')") + ) + end + ) - it('retrieve the tree given a range', function () + it('retrieve the tree given a range', function() insert([[ int main() { int x = 3; @@ -99,7 +120,7 @@ describe('treesitter language API', function() eq('<node translation_unit>', exec_lua('return tostring(tree:root())')) end) - it('retrieve the node given a range', function () + it('retrieve the node given a range', function() insert([[ int main() { int x = 3; @@ -113,4 +134,3 @@ describe('treesitter language API', function() eq('<node primitive_type>', exec_lua('return tostring(node)')) end) end) - diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index eef75d0e91..f114d36823 100644 --- a/test/functional/treesitter/node_spec.lua +++ b/test/functional/treesitter/node_spec.lua @@ -9,7 +9,7 @@ local assert_alive = helpers.assert_alive before_each(clear) local function lua_eval(lua_expr) - return exec_lua("return " .. lua_expr) + return exec_lua('return ' .. lua_expr) end describe('treesitter node API', function() @@ -40,6 +40,20 @@ describe('treesitter node API', function() assert_alive() end) + it('get_node() with lang given', function() + -- this buffer doesn't have filetype set! + insert('local foo = function() end') + exec_lua([[ + node = vim.treesitter.get_node({ + bufnr = 0, + pos = { 0, 6 }, -- on "foo" + lang = 'lua', + }) + ]]) + eq('foo', lua_eval('vim.treesitter.get_node_text(node, 0)')) + eq('identifier', lua_eval('node:type()')) + end) + it('can move between siblings', function() insert([[ int main(int x, int y, int z) { @@ -48,14 +62,13 @@ describe('treesitter node API', function() ]]) exec_lua([[ - query = require"vim.treesitter.query" parser = vim.treesitter.get_parser(0, "c") tree = parser:parse()[1] root = tree:root() lang = vim.treesitter.language.inspect('c') function node_text(node) - return query.get_node_text(node, 0) + return vim.treesitter.get_node_text(node, 0) end ]]) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 6f386115ae..875b772fec 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -8,11 +8,13 @@ local exec_lua = helpers.exec_lua local pcall_err = helpers.pcall_err local feed = helpers.feed local is_os = helpers.is_os +local api = helpers.api +local fn = helpers.fn describe('treesitter parser API', function() before_each(function() clear() - exec_lua[[ + exec_lua [[ vim.g.__ts_debug = 1 ]] end) @@ -30,59 +32,62 @@ describe('treesitter parser API', function() lang = vim.treesitter.language.inspect('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") + 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(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")) + 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(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()")) + 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")) + 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")) + eq(true, exec_lua('return parser:parse()[1] == tree2')) end) local test_text = [[ @@ -107,7 +112,7 @@ void ui_refresh(void) }]] it('allows to iterate over nodes children', function() - insert(test_text); + insert(test_text) local res = exec_lua([[ parser = vim.treesitter.get_parser(0, "c") @@ -122,26 +127,28 @@ void ui_refresh(void) ]]) eq({ - {"primitive_type", "type"}, - {"function_declarator", "declarator"}, - {"compound_statement", "body"} + { 'primitive_type', 'type' }, + { 'function_declarator', 'declarator' }, + { 'compound_statement', 'body' }, }, res) end) it('does not get parser for empty filetype', function() - insert(test_text); + insert(test_text) - eq('.../treesitter.lua:0: There is no parser available for buffer 1 and one' - .. ' could not be created because lang could not be determined. Either' - .. ' pass lang or set the buffer filetype', - pcall_err(exec_lua, 'vim.treesitter.get_parser(0)')) + eq( + '.../treesitter.lua:0: There is no parser available for buffer 1 and one' + .. ' could not be created because lang could not be determined. Either' + .. ' pass lang or set the buffer filetype', + pcall_err(exec_lua, 'vim.treesitter.get_parser(0)') + ) -- Must provide language for buffers with an empty filetype exec_lua("vim.treesitter.get_parser(0, 'c')") end) it('allows to get a child by field', function() - insert(test_text); + insert(test_text) local res = exec_lua([[ parser = vim.treesitter.get_parser(0, "c") @@ -155,7 +162,7 @@ void ui_refresh(void) return res ]]) - eq({{ "primitive_type", 0, 0, 0, 4 }}, res) + eq({ { 'primitive_type', 0, 0, 0, 4 } }, res) local res_fail = exec_lua([[ parser = vim.treesitter.get_parser(0, "c") @@ -166,14 +173,14 @@ void ui_refresh(void) assert(res_fail) end) - local query = [[ - ((call_expression function: (identifier) @minfunc (argument_list (identifier) @min_id)) (eq? @minfunc "MIN")) + local test_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() + it('supports runtime queries', function() local ret = exec_lua [[ return vim.treesitter.query.get("c", "highlights").captures[1] ]] @@ -181,10 +188,11 @@ void ui_refresh(void) eq('variable', ret) end) - it("supports caching queries", function() - local long_query = query:rep(100) + it('supports caching queries', function() + local long_query = test_query:rep(100) local function q(n) - return exec_lua ([[ + return exec_lua( + [[ local query, n = ... local before = vim.uv.hrtime() for i=1,n,1 do @@ -192,7 +200,10 @@ void ui_refresh(void) end local after = vim.uv.hrtime() return after - before - ]], long_query, n) + ]], + long_query, + n + ) end local firstrun = q(1) @@ -200,13 +211,17 @@ void ui_refresh(void) -- First run should be at least 200x slower than an 100 subsequent runs. local factor = is_os('win') and 100 or 200 - assert(factor * manyruns < firstrun, ('firstrun: %f ms, manyruns: %f ms'):format(firstrun / 1e6, manyruns / 1e6)) + assert( + factor * manyruns < firstrun, + ('firstrun: %f ms, manyruns: %f ms'):format(firstrun / 1e6, manyruns / 1e6) + ) end) it('support query and iter by capture', function() insert(test_text) - local res = exec_lua([[ + local res = exec_lua( + [[ cquery = vim.treesitter.query.parse("c", ...) parser = vim.treesitter.get_parser(0, "c") tree = parser:parse()[1] @@ -216,50 +231,124 @@ void ui_refresh(void) table.insert(res, {cquery.captures[cid], node:type(), node:range()}) end return res - ]], query) + ]], + test_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 } + { '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() insert(test_text) - local res = exec_lua([[ + local res = exec_lua( + [[ cquery = vim.treesitter.query.parse("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 + for pattern, match in cquery:iter_matches(tree:root(), 0, 7, 14, { all = true }) 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()}) + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end end table.insert(res, {pattern, mrepr}) end return res - ]], query) + ]], + test_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('support query and iter by capture for quantifiers', function() + insert(test_text) + + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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 + ]], + '(expression_statement (assignment_expression (call_expression)))+ @funccall' + ) 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 } } } + { 'funccall', 'expression_statement', 11, 4, 11, 34 }, + { 'funccall', 'expression_statement', 12, 4, 12, 37 }, + { 'funccall', 'expression_statement', 13, 4, 13, 34 }, + }, res) + end) + + it('support query and iter by match for quantifiers', function() + insert(test_text) + + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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, { all = true }) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end + end + table.insert(res, {pattern, mrepr}) + end + return res + ]], + '(expression_statement (assignment_expression (call_expression)))+ @funccall' + ) + + eq({ + { + 1, + { + { 'funccall', 'expression_statement', 11, 4, 11, 34 }, + { 'funccall', 'expression_statement', 12, 4, 12, 37 }, + { 'funccall', 'expression_statement', 13, 4, 13, 34 }, + }, + }, }, res) end) @@ -286,7 +375,9 @@ def run a = <<~E end]] insert(text) - eq('', exec_lua[[ + eq( + '', + exec_lua [[ local fake_node = {} function fake_node:start() return 3, 0, 23 @@ -301,7 +392,8 @@ end]] return 3, 0, 3, 0 end return vim.treesitter.get_node_text(fake_node, 0) - ]]) + ]] + ) end) it('support getting empty text if node range is zero width', function() @@ -338,11 +430,13 @@ end]] parser = vim.treesitter.get_parser(0, "c") tree = parser:parse()[1] res = {} - for pattern, match in cquery:iter_matches(tree:root(), 0) do + for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1, { all = true }) 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()}) + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end end table.insert(res, {pattern, mrepr}) end @@ -350,12 +444,12 @@ end]] ]]) eq({ - { 2, { { "times", '*', 0, 4, 0, 5 } } }, - { 5, { { "string", 'string_literal', 0, 16, 0, 20 } } }, - { 4, { { "escape", 'escape_sequence', 0, 17, 0, 19 } } }, - { 3, { { "paren", '(', 0, 22, 0, 23 } } }, - { 1, { { "plus", '+', 0, 25, 0, 26 } } }, - { 2, { { "times", '*', 0, 30, 0, 31 } } }, + { 2, { { 'times', '*', 0, 4, 0, 5 } } }, + { 5, { { 'string', 'string_literal', 0, 16, 0, 20 } } }, + { 4, { { 'escape', 'escape_sequence', 0, 17, 0, 19 } } }, + { 3, { { 'paren', '(', 0, 22, 0, 23 } } }, + { 1, { { 'plus', '+', 0, 25, 0, 26 } } }, + { 2, { { 'times', '*', 0, 30, 0, 31 } } }, }, res) end) @@ -394,54 +488,63 @@ end]] end ]]) - local res0 = exec_lua([[return get_query_result(...)]], - [[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]]) + local res0 = exec_lua( + [[return get_query_result(...)]], + [[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]] + ) eq({ - { "c-keyword", "primitive_type", { 2, 2, 2, 5 }, "int" }, - { "c-keyword", "primitive_type", { 3, 4, 3, 7 }, "int" }, + { 'c-keyword', 'primitive_type', { 2, 2, 2, 5 }, 'int' }, + { 'c-keyword', 'primitive_type', { 3, 4, 3, 7 }, 'int' }, }, res0) - local res1 = exec_lua([[return get_query_result(...)]], + local res1 = exec_lua( + [[return get_query_result(...)]], [[ ((string_literal) @fizzbuzz-strings (#any-of? @fizzbuzz-strings "\"number= %d FizzBuzz\\n\"" "\"number= %d Fizz\\n\"" "\"number= %d Buzz\\n\"" )) - ]]) + ]] + ) eq({ - { "fizzbuzz-strings", "string_literal", { 6, 15, 6, 38 }, "\"number= %d FizzBuzz\\n\""}, - { "fizzbuzz-strings", "string_literal", { 8, 15, 8, 34 }, "\"number= %d Fizz\\n\""}, - { "fizzbuzz-strings", "string_literal", { 10, 15, 10, 34 }, "\"number= %d Buzz\\n\""}, + { 'fizzbuzz-strings', 'string_literal', { 6, 15, 6, 38 }, '"number= %d FizzBuzz\\n"' }, + { 'fizzbuzz-strings', 'string_literal', { 8, 15, 8, 34 }, '"number= %d Fizz\\n"' }, + { 'fizzbuzz-strings', 'string_literal', { 10, 15, 10, 34 }, '"number= %d Buzz\\n"' }, }, res1) end) - it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() - insert('char* astring = "Hello World!";') + it( + 'allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', + function() + insert('char* astring = "Hello World!";') - local res = exec_lua([[ + local res = exec_lua([[ cquery = vim.treesitter.query.parse("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) do + for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1, { all = true }) 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()}) + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) + end 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) + 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([[ @@ -450,44 +553,250 @@ end]] } ]]) - local custom_query = "((identifier) @main (#is-main? @main))" + local custom_query = '((identifier) @main (#is-main? @main))' - local res = exec_lua([[ - local query = vim.treesitter.query + do + local res = exec_lua( + [[ + local query = vim.treesitter.query + + local function is_main(match, pattern, bufnr, predicate) + local nodes = match[ predicate[2] ] + for _, node in ipairs(nodes) do + if vim.treesitter.get_node_text(node, bufnr) == 'main' then + return true + end + end + return false + end + + local parser = vim.treesitter.get_parser(0, "c") - local function is_main(match, pattern, bufnr, predicate) - local node = match[ predicate[2] ] + -- Time bomb: update this in 0.12 + if vim.fn.has('nvim-0.12') == 1 then + return 'Update this test to remove this message and { all = true } from add_predicate' + end + query.add_predicate("is-main?", is_main, { all = true }) + + local query = query.parse("c", ...) + + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + table.insert(nodes, {node:range()}) + end + + return nodes + ]], + custom_query + ) - return query.get_node_text(node, bufnr) + eq({ { 0, 4, 0, 8 } }, res) end - local parser = vim.treesitter.get_parser(0, "c") + -- Once with the old API. Remove this whole 'do' block in 0.12 + do + local res = exec_lua( + [[ + local query = vim.treesitter.query - query.add_predicate("is-main?", is_main) + local function is_main(match, pattern, bufnr, predicate) + local node = match[ predicate[2] ] - local query = query.parse("c", ...) + return vim.treesitter.get_node_text(node, bufnr) == 'main' + end - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do - table.insert(nodes, {node:range()}) + local parser = vim.treesitter.get_parser(0, "c") + + query.add_predicate("is-main?", is_main, true) + + local query = query.parse("c", ...) + + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + table.insert(nodes, {node:range()}) + end + + return nodes + ]], + custom_query + ) + + -- Remove this 'do' block in 0.12 + eq(0, fn.has('nvim-0.12')) + eq({ { 0, 4, 0, 8 } }, res) end - return nodes - ]], custom_query) + do + local res = exec_lua [[ + local query = vim.treesitter.query - eq({{0, 4, 0, 8}}, res) + local t = {} + for _, v in ipairs(query.list_predicates()) do + t[v] = true + end - local res_list = exec_lua[[ - local query = vim.treesitter.query + return t + ]] - local list = query.list_predicates() + eq(true, res['is-main?']) + end + end) - table.sort(list) + it('supports "all" and "any" semantics for predicates on quantified captures #24738', function() + local query_all = [[ + (((comment (comment_content))+) @bar + (#lua-match? @bar "Yes")) + ]] + + local query_any = [[ + (((comment (comment_content))+) @bar + (#any-lua-match? @bar "Yes")) + ]] + + local function test(input, query) + api.nvim_buf_set_lines(0, 0, -1, true, vim.split(dedent(input), '\n')) + return exec_lua( + [[ + local parser = vim.treesitter.get_parser(0, "lua") + local query = vim.treesitter.query.parse("lua", ...) + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + nodes[#nodes+1] = { node:range() } + end + return nodes + ]], + query + ) + end + + eq( + {}, + test( + [[ + -- Yes + -- No + -- Yes + ]], + query_all + ) + ) + + eq( + { + { 0, 2, 0, 8 }, + { 1, 2, 1, 8 }, + { 2, 2, 2, 8 }, + }, + test( + [[ + -- Yes + -- Yes + -- Yes + ]], + query_all + ) + ) + + eq( + {}, + test( + [[ + -- No + -- No + -- No + ]], + query_any + ) + ) + + eq( + { + { 0, 2, 0, 7 }, + { 1, 2, 1, 8 }, + { 2, 2, 2, 7 }, + }, + test( + [[ + -- No + -- Yes + -- No + ]], + query_any + ) + ) + end) + + it('supports any- prefix to match any capture when using quantifiers #24738', function() + insert([[ + -- Comment + -- Comment + -- Comment + ]]) - return list + local query = [[ + (((comment (comment_content))+) @bar + (#lua-match? @bar "Comment")) ]] - eq({ 'any-of?', 'contains?', 'eq?', 'has-ancestor?', 'has-parent?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list) + local result = exec_lua( + [[ + local parser = vim.treesitter.get_parser(0, "lua") + local query = vim.treesitter.query.parse("lua", ...) + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + nodes[#nodes+1] = { node:range() } + end + return nodes + ]], + query + ) + + eq({ + { 0, 2, 0, 12 }, + { 1, 2, 1, 12 }, + { 2, 2, 2, 12 }, + }, result) + end) + + it('supports the old broken version of iter_matches #24738', function() + -- Delete this test in 0.12 when iter_matches is removed + eq(0, fn.has('nvim-0.12')) + + insert(test_text) + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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 + 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 + ]], + test_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('allows to set simple ranges', function() @@ -498,7 +807,7 @@ end]] return { parser:parse()[1]:root():range() } ]] - eq({0, 0, 19, 0}, res) + 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) @@ -509,7 +818,7 @@ end]] return { parser:parse(true)[1]:root():range() } ]] - eq({0, 0, 18, 1}, res2) + eq({ 0, 0, 18, 1 }, res2) eq({ { { 0, 0, 0, 18, 1, 512 } } }, exec_lua [[ return parser:included_regions() ]]) @@ -522,7 +831,7 @@ end]] eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) end) - it("allows to set complex ranges", function() + it('allows to set complex ranges', function() insert(test_text) local res = exec_lua [[ @@ -552,10 +861,11 @@ end]] { 8, 2, 8, 33 }, { 9, 7, 9, 20 }, { 10, 4, 10, 20 }, - { 14, 9, 14, 27 } }, res) + { 14, 9, 14, 27 }, + }, res) end) - it("allows to create string parsers", function() + 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() } @@ -564,35 +874,39 @@ end]] eq({ 0, 0, 0, 13 }, ret) end) - it("allows to run queries with string parsers", function() + it('allows to run queries with string parsers', function() local txt = [[ int foo = 42; int bar = 13; ]] - local ret = exec_lua([[ + local ret = exec_lua( + [[ local str = ... local parser = vim.treesitter.get_string_parser(str, "c") local nodes = {} - local query = vim.treesitter.query.parse("c", '((identifier) @id (eq? @id "foo"))') + local query = vim.treesitter.query.parse("c", '((identifier) @id (#eq? @id "foo"))') for _, node in query:iter_captures(parser:parse()[1]:root(), str) do table.insert(nodes, { node:range() }) end - return nodes]], txt) + return nodes]], + txt + ) - eq({ {0, 10, 0, 13} }, ret) + eq({ { 0, 10, 0, 13 } }, ret) end) - it("should use node range when omitted", function() + it('should use node range when omitted', function() local txt = [[ int foo = 42; int bar = 13; ]] - local ret = exec_lua([[ + local ret = exec_lua( + [[ local str = ... local parser = vim.treesitter.get_string_parser(str, "c") @@ -604,12 +918,14 @@ end]] table.insert(nodes, { node:range() }) end - return nodes]], txt) + return nodes]], + txt + ) - eq({ {1, 10, 1, 13} }, ret) + eq({ { 1, 10, 1, 13 } }, ret) end) - describe("when creating a language tree", function() + describe('when creating a language tree', function() local function get_ranges() return exec_lua([[ local result = {} @@ -629,8 +945,8 @@ int x = INT_MAX; ]]) end) - describe("when parsing regions independently", function() - it("should inject a language", function() + describe('when parsing regions independently', function() + it('should inject a language', function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { injections = { @@ -638,32 +954,32 @@ int x = INT_MAX; parser:parse(true) ]]) - eq("table", exec_lua("return type(parser:children().c)")) - eq(5, exec_lua("return #parser:children().c:trees()")) + 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, 63}, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - {2, 29, 2, 66} -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + { 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, 63 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + { 2, 29, 2, 66 }, -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) helpers.feed('ggo<esc>') - eq(5, exec_lua("return #parser:children().c:trees()")) + eq(5, exec_lua('return #parser:children().c:trees()')) eq({ - {0, 0, 8, 0}, -- root tree - {4, 14, 4, 17}, -- VALUE 123 - {5, 15, 5, 18}, -- VALUE1 123 - {6, 15, 6, 18}, -- VALUE2 123 - {2, 26, 2, 63}, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - {3, 29, 3, 66} -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + { 0, 0, 8, 0 }, -- root tree + { 4, 14, 4, 17 }, -- VALUE 123 + { 5, 15, 5, 18 }, -- VALUE1 123 + { 6, 15, 6, 18 }, -- VALUE2 123 + { 2, 26, 2, 63 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + { 3, 29, 3, 66 }, -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) end) end) - describe("when parsing regions combined", function() - it("should inject a language", function() + describe('when parsing regions combined', function() + it('should inject a language', function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { injections = { @@ -671,33 +987,45 @@ int x = INT_MAX; parser:parse(true) ]]) - eq("table", exec_lua("return type(parser:children().c)")) - eq(2, exec_lua("return #parser:children().c:trees()")) + 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, 66} -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + { 0, 0, 7, 0 }, -- root tree + { 3, 14, 5, 18 }, -- VALUE 123 + -- VALUE1 123 + -- VALUE2 123 + { 1, 26, 2, 66 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) helpers.feed('ggo<esc>') - eq("table", exec_lua("return type(parser:children().c)")) - eq(2, exec_lua("return #parser:children().c:trees()")) + eq('table', exec_lua('return type(parser:children().c)')) + eq(2, exec_lua('return #parser:children().c:trees()')) eq({ - {0, 0, 8, 0}, -- root tree - {4, 14, 6, 18}, -- VALUE 123 - -- VALUE1 123 - -- VALUE2 123 - {2, 26, 3, 66} -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + { 0, 0, 8, 0 }, -- root tree + { 4, 14, 6, 18 }, -- VALUE 123 + -- VALUE1 123 + -- VALUE2 123 + { 2, 26, 3, 66 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + }, get_ranges()) + + helpers.feed('7ggI//<esc>') + exec_lua([[parser:parse({6, 7})]]) + eq('table', exec_lua('return type(parser:children().c)')) + eq(2, exec_lua('return #parser:children().c:trees()')) + eq({ + { 0, 0, 8, 0 }, -- root tree + { 4, 14, 5, 18 }, -- VALUE 123 + -- VALUE1 123 + { 2, 26, 3, 66 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) end) end) - describe("when using injection.self", function() - it("should inject the source language", function() + describe('when using injection.self', function() + it('should inject the source language', function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { injections = { @@ -705,32 +1033,32 @@ int x = INT_MAX; parser:parse(true) ]]) - eq("table", exec_lua("return type(parser:children().c)")) - eq(5, exec_lua("return #parser:children().c:trees()")) + 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, 63}, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - {2, 29, 2, 66} -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + { 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, 63 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + { 2, 29, 2, 66 }, -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) helpers.feed('ggo<esc>') - eq(5, exec_lua("return #parser:children().c:trees()")) + eq(5, exec_lua('return #parser:children().c:trees()')) eq({ - {0, 0, 8, 0}, -- root tree - {4, 14, 4, 17}, -- VALUE 123 - {5, 15, 5, 18}, -- VALUE1 123 - {6, 15, 6, 18}, -- VALUE2 123 - {2, 26, 2, 63}, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - {3, 29, 3, 66} -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + { 0, 0, 8, 0 }, -- root tree + { 4, 14, 4, 17 }, -- VALUE 123 + { 5, 15, 5, 18 }, -- VALUE1 123 + { 6, 15, 6, 18 }, -- VALUE2 123 + { 2, 26, 2, 63 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + { 3, 29, 3, 66 }, -- READ_STRING_OK(x, y) (char *)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() + 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", { injections = { @@ -738,18 +1066,18 @@ int x = INT_MAX; parser:parse(true) ]]) - eq("table", exec_lua("return type(parser:children().c)")) + eq('table', exec_lua('return type(parser:children().c)')) eq({ - {0, 0, 7, 0}, -- root tree - {3, 16, 3, 16}, -- VALUE 123 - {4, 17, 4, 17}, -- VALUE1 123 - {5, 17, 5, 17}, -- VALUE2 123 - {1, 26, 1, 63}, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - {2, 29, 2, 66} -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) + { 0, 0, 7, 0 }, -- root tree + { 3, 16, 3, 16 }, -- VALUE 123 + { 4, 17, 4, 17 }, -- VALUE1 123 + { 5, 17, 5, 17 }, -- VALUE2 123 + { 1, 26, 1, 63 }, -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) + { 2, 29, 2, 66 }, -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) end) - it("should list all directives", function() - local res_list = exec_lua[[ + it('should list all directives', function() + local res_list = exec_lua [[ local query = vim.treesitter.query local list = query.list_directives() @@ -764,7 +1092,7 @@ int x = INT_MAX; end) end) - describe("when getting the language for a range", function() + describe('when getting the language for a range', function() before_each(function() insert([[ int x = INT_MAX; @@ -772,7 +1100,7 @@ int x = INT_MAX; ]]) end) - it("should return the correct language tree", function() + it('should return the correct language tree', function() local result = exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { injections = { c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c"))'}}) @@ -787,9 +1115,9 @@ int x = INT_MAX; 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() + 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; ]]) @@ -800,18 +1128,18 @@ int x = INT_MAX; query = vim.treesitter.query.parse("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) do + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, -1, { all = true }) do result = metadata.key end return result ]]) - eq(result, "value") + eq(result, 'value') end) - describe("when setting a key on a capture", function() - it("it should create the nested table", function() + describe('when setting a key on a capture', function() + it('it should create the nested table', function() insert([[ int x = 3; ]]) @@ -823,17 +1151,17 @@ int x = INT_MAX; query = vim.treesitter.query.parse("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) do + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, -1, { all = true }) do for _, nested_tbl in pairs(metadata) do return nested_tbl.key end end ]]) - eq(result, "value") + eq(result, 'value') end) - it("it should not overwrite the nested table", function() + it('it should not overwrite the nested table', function() insert([[ int x = 3; ]]) @@ -845,15 +1173,15 @@ int x = INT_MAX; query = vim.treesitter.query.parse("c", '((number_literal) @number (#set! @number "key" "value") (#set! @number "key2" "value2"))') parser = vim.treesitter.get_parser(0, "c") - for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0) do + for pattern, match, metadata in query:iter_matches(parser:parse()[1]:root(), 0, 0, -1, { all = true }) do for _, nested_tbl in pairs(metadata) do return nested_tbl end end ]]) local expected = { - ["key"] = "value", - ["key2"] = "value2", + ['key'] = 'value', + ['key2'] = 'value2', } eq(expected, result) @@ -878,7 +1206,8 @@ int x = INT_MAX; ]]) local function run_query() - return exec_lua([[ + return exec_lua( + [[ local query = vim.treesitter.query.parse("c", ...) parser = vim.treesitter.get_parser() tree = parser:parse()[1] @@ -887,23 +1216,24 @@ int x = INT_MAX; table.insert(res, {query.captures[id], node:range()}) end return res - ]], query0) + ]], + query0 + ) end eq({ { 'function', 0, 0, 2, 1 }, - { 'declaration', 1, 2, 1, 12 } + { 'declaration', 1, 2, 1, 12 }, }, run_query()) - helpers.command'normal ggO' + helpers.command 'normal ggO' insert('int a;') eq({ { 'declaration', 0, 0, 0, 6 }, { 'function', 1, 0, 3, 1 }, - { 'declaration', 2, 2, 2, 12 } + { 'declaration', 2, 2, 2, 12 }, }, run_query()) - end) it('handles ranges when source is a multiline string (#20419)', function() @@ -926,7 +1256,8 @@ int x = INT_MAX; ]] ]==] - local r = exec_lua([[ + local r = exec_lua( + [[ local parser = vim.treesitter.get_string_parser(..., 'lua') parser:parse(true) local ranges = {} @@ -934,19 +1265,22 @@ int x = INT_MAX; ranges[tree:lang()] = { tstree:root():range(true) } end) return ranges - ]], source) + ]], + source + ) eq({ lua = { 0, 6, 6, 16, 4, 438 }, query = { 6, 20, 113, 15, 6, 431 }, - vim = { 1, 0, 16, 4, 6, 89 } + vim = { 1, 0, 16, 4, 6, 89 }, }, r) -- The above ranges are provided directly from treesitter, however query directives may mutate -- the ranges but only provide a Range4. Strip the byte entries from the ranges and make sure -- add_bytes() produces the same result. - local rb = exec_lua([[ + local rb = exec_lua( + [[ local r, source = ... local add_bytes = require('vim.treesitter._range').add_bytes for lang, range in pairs(r) do @@ -954,13 +1288,15 @@ int x = INT_MAX; r[lang] = add_bytes(source, r[lang]) end return r - ]], r, source) + ]], + r, + source + ) eq(rb, r) - end) - it("does not produce empty injection ranges (#23409)", function() + it('does not produce empty injection ranges (#23409)', function() insert [[ Examples: >lua local a = {} @@ -977,7 +1313,7 @@ int x = INT_MAX; parser1:parse(true) ]] - eq(0, exec_lua("return #vim.tbl_keys(parser1:children())")) + eq(0, exec_lua('return #vim.tbl_keys(parser1:children())')) exec_lua [[ parser2 = require('vim.treesitter.languagetree').new(0, "vimdoc", { @@ -988,13 +1324,12 @@ int x = INT_MAX; parser2:parse(true) ]] - eq(1, exec_lua("return #vim.tbl_keys(parser2:children())")) - eq( { { { 1, 0, 21, 2, 0, 42 } } }, exec_lua("return parser2:children().lua:included_regions()")) - + eq(1, exec_lua('return #vim.tbl_keys(parser2:children())')) + eq({ { { 1, 0, 21, 2, 0, 42 } } }, exec_lua('return parser2:children().lua:included_regions()')) end) - it("parsers injections incrementally", function() - insert(dedent[[ + it('parsers injections incrementally', function() + insert(dedent [[ >lua local a = {} < @@ -1033,26 +1368,38 @@ int x = INT_MAX; ]] --- Do not parse injections by default - eq(0, exec_lua [[ + eq( + 0, + exec_lua [[ parser:parse() return #vim.tbl_keys(parser:children()) - ]]) + ]] + ) --- Only parse injections between lines 0, 2 - eq(1, exec_lua [[ + eq( + 1, + exec_lua [[ parser:parse({0, 2}) return #parser:children().lua:trees() - ]]) + ]] + ) - eq(2, exec_lua [[ + eq( + 2, + exec_lua [[ parser:parse({2, 6}) return #parser:children().lua:trees() - ]]) + ]] + ) - eq(7, exec_lua [[ + eq( + 7, + exec_lua [[ parser:parse(true) return #parser:children().lua:trees() - ]]) + ]] + ) end) it('fails to load queries', function() @@ -1062,43 +1409,48 @@ int x = INT_MAX; -- Invalid node type test( - '.../query.lua:0: Query error at 1:2. Invalid node type "dentifier":\n'.. - '(dentifier) @variable\n'.. - ' ^', - '(dentifier) @variable') + '.../query.lua:0: Query error at 1:2. Invalid node type "dentifier":\n' + .. '(dentifier) @variable\n' + .. ' ^', + '(dentifier) @variable' + ) -- Impossible pattern test( - '.../query.lua:0: Query error at 1:13. Impossible pattern:\n'.. - '(identifier (identifier) @variable)\n'.. - ' ^', - '(identifier (identifier) @variable)') + '.../query.lua:0: Query error at 1:13. Impossible pattern:\n' + .. '(identifier (identifier) @variable)\n' + .. ' ^', + '(identifier (identifier) @variable)' + ) -- Invalid syntax test( - '.../query.lua:0: Query error at 1:13. Invalid syntax:\n'.. - '(identifier @variable\n'.. - ' ^', - '(identifier @variable') + '.../query.lua:0: Query error at 1:13. Invalid syntax:\n' + .. '(identifier @variable\n' + .. ' ^', + '(identifier @variable' + ) -- Invalid field name test( - '.../query.lua:0: Query error at 1:15. Invalid field name "invalid_field":\n'.. - '((identifier) invalid_field: (identifier))\n'.. - ' ^', - '((identifier) invalid_field: (identifier))') + '.../query.lua:0: Query error at 1:15. Invalid field name "invalid_field":\n' + .. '((identifier) invalid_field: (identifier))\n' + .. ' ^', + '((identifier) invalid_field: (identifier))' + ) -- Invalid capture name test( - '.../query.lua:0: Query error at 3:2. Invalid capture name "ok.capture":\n'.. - '@ok.capture\n'.. - ' ^', - '((identifier) @id \n(#eq? @id\n@ok.capture\n))') + '.../query.lua:0: Query error at 3:2. Invalid capture name "ok.capture":\n' + .. '@ok.capture\n' + .. ' ^', + '((identifier) @id \n(#eq? @id\n@ok.capture\n))' + ) end) describe('is_valid()', function() before_each(function() - insert(dedent[[ + insert(dedent [[ Treesitter integration *treesitter* Nvim integrates the `tree-sitter` library for incremental parsing of buffers: @@ -1137,7 +1489,7 @@ int x = INT_MAX; describe('when adding content with injections', function() before_each(function() feed('G') - insert(dedent[[ + insert(dedent [[ >lua local a = {} < @@ -1156,23 +1508,29 @@ int x = INT_MAX; eq(false, exec_lua('return vim.treesitter.get_parser():is_valid()')) end) - it('is fully valid after a range parse that leads to parsing not parsed injections', function() - exec_lua('vim.treesitter.get_parser():parse({5, 7})') - eq(true, exec_lua('return vim.treesitter.get_parser():is_valid(true)')) - eq(true, exec_lua('return vim.treesitter.get_parser():is_valid()')) - end) - - it('is valid excluding, invalid including children after a range parse that does not lead to parsing not parsed injections', function() - exec_lua('vim.treesitter.get_parser():parse({2, 4})') - eq(true, exec_lua('return vim.treesitter.get_parser():is_valid(true)')) - eq(false, exec_lua('return vim.treesitter.get_parser():is_valid()')) - end) + it( + 'is fully valid after a range parse that leads to parsing not parsed injections', + function() + exec_lua('vim.treesitter.get_parser():parse({5, 7})') + eq(true, exec_lua('return vim.treesitter.get_parser():is_valid(true)')) + eq(true, exec_lua('return vim.treesitter.get_parser():is_valid()')) + end + ) + + it( + 'is valid excluding, invalid including children after a range parse that does not lead to parsing not parsed injections', + function() + exec_lua('vim.treesitter.get_parser():parse({2, 4})') + eq(true, exec_lua('return vim.treesitter.get_parser():is_valid(true)')) + eq(false, exec_lua('return vim.treesitter.get_parser():is_valid()')) + end + ) end) describe('when removing content with injections', function() before_each(function() feed('G') - insert(dedent[[ + insert(dedent [[ >lua local a = {} < @@ -1205,11 +1563,14 @@ int x = INT_MAX; eq(true, exec_lua('return vim.treesitter.get_parser():is_valid()')) end) - it('is valid excluding, invalid including children after a range parse that does not lead to parsing modified child tree', function() - exec_lua('vim.treesitter.get_parser():parse({2, 4})') - eq(true, exec_lua('return vim.treesitter.get_parser():is_valid(true)')) - eq(false, exec_lua('return vim.treesitter.get_parser():is_valid()')) - end) + it( + 'is valid excluding, invalid including children after a range parse that does not lead to parsing modified child tree', + function() + exec_lua('vim.treesitter.get_parser():parse({2, 4})') + eq(true, exec_lua('return vim.treesitter.get_parser():is_valid(true)')) + eq(false, exec_lua('return vim.treesitter.get_parser():is_valid()')) + end + ) end) end) end) diff --git a/test/functional/treesitter/utils_spec.lua b/test/functional/treesitter/utils_spec.lua index 9c07959098..2734c22499 100644 --- a/test/functional/treesitter/utils_spec.lua +++ b/test/functional/treesitter/utils_spec.lua @@ -11,7 +11,6 @@ describe('treesitter utils', function() before_each(clear) it('can find an ancestor', function() - insert([[ int main() { int x = 3; |