aboutsummaryrefslogtreecommitdiff
path: root/test/functional/treesitter
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
committerJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
commitd5f194ce780c95821a855aca3c19426576d28ae0 (patch)
treed45f461b19f9118ad2bb1f440a7a08973ad18832 /test/functional/treesitter
parentc5d770d311841ea5230426cc4c868e8db27300a8 (diff)
parent44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff)
downloadrneovim-rahm.tar.gz
rneovim-rahm.tar.bz2
rneovim-rahm.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309HEADrahm
Diffstat (limited to 'test/functional/treesitter')
-rw-r--r--test/functional/treesitter/fold_spec.lua76
-rw-r--r--test/functional/treesitter/highlight_spec.lua823
-rw-r--r--test/functional/treesitter/inspect_tree_spec.lua86
-rw-r--r--test/functional/treesitter/language_spec.lua4
-rw-r--r--test/functional/treesitter/node_spec.lua28
-rw-r--r--test/functional/treesitter/parser_spec.lua347
-rw-r--r--test/functional/treesitter/query_spec.lua69
-rw-r--r--test/functional/treesitter/testutil.lua25
8 files changed, 974 insertions, 484 deletions
diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua
index e38e58ff92..ac58df4bba 100644
--- a/test/functional/treesitter/fold_spec.lua
+++ b/test/functional/treesitter/fold_spec.lua
@@ -5,6 +5,7 @@ local Screen = require('test.functional.ui.screen')
local clear = n.clear
local eq = t.eq
local insert = n.insert
+local write_file = t.write_file
local exec_lua = n.exec_lua
local command = n.command
local feed = n.feed
@@ -767,4 +768,79 @@ t2]])
]],
}
end)
+
+ it("doesn't call get_parser too often when parser is not available", function()
+ -- spy on vim.treesitter.get_parser() to keep track of how many times it is called
+ exec_lua(function()
+ _G.count = 0
+ vim.treesitter.get_parser = (function(wrapped)
+ return function(...)
+ _G.count = _G.count + 1
+ return wrapped(...)
+ end
+ end)(vim.treesitter.get_parser)
+ end)
+
+ insert(test_text)
+ command [[
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ eq(
+ 1,
+ exec_lua [[ return _G.count ]],
+ 'count should not be as high as the # of lines; actually only once for the buffer.'
+ )
+ end)
+
+ it('can detect a new parser and refresh folds accordingly', function()
+ local name = t.tmpname()
+ write_file(name, test_text)
+ command('edit ' .. name)
+ command [[
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ -- reload buffer as c filetype to simulate new parser being found
+ feed('GA// vim: ft=c<Esc>')
+ command([[write | edit]])
+
+ eq({
+ [1] = '>1',
+ [2] = '1',
+ [3] = '1',
+ [4] = '1',
+ [5] = '>2',
+ [6] = '2',
+ [7] = '2',
+ [8] = '1',
+ [9] = '1',
+ [10] = '>2',
+ [11] = '2',
+ [12] = '2',
+ [13] = '2',
+ [14] = '2',
+ [15] = '>3',
+ [16] = '3',
+ [17] = '3',
+ [18] = '2',
+ [19] = '1',
+ }, get_fold_levels())
+ end)
end)
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index 5c6be869c6..7f0a3cb342 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -12,6 +12,7 @@ local fn = n.fn
local eq = t.eq
local hl_query_c = [[
+ ; query
(ERROR) @error
"if" @keyword
@@ -65,40 +66,40 @@ static int nlua_schedule(lua_State *const lstate)
}]]
local hl_grid_legacy_c = [[
- {2:^/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} nlua_schedule(lua_State *{3:const} lstate) |
+ {18:^/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} nlua_schedule(lua_State *{6:const} lstate) |
{ |
- {4:if} (lua_type(lstate, {5:1}) != LUA_TFUNCTION |
+ {15:if} (lua_type(lstate, {26:1}) != LUA_TFUNCTION |
|| lstate != lstate) { |
- lua_pushliteral(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} lua_error(lstate); |
+ lua_pushliteral(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} lua_error(lstate); |
} |
|
- LuaRef cb = nlua_ref(lstate, {5:1}); |
+ LuaRef cb = nlua_ref(lstate, {26:1}); |
|
multiqueue_put(main_loop.events, nlua_schedule_event, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
} |
{1:~ }|*2
|
]]
local hl_grid_ts_c = [[
- {2:^/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
+ {18:^/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
{ |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
} |
|
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
|
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
} |
{1:~ }|*2
|
@@ -145,10 +146,10 @@ local injection_grid_c = [[
]]
local injection_grid_expected_c = [[
- {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}; \ |
+ {6:int} x = {26:INT_MAX}; |
+ #define {26:READ_STRING}(x, y) ({6:char} *)read_string((x), ({6:size_t})(y)) |
+ #define foo {6:void} main() { \ |
+ {15:return} {26:42}; \ |
} |
^ |
{1:~ }|*11
@@ -161,20 +162,6 @@ describe('treesitter highlighting (C)', function()
before_each(function()
clear()
screen = Screen.new(65, 18)
- 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 },
- }
-
command [[ hi link @error ErrorMsg ]]
command [[ hi link @warning WarningMsg ]]
end)
@@ -246,124 +233,124 @@ describe('treesitter highlighting (C)', function()
feed('5Goc<esc>dd')
- screen:expect {
+ screen:expect({
grid = [[
- {2:/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- } |
- {1:~ }|*2
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:^lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|*2
+ |
+ ]],
+ })
feed('7Go*/<esc>')
- screen:expect {
+ screen:expect({
grid = [[
- {2:/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
- {8:*^/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- } |
- {1:~ }|
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ {9:*^/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|
+ |
+ ]],
+ })
feed('3Go/*<esc>')
- screen:expect {
+ 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) |
- { |
- {2:/^*} |
- {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
- {2: || lstate != lstate) {} |
- {2: lua_pushliteral(lstate, "vim.schedule: expected function");} |
- {2: return lua_error(lstate);} |
- {2:*/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- {8:}} |
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {18:/^*} |
+ {18: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
+ {18: || lstate != lstate) {} |
+ {18: lua_pushliteral(lstate, "vim.schedule: expected function");} |
+ {18: return lua_error(lstate);} |
+ {18:*/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ {9:}} |
+ |
+ ]],
+ })
feed('gg$')
feed('~')
- screen:expect {
+ 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) |
- { |
- {2:/*} |
- {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
- {2: || lstate != lstate) {} |
- {2: lua_pushliteral(lstate, "vim.schedule: expected function");} |
- {2: return lua_error(lstate);} |
- {2:*/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- {8:}} |
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queu^E} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {18:/*} |
+ {18: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
+ {18: || lstate != lstate) {} |
+ {18: lua_pushliteral(lstate, "vim.schedule: expected function");} |
+ {18: return lua_error(lstate);} |
+ {18:*/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ {9:}} |
+ |
+ ]],
+ })
feed('re')
- screen:expect {
+ 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) |
- { |
- {2:/*} |
- {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
- {2: || lstate != lstate) {} |
- {2: lua_pushliteral(lstate, "vim.schedule: expected function");} |
- {2: return lua_error(lstate);} |
- {2:*/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- {8:}} |
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queu^e} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {18:/*} |
+ {18: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
+ {18: || lstate != lstate) {} |
+ {18: lua_pushliteral(lstate, "vim.schedule: expected function");} |
+ {18: return lua_error(lstate);} |
+ {18:*/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ {9:}} |
+ |
+ ]],
+ })
end)
it('is updated with :sort', function()
@@ -372,83 +359,79 @@ describe('treesitter highlighting (C)', function()
local parser = vim.treesitter.get_parser(0, 'c')
vim.treesitter.highlighter.new(parser, { queries = { c = hl_query_c } })
end)
- screen:expect {
+ 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++) { |
- ext_widgets[i] = true; |
- } |
- |
- {3:bool} inclusive = ui_override(); |
- {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { |
- {3:UI} *ui = uis[i]; |
- width = {5:MIN}(ui->width, width); |
- height = {5:MIN}(ui->height, height); |
- foo = {5:BAR}(ui->bazaar, bazaar); |
- {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- } |
- } |
- ^} |
- |
- ]],
- }
+ {6:int} width = {26:INT_MAX}, height = {26:INT_MAX}; |
+ {6:bool} ext_widgets[kUIExtCount]; |
+ {15:for} ({6:UIExtension} i = {26:0}; ({6:int})i < kUIExtCount; i++) { |
+ ext_widgets[i] = true; |
+ } |
+ |
+ {6:bool} inclusive = ui_override(); |
+ {15:for} ({6:size_t} i = {26:0}; i < ui_count; i++) { |
+ {6:UI} *ui = uis[i]; |
+ width = {26:MIN}(ui->width, width); |
+ height = {26:MIN}(ui->height, height); |
+ foo = {26:BAR}(ui->bazaar, bazaar); |
+ {15:for} ({6:UIExtension} j = {26:0}; ({6:int})j < kUIExtCount; j++) { |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ } |
+ } |
+ ^} |
+ |
+ ]],
+ })
feed ':sort<cr>'
- screen:expect {
+ screen:expect({
grid = [[
- ^ |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- {3:UI} *ui = uis[i]; |
- ext_widgets[i] = true; |
- foo = {5:BAR}(ui->bazaar, bazaar); |
- {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { |
- height = {5:MIN}(ui->height, height); |
- width = {5:MIN}(ui->width, width); |
- } |
- {3:bool} ext_widgets[kUIExtCount]; |
- {3:bool} inclusive = ui_override(); |
- {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 |
- ]],
- }
+ ^ |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {6:UI} *ui = uis[i]; |
+ ext_widgets[i] = true; |
+ foo = {26:BAR}(ui->bazaar, bazaar); |
+ {15:for} ({6:UIExtension} j = {26:0}; ({6:int})j < kUIExtCount; j++) { |
+ height = {26:MIN}(ui->height, height); |
+ width = {26:MIN}(ui->width, width); |
+ } |
+ {6:bool} ext_widgets[kUIExtCount]; |
+ {6:bool} inclusive = ui_override(); |
+ {15:for} ({6:UIExtension} i = {26:0}; ({6:int})i < kUIExtCount; i++) { |
+ {15:for} ({6:size_t} i = {26:0}; i < ui_count; i++) { |
+ {6:int} width = {26:INT_MAX}, height = {26:INT_MAX}; |
+ } |*2
+ {6:void} ui_refresh({6:void}) |
+ :sort |
+ ]],
+ })
- feed 'u'
+ feed 'u:<esc>'
- screen:expect {
+ 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++) { |
- ext_widgets[i] = true; |
- } |
- |
- {3:bool} inclusive = ui_override(); |
- {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { |
- {3:UI} *ui = uis[i]; |
- width = {5:MIN}(ui->width, width); |
- height = {5:MIN}(ui->height, height); |
- foo = {5:BAR}(ui->bazaar, bazaar); |
- {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- } |
- } |
- ^} |
- 19 changes; before #2 {MATCH:.*}|
- ]],
- }
+ {6:int} width = {26:INT_MAX}, height = {26:INT_MAX}; |
+ {6:bool} ext_widgets[kUIExtCount]; |
+ {15:for} ({6:UIExtension} i = {26:0}; ({6:int})i < kUIExtCount; i++) { |
+ ext_widgets[i] = true; |
+ } |
+ |
+ {6:bool} inclusive = ui_override(); |
+ {15:for} ({6:size_t} i = {26:0}; i < ui_count; i++) { |
+ {6:UI} *ui = uis[i]; |
+ width = {26:MIN}(ui->width, width); |
+ height = {26:MIN}(ui->height, height); |
+ foo = {26:BAR}(ui->bazaar, bazaar); |
+ {15:for} ({6:UIExtension} j = {26:0}; ({6:int})j < kUIExtCount; j++) { |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ } |
+ } |
+ ^} |
+ |
+ ]],
+ })
end)
it('supports with custom parser', function()
- screen:set_default_attr_ids {
- [1] = { bold = true, foreground = Screen.colors.SeaGreen4 },
- }
-
insert(test_text_c)
screen:expect {
@@ -488,28 +471,28 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(parser, { queries = { c = '(identifier) @type' } })
end)
- screen:expect {
+ screen:expect({
grid = [[
- int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; |
- bool {1:ext_widgets}[{1:kUIExtCount}]; |
- for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { |
- ext_widgets[i] = true; |
- } |
- |
- bool {1:inclusive} = {1:ui_override}(); |
- for (size_t {1:i} = 0; i < ui_count; i++) { |
- UI *{1:ui} = {1:uis}[{1:i}]; |
- width = MIN(ui->width, width); |
- height = MIN(ui->height, height); |
- foo = BAR(ui->bazaar, bazaar); |
- for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- } |
- } |
- ^} |
- |
- ]],
- }
+ int {6:width} = {6:INT_MAX}, {6:height} = {6:INT_MAX}; |
+ bool {6:ext_widgets}[{6:kUIExtCount}]; |
+ for (UIExtension {6:i} = 0; (int)i < kUIExtCount; i++) { |
+ ext_widgets[i] = true; |
+ } |
+ |
+ bool {6:inclusive} = {6:ui_override}(); |
+ for (size_t {6:i} = 0; i < ui_count; i++) { |
+ UI *{6:ui} = {6:uis}[{6:i}]; |
+ width = MIN(ui->width, width); |
+ height = MIN(ui->height, height); |
+ foo = BAR(ui->bazaar, bazaar); |
+ for (UIExtension {6:j} = 0; (int)j < kUIExtCount; j++) { |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ } |
+ } |
+ ^} |
+ |
+ ]],
+ })
end)
it('supports injected languages', function()
@@ -567,18 +550,18 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
end)
- screen:expect {
+ 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:~ }|*11
- |
- ]],
- }
+ {6:int} x = {26:INT_MAX}; |
+ #define {26:READ_STRING}(x, y) ({6:char} *)read_string((x), ({6:size_t})(y)) |
+ #define foo {6:void} main() { \ |
+ {15:return} {26:42}; \ |
+ } |
+ ^ |
+ {1:~ }|*11
+ |
+ ]],
+ })
end)
it('supports highlighting with custom highlight groups', function()
@@ -595,27 +578,27 @@ describe('treesitter highlighting (C)', function()
-- 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 {
+ screen:expect({
grid = [[
- {2:^/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:lua_pushliteral}(lstate, {2:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- } |
- {1:~ }|*2
- |
- ]],
- }
+ {18:^/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {18:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|*2
+ |
+ ]],
+ })
screen:expect { unchanged = true }
end)
@@ -657,8 +640,8 @@ describe('treesitter highlighting (C)', function()
}
eq({
- { capture = 'constant', metadata = { priority = '101' }, lang = 'c' },
- { capture = 'type', metadata = {}, lang = 'c' },
+ { capture = 'constant', metadata = { priority = '101' }, lang = 'c', id = 14 },
+ { capture = 'type', metadata = {}, lang = 'c', id = 3 },
}, exec_lua [[ return vim.treesitter.get_captures_at_pos(0, 0, 2) ]])
end)
@@ -691,25 +674,25 @@ describe('treesitter highlighting (C)', function()
)
end)
- screen:expect {
+ screen:expect({
grid = [[
- {3:char}* x = {5:"Will somebody ever read this?"}; |
- ^ |
- {1:~ }|*15
- |
- ]],
- }
+ {6:char}* x = {26:"Will somebody ever read this?"}; |
+ ^ |
+ {1:~ }|*15
+ |
+ ]],
+ })
-- clearing specialization reactivates fallback
command [[ hi clear @foo.bar ]]
- screen:expect {
+ screen:expect({
grid = [[
- {5:char}* x = {5:"Will somebody ever read this?"}; |
- ^ |
- {1:~ }|*15
- |
- ]],
- }
+ {26:char}* x = {26:"Will somebody ever read this?"}; |
+ ^ |
+ {1:~ }|*15
+ |
+ ]],
+ })
end
)
@@ -740,27 +723,27 @@ describe('treesitter highlighting (C)', function()
})
end)
- screen:expect {
+ screen:expect({
grid = [[
- /// Schedule Lua callback on main loop's event queue |
- {4:R} int nlua_schedule(lua_State *const ) |
- { |
- if (lua_type(, 1) != LUA_TFUNCTION |
- || != ) { |
- lua_pushliteral(, "vim.schedule: expected function"); |
- return lua_error(); |
- } |
- |
- LuaRef cb = nlua_ref(, 1); |
- |
- {11:V}(main_loop.events, nlua_schedule_event, |
- 1, (void *)(ptrdiff_t)cb); |
- return 0; |
- ^} |
- {1:~ }|*2
- |
- ]],
- }
+ /// Schedule Lua callback on main loop's event queue |
+ {15:R} int nlua_schedule(lua_State *const ) |
+ { |
+ if (lua_type(, 1) != LUA_TFUNCTION |
+ || != ) { |
+ lua_pushliteral(, "vim.schedule: expected function"); |
+ return lua_error(); |
+ } |
+ |
+ LuaRef cb = nlua_ref(, 1); |
+ |
+ {25:V}(main_loop.events, nlua_schedule_event, |
+ 1, (void *)(ptrdiff_t)cb); |
+ return 0; |
+ ^} |
+ {1:~ }|*2
+ |
+ ]],
+ })
end)
it('@foo.bar groups has the correct fallback behavior', function()
@@ -801,16 +784,16 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
end)
- screen:expect {
+ screen:expect({
grid = [[
- {5:int x = 4;} |
- {5:int y = 5;} |
- {5:int z = 6;} |
- ^ |
- {1:~ }|*13
- |
- ]],
- }
+ {26:int x = 4;} |
+ {26:int y = 5;} |
+ {26:int z = 6;} |
+ ^ |
+ {1:~ }|*13
+ |
+ ]],
+ })
end)
it('gives higher priority to more specific captures #27895', function()
@@ -830,14 +813,52 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
end)
- screen:expect {
+ screen:expect({
grid = [[
- void foo(int {4:*}{11:bar}); |
- ^ |
- {1:~ }|*15
- |
- ]],
- }
+ void foo(int {15:*}{25:bar}); |
+ ^ |
+ {1:~ }|*15
+ |
+ ]],
+ })
+ end)
+
+ it('highlights applied to first line of closed fold', function()
+ insert(hl_text_c)
+ exec_lua(function()
+ vim.treesitter.query.set('c', 'highlights', hl_query_c)
+ vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
+ end)
+ feed('ggjzfj')
+ command('set foldtext=')
+ screen:add_extra_attr_ids({
+ [100] = {
+ bold = true,
+ background = Screen.colors.LightGray,
+ foreground = Screen.colors.SeaGreen4,
+ },
+ [101] = { background = Screen.colors.LightGray, foreground = Screen.colors.DarkCyan },
+ })
+ screen:expect({
+ grid = [[
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {100:^static}{13: }{100:int}{13: }{101:nlua_schedule}{13:(}{100:lua_State}{13: *}{100:const}{13: lstate)················}|
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|*3
+ |
+ ]],
+ })
end)
end)
@@ -847,13 +868,6 @@ describe('treesitter highlighting (lua)', function()
before_each(function()
clear()
screen = Screen.new(65, 18)
- screen:set_default_attr_ids {
- [1] = { bold = true, foreground = Screen.colors.Blue },
- [2] = { foreground = Screen.colors.DarkCyan },
- [3] = { foreground = Screen.colors.Magenta },
- [4] = { foreground = Screen.colors.SlateBlue },
- [5] = { bold = true, foreground = Screen.colors.Brown },
- }
end)
it('supports language injections', function()
@@ -867,15 +881,15 @@ describe('treesitter highlighting (lua)', function()
vim.treesitter.start()
end)
- screen:expect {
+ screen:expect({
grid = [[
- {5:local} {2:ffi} {5:=} {4:require(}{3:'ffi'}{4:)} |
- {2:ffi}{4:.}{2:cdef}{4:(}{3:"}{4:int}{3: }{4:(}{5:*}{3:fun}{4:)(int,}{3: }{4:char}{3: }{5:*}{4:);}{3:"}{4:)} |
- ^ |
- {1:~ }|*14
- |
- ]],
- }
+ {15:local} {25:ffi} {15:=} {16:require(}{26:'ffi'}{16:)} |
+ {25:ffi}{16:.}{25:cdef}{16:(}{26:"}{16:int}{26: }{16:(}{15:*}{26:fun}{16:)(int,}{26: }{16:char}{26: }{15:*}{16:);}{26:"}{16:)} |
+ ^ |
+ {1:~ }|*14
+ |
+ ]],
+ })
end)
end)
@@ -885,16 +899,6 @@ describe('treesitter highlighting (help)', function()
before_each(function()
clear()
screen = Screen.new(40, 6)
- 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 },
- title = { bold = true, foreground = Screen.colors.Magenta1 },
- h1_delim = { nocombine = true, underdouble = true },
- h2_delim = { nocombine = true, underline = true },
- }
end)
it('defaults in vimdoc/highlights.scm', function()
@@ -918,13 +922,18 @@ describe('treesitter highlighting (help)', function()
vim.treesitter.start()
end)
+ screen:add_extra_attr_ids({
+ [100] = { nocombine = true, underdouble = true },
+ [101] = { foreground = Screen.colors.Fuchsia, bold = true },
+ [102] = { underline = true, nocombine = true },
+ })
screen:expect({
grid = [[
- {h1_delim:^========================================}|
- {title:NVIM DOCUMENTATION} |
+ {100:^========================================}|
+ {101:NVIM DOCUMENTATION} |
|
- {h2_delim:----------------------------------------}|
- {title:ABOUT NVIM} |
+ {102:----------------------------------------}|
+ {101:ABOUT NVIM} |
|
]],
})
@@ -943,42 +952,42 @@ describe('treesitter highlighting (help)', function()
vim.treesitter.start()
end)
- screen:expect {
+ screen:expect({
grid = [[
- {1:>}{3:ruby} |
- {1: -- comment} |
- {1: local this_is = 'actually_lua'} |
- {1:<} |
- ^ |
- |
- ]],
- }
+ {18:>}{15:ruby} |
+ {18: -- comment} |
+ {18: local this_is = 'actually_lua'} |
+ {18:<} |
+ ^ |
+ |
+ ]],
+ })
n.api.nvim_buf_set_text(0, 0, 1, 0, 5, { 'lua' })
- screen:expect {
+ screen:expect({
grid = [[
- {1:>}{3:lua} |
- {1: -- comment} |
- {1: }{3:local}{1: }{4:this_is}{1: }{3:=}{1: }{5:'actually_lua'} |
- {1:<} |
- ^ |
- |
- ]],
- }
+ {18:>}{15:lua} |
+ {18: -- comment} |
+ {18: }{15:local}{18: }{25:this_is}{18: }{15:=}{18: }{26:'actually_lua'} |
+ {18:<} |
+ ^ |
+ |
+ ]],
+ })
n.api.nvim_buf_set_text(0, 0, 1, 0, 4, { 'ruby' })
- screen:expect {
+ screen:expect({
grid = [[
- {1:>}{3:ruby} |
- {1: -- comment} |
- {1: local this_is = 'actually_lua'} |
- {1:<} |
- ^ |
- |
- ]],
- }
+ {18:>}{15:ruby} |
+ {18: -- comment} |
+ {18: local this_is = 'actually_lua'} |
+ {18:<} |
+ ^ |
+ |
+ ]],
+ })
end)
it('correctly redraws injections subpriorities', function()
@@ -1003,16 +1012,16 @@ describe('treesitter highlighting (help)', function()
vim.treesitter.highlighter.new(parser)
end)
- screen:expect {
+ screen:expect({
grid = [=[
- {3:local} {4:s} {3:=} {5:[[} |
- {5: }{3:local}{5: }{4:also}{5: }{3:=}{5: }{4:lua} |
- {5:]]} |
- ^ |
- {2:~ }|
- |
- ]=],
- }
+ {15:local} {25:s} {15:=} {26:[[} |
+ {26: }{15:local}{26: }{25:also}{26: }{15:=}{26: }{25:lua} |
+ {26:]]} |
+ ^ |
+ {1:~ }|
+ |
+ ]=],
+ })
end)
end)
@@ -1022,12 +1031,6 @@ describe('treesitter highlighting (nested injections)', function()
before_each(function()
clear()
screen = Screen.new(80, 7)
- 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 },
- }
end)
it('correctly redraws nested injections (GitHub #25252)', function()
@@ -1054,32 +1057,32 @@ vim.cmd([[
-- invalidate the language tree
feed('ggi--[[<ESC>04x')
- screen:expect {
+ screen:expect({
grid = [[
- {2:^function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
- |
- {2:local} {3:lorem} {2:=} {1:{} |
- {3:ipsum} {2:=} {1:{},} |
- {3:bar} {2:=} {1:{},} |
- {1:}} |
- |
- ]],
- }
+ {15:^function} {25:foo}{16:()} {16:print(}{26:"Lua!"}{16:)} {15:end} |
+ |
+ {15:local} {25:lorem} {15:=} {16:{} |
+ {25:ipsum} {15:=} {16:{},} |
+ {25:bar} {15:=} {16:{},} |
+ {16:}} |
+ |
+ ]],
+ })
-- spam newline insert/delete to invalidate Lua > Vim > Lua region
feed('3jo<ESC>ddko<ESC>ddko<ESC>ddko<ESC>ddk0')
- screen:expect {
+ screen:expect({
grid = [[
- {2:function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
- |
- {2:local} {3:lorem} {2:=} {1:{} |
- ^ {3:ipsum} {2:=} {1:{},} |
- {3:bar} {2:=} {1:{},} |
- {1:}} |
- |
- ]],
- }
+ {15:function} {25:foo}{16:()} {16:print(}{26:"Lua!"}{16:)} {15:end} |
+ |
+ {15:local} {25:lorem} {15:=} {16:{} |
+ ^ {25:ipsum} {15:=} {16:{},} |
+ {25:bar} {15:=} {16:{},} |
+ {16:}} |
+ |
+ ]],
+ })
end)
end)
@@ -1108,7 +1111,7 @@ describe('treesitter highlighting (markdown)', function()
})
screen:expect({
grid = [[
- {25:[}{100:This link text}{25:](}{101:https://example.com}{25:)} is|
+ {100:[This link text](}{101:https://example.com}{100:)} is|
a hyperlink^. |
{1:~ }|*3
|
@@ -1156,20 +1159,6 @@ it('starting and stopping treesitter highlight in init.lua works #29541', functi
eq('', api.nvim_get_vvar('errmsg'))
local screen = Screen.new(65, 18)
- 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 },
- }
-
fn.setreg('r', hl_text_c)
feed('i<C-R><C-O>r<Esc>gg')
-- legacy syntax highlighting is used
diff --git a/test/functional/treesitter/inspect_tree_spec.lua b/test/functional/treesitter/inspect_tree_spec.lua
index 1f7d15cc96..68622140e4 100644
--- a/test/functional/treesitter/inspect_tree_spec.lua
+++ b/test/functional/treesitter/inspect_tree_spec.lua
@@ -120,14 +120,17 @@ describe('vim.treesitter.inspect_tree', function()
end)
it('updates source and tree buffer windows and closes them correctly', function()
+ local name = t.tmpname()
+ n.command('edit ' .. name)
insert([[
print()
]])
+ n.command('set filetype=lua | write')
-- setup two windows for the source buffer
exec_lua(function()
_G.source_win = vim.api.nvim_get_current_win()
- vim.api.nvim_open_win(0, false, {
+ _G.source_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
@@ -135,40 +138,103 @@ describe('vim.treesitter.inspect_tree', function()
-- setup three windows for the tree buffer
exec_lua(function()
- vim.treesitter.start(0, 'lua')
vim.treesitter.inspect_tree()
_G.tree_win = vim.api.nvim_get_current_win()
- _G.tree_win_copy_1 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
- _G.tree_win_copy_2 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win3 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
end)
- -- close original source window
- exec_lua('vim.api.nvim_win_close(source_win, false)')
+ -- close original source window without closing tree views
+ exec_lua('vim.api.nvim_set_current_win(source_win)')
+ feed(':quit<CR>')
+ eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win2)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win3)'))
-- navigates correctly to the remaining source buffer window
+ exec_lua('vim.api.nvim_set_current_win(tree_win)')
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close original tree window
exec_lua(function()
- vim.api.nvim_set_current_win(_G.tree_win_copy_1)
+ vim.api.nvim_set_current_win(_G.tree_win2)
vim.api.nvim_win_close(_G.tree_win, false)
end)
-- navigates correctly to the remaining source buffer window
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close source buffer window and all remaining tree windows
- t.pcall_err(exec_lua, 'vim.api.nvim_win_close(0, false)')
+ n.expect_exit(n.command, 'quit')
+ end)
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_1)'))
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_2)'))
+ it('shows which nodes are missing', function()
+ insert([[
+ int main() {
+ if (a.) {
+ // ^ MISSING field_identifier here
+ if (1) d()
+ // ^ MISSING ";" here
+ }
+ }
+ ]])
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'c')
+ vim.treesitter.inspect_tree()
+ end)
+ feed('a')
+
+ expect_tree [[
+ (translation_unit ; [0, 0] - [8, 0]
+ (function_definition ; [0, 0] - [6, 1]
+ type: (primitive_type) ; [0, 0] - [0, 3]
+ declarator: (function_declarator ; [0, 4] - [0, 10]
+ declarator: (identifier) ; [0, 4] - [0, 8]
+ parameters: (parameter_list ; [0, 8] - [0, 10]
+ "(" ; [0, 8] - [0, 9]
+ ")")) ; [0, 9] - [0, 10]
+ body: (compound_statement ; [0, 11] - [6, 1]
+ "{" ; [0, 11] - [0, 12]
+ (if_statement ; [1, 4] - [5, 5]
+ "if" ; [1, 4] - [1, 6]
+ condition: (parenthesized_expression ; [1, 7] - [1, 11]
+ "(" ; [1, 7] - [1, 8]
+ (field_expression ; [1, 8] - [1, 10]
+ argument: (identifier) ; [1, 8] - [1, 9]
+ operator: "." ; [1, 9] - [1, 10]
+ field: (MISSING field_identifier)) ; [1, 10] - [1, 10]
+ ")") ; [1, 10] - [1, 11]
+ consequence: (compound_statement ; [1, 12] - [5, 5]
+ "{" ; [1, 12] - [1, 13]
+ (comment) ; [2, 4] - [2, 41]
+ (if_statement ; [3, 8] - [4, 36]
+ "if" ; [3, 8] - [3, 10]
+ condition: (parenthesized_expression ; [3, 11] - [3, 14]
+ "(" ; [3, 11] - [3, 12]
+ (number_literal) ; [3, 12] - [3, 13]
+ ")") ; [3, 13] - [3, 14]
+ consequence: (expression_statement ; [3, 15] - [4, 36]
+ (call_expression ; [3, 15] - [3, 18]
+ function: (identifier) ; [3, 15] - [3, 16]
+ arguments: (argument_list ; [3, 16] - [3, 18]
+ "(" ; [3, 16] - [3, 17]
+ ")")) ; [3, 17] - [3, 18]
+ (comment) ; [4, 8] - [4, 36]
+ (MISSING ";"))) ; [4, 36] - [4, 36]
+ "}")) ; [5, 4] - [5, 5]
+ "}"))) ; [6, 0] - [6, 1]
+ ]]
end)
end)
diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua
index 120a15d7f9..a93b1063a1 100644
--- a/test/functional/treesitter/language_spec.lua
+++ b/test/functional/treesitter/language_spec.lua
@@ -117,6 +117,7 @@ describe('treesitter language API', function()
'<node translation_unit>',
exec_lua(function()
local langtree = vim.treesitter.get_parser(0, 'c')
+ langtree:parse()
local tree = langtree:tree_for_range({ 1, 3, 1, 3 })
return tostring(tree:root())
end)
@@ -133,6 +134,7 @@ describe('treesitter language API', function()
'<node translation_unit>',
exec_lua(function()
local langtree = vim.treesitter.get_parser(0, 'c')
+ langtree:parse()
local tree = langtree:tree_for_range({ 10, 10, 10, 10 })
return tostring(tree:root())
end)
@@ -149,6 +151,7 @@ describe('treesitter language API', function()
'<node primitive_type>',
exec_lua(function()
local langtree = vim.treesitter.get_parser(0, 'c')
+ langtree:parse()
local node = langtree:named_node_for_range({ 1, 3, 1, 3 })
return tostring(node)
end)
@@ -160,6 +163,7 @@ describe('treesitter language API', function()
exec_lua(function()
_G.langtree = vim.treesitter.get_parser(0, 'lua')
+ _G.langtree:parse()
_G.node = _G.langtree:node_for_range({ 0, 3, 0, 3 })
end)
diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua
index c87a56b160..235bf7861c 100644
--- a/test/functional/treesitter/node_spec.lua
+++ b/test/functional/treesitter/node_spec.lua
@@ -20,6 +20,7 @@ describe('treesitter node API', function()
insert('F')
exec_lua(function()
vim.treesitter.start(0, 'lua')
+ vim.treesitter.get_parser(0):parse()
vim.treesitter.get_node():tree()
vim.treesitter.get_node():tree()
collectgarbage()
@@ -45,6 +46,7 @@ describe('treesitter node API', function()
-- this buffer doesn't have filetype set!
insert('local foo = function() end')
exec_lua(function()
+ vim.treesitter.get_parser(0, 'lua'):parse()
_G.node = vim.treesitter.get_node({
bufnr = 0,
pos = { 0, 6 }, -- on "foo"
@@ -161,32 +163,6 @@ describe('treesitter node API', function()
eq(3, lua_eval('child:byte_length()'))
end)
- it('child_containing_descendant() works', function()
- insert([[
- int main() {
- int x = 3;
- }]])
-
- exec_lua(function()
- local tree = vim.treesitter.get_parser(0, 'c'):parse()[1]
- _G.root = tree:root()
- _G.main = _G.root:child(0)
- _G.body = _G.main:child(2)
- _G.statement = _G.body:child(1)
- _G.declarator = _G.statement:child(1)
- _G.value = _G.declarator:child(1)
- end)
-
- eq(lua_eval('main:type()'), lua_eval('root:child_containing_descendant(value):type()'))
- eq(lua_eval('body:type()'), lua_eval('main:child_containing_descendant(value):type()'))
- eq(lua_eval('statement:type()'), lua_eval('body:child_containing_descendant(value):type()'))
- eq(
- lua_eval('declarator:type()'),
- lua_eval('statement:child_containing_descendant(value):type()')
- )
- eq(vim.NIL, lua_eval('declarator:child_containing_descendant(value)'))
- end)
-
it('child_with_descendant() works', function()
insert([[
int main() {
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)
diff --git a/test/functional/treesitter/query_spec.lua b/test/functional/treesitter/query_spec.lua
index 634f8af83d..6db0ffe5a0 100644
--- a/test/functional/treesitter/query_spec.lua
+++ b/test/functional/treesitter/query_spec.lua
@@ -86,7 +86,7 @@ void ui_refresh(void)
local before = vim.api.nvim__stats().ts_query_parse_count
collectgarbage('stop')
for _ = 1, _n, 1 do
- vim.treesitter.query.parse('c', long_query, _n)
+ vim.treesitter.query.parse('c', long_query)
end
collectgarbage('restart')
collectgarbage('collect')
@@ -96,8 +96,39 @@ void ui_refresh(void)
end
eq(1, q(1))
- -- cache is cleared by garbage collection even if valid "cquery" reference is kept around
- eq(1, q(100))
+ -- cache is retained even after garbage collection
+ eq(0, q(100))
+ end)
+
+ it('cache is cleared upon runtimepath changes, or setting query manually', function()
+ ---@return number
+ exec_lua(function()
+ _G.query_parse_count = _G.query_parse_count or 0
+ local parse = vim.treesitter.query.parse
+ vim.treesitter.query.parse = function(...)
+ _G.query_parse_count = _G.query_parse_count + 1
+ return parse(...)
+ end
+ end)
+
+ local function q(_n)
+ return exec_lua(function()
+ for _ = 1, _n, 1 do
+ vim.treesitter.query.get('c', 'highlights')
+ end
+ return _G.query_parse_count
+ end)
+ end
+
+ eq(1, q(10))
+ exec_lua(function()
+ vim.opt.rtp:prepend('/another/dir')
+ end)
+ eq(2, q(100))
+ exec_lua(function()
+ vim.treesitter.query.set('c', 'highlights', [[; test]])
+ end)
+ eq(3, q(100))
end)
it('supports query and iter by capture (iter_captures)', function()
@@ -781,6 +812,34 @@ void ui_refresh(void)
)
end)
+ it('supports "; extends" modeline in custom queries', function()
+ insert('int zeero = 0;')
+ local result = exec_lua(function()
+ vim.treesitter.query.set(
+ 'c',
+ 'highlights',
+ [[; extends
+ (identifier) @spell]]
+ )
+ local query = vim.treesitter.query.get('c', 'highlights')
+ local parser = vim.treesitter.get_parser(0, 'c')
+ local root = parser:parse()[1]:root()
+ local res = {}
+ for id, node in query:iter_captures(root, 0) do
+ table.insert(res, { query.captures[id], vim.treesitter.get_node_text(node, 0) })
+ end
+ return res
+ end)
+ eq({
+ { 'type.builtin', 'int' },
+ { 'variable', 'zeero' },
+ { 'spell', 'zeero' },
+ { 'operator', '=' },
+ { 'number', '0' },
+ { 'punctuation.delimiter', ';' },
+ }, result)
+ end)
+
describe('Query:iter_captures', function()
it('includes metadata for all captured nodes #23664', function()
insert([[
@@ -835,9 +894,9 @@ void ui_refresh(void)
local result = exec_lua(function()
local query0 = vim.treesitter.query.parse('c', query)
- local match_preds = query0.match_preds
+ local match_preds = query0._match_predicates
local called = 0
- function query0:match_preds(...)
+ function query0:_match_predicates(...)
called = called + 1
return match_preds(self, ...)
end
diff --git a/test/functional/treesitter/testutil.lua b/test/functional/treesitter/testutil.lua
new file mode 100644
index 0000000000..f8934f06c3
--- /dev/null
+++ b/test/functional/treesitter/testutil.lua
@@ -0,0 +1,25 @@
+local n = require('test.functional.testnvim')()
+
+local exec_lua = n.exec_lua
+
+local M = {}
+
+---@param language string
+---@param query_string string
+function M.run_query(language, query_string)
+ return exec_lua(function(lang, query_str)
+ local query = vim.treesitter.query.parse(lang, query_str)
+ local parser = vim.treesitter.get_parser()
+ local tree = parser:parse()[1]
+ local res = {}
+ for id, node, metadata in query:iter_captures(tree:root(), 0) do
+ table.insert(
+ res,
+ { query.captures[id], metadata[id] and metadata[id].range or { node:range() } }
+ )
+ end
+ return res
+ end, language, query_string)
+end
+
+return M