aboutsummaryrefslogtreecommitdiff
path: root/test/functional/treesitter
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/treesitter')
-rw-r--r--test/functional/treesitter/fold_spec.lua474
-rw-r--r--test/functional/treesitter/highlight_spec.lua291
-rw-r--r--test/functional/treesitter/language_spec.lua18
-rw-r--r--test/functional/treesitter/node_spec.lua28
-rw-r--r--test/functional/treesitter/parser_spec.lua573
-rw-r--r--test/functional/treesitter/utils_spec.lua17
6 files changed, 1225 insertions, 176 deletions
diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua
new file mode 100644
index 0000000000..a8abbc002b
--- /dev/null
+++ b/test/functional/treesitter/fold_spec.lua
@@ -0,0 +1,474 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local eq = helpers.eq
+local insert = helpers.insert
+local exec_lua = helpers.exec_lua
+local command = helpers.command
+local feed = helpers.feed
+local Screen = require('test.functional.ui.screen')
+
+before_each(clear)
+
+describe('treesitter foldexpr', function()
+ clear()
+
+ local test_text = [[
+void ui_refresh(void)
+{
+ int width = INT_MAX, height = INT_MAX;
+ bool ext_widgets[kUIExtCount];
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
+ ext_widgets[i] = true;
+ }
+
+ bool inclusive = ui_override();
+ for (size_t i = 0; i < ui_count; i++) {
+ UI *ui = uis[i];
+ width = MIN(ui->width, width);
+ height = MIN(ui->height, height);
+ foo = BAR(ui->bazaar, bazaar);
+ for (UIExtension j = 0; (int)j < kUIExtCount; j++) {
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive);
+ }
+ }
+}]]
+
+ local function get_fold_levels()
+ return exec_lua([[
+ local res = {}
+ for i = 1, vim.api.nvim_buf_line_count(0) do
+ res[i] = vim.treesitter.foldexpr(i)
+ end
+ return res
+ ]])
+ end
+
+ it("can compute fold levels", function()
+ insert(test_text)
+
+ exec_lua([[vim.treesitter.get_parser(0, "c")]])
+
+ 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)
+
+ it("recomputes fold levels after lines are added/removed", function()
+ insert(test_text)
+
+ exec_lua([[vim.treesitter.get_parser(0, "c")]])
+
+ command('1,2d')
+
+ eq({
+ [1] = '0',
+ [2] = '0',
+ [3] = '>1',
+ [4] = '1',
+ [5] = '1',
+ [6] = '0',
+ [7] = '0',
+ [8] = '>1',
+ [9] = '1',
+ [10] = '1',
+ [11] = '1',
+ [12] = '1',
+ [13] = '>2',
+ [14] = '2',
+ [15] = '2',
+ [16] = '1',
+ [17] = '0' }, get_fold_levels())
+
+ command('1put!')
+
+ 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)
+
+ 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};
+ })
+
+ exec_lua([[vim.treesitter.get_parser(0, "c")]])
+ command([[set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=9]])
+ command('split')
+
+ insert(test_text)
+
+ screen:expect{grid=[[
+ {1:-}void ui_refresh(void) |
+ {1:│}{ |
+ {1:│} int width = INT_MAX, height = INT_MAX; |
+ {1:│} bool ext_widgets[kUIExtCount]; |
+ {1:-} for (UIExtension i = 0; (int)i < kUIExtCount; i++) { |
+ {1:2} ext_widgets[i] = true; |
+ {1:2} } |
+ {1:│} |
+ {1:│} bool inclusive = ui_override(); |
+ {1:-} for (size_t i = 0; i < ui_count; i++) { |
+ {1:2} UI *ui = uis[i]; |
+ {1:2} width = MIN(ui->width, width); |
+ {1:2} height = MIN(ui->height, height); |
+ {1:2} foo = BAR(ui->bazaar, bazaar); |
+ {1:-} for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1:3} ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1:3} } |
+ {1:2} } |
+ {1:│}^} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ {1:-}void ui_refresh(void) |
+ {1:│}{ |
+ {1:│} int width = INT_MAX, height = INT_MAX; |
+ {1:│} bool ext_widgets[kUIExtCount]; |
+ {1:-} for (UIExtension i = 0; (int)i < kUIExtCount; i++) { |
+ {1:2} ext_widgets[i] = true; |
+ {1:2} } |
+ {1:│} |
+ {1:│} bool inclusive = ui_override(); |
+ {1:-} for (size_t i = 0; i < ui_count; i++) { |
+ {1:2} UI *ui = uis[i]; |
+ {1:2} width = MIN(ui->width, width); |
+ {1:2} height = MIN(ui->height, height); |
+ {1:2} foo = BAR(ui->bazaar, bazaar); |
+ {1:-} for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1:3} ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1:3} } |
+ {1:2} } |
+ {1:│}} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]]}
+
+ command('1,2d')
+
+ 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++) { |
+ {1:│} ext_widgets[i] = true; |
+ {1:│} } |
+ {1: } |
+ {1: } bool inclusive = ui_override(); |
+ {1:-} for (size_t i = 0; i < ui_count; i++) { |
+ {1:│} UI *ui = uis[i]; |
+ {1:│} width = MIN(ui->width, width); |
+ {1:│} height = MIN(ui->height, height); |
+ {1:│} foo = BAR(ui->bazaar, bazaar); |
+ {1:-} for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1:2} ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1:2} } |
+ {1:│} } |
+ {1: }} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ {1: } int width = INT_MAX, height = INT_MAX; |
+ {1: } bool ext_widgets[kUIExtCount]; |
+ {1:-} for (UIExtension i = 0; (int)i < kUIExtCount; i++) { |
+ {1:│} ext_widgets[i] = true; |
+ {1:│} } |
+ {1: } |
+ {1: } bool inclusive = ui_override(); |
+ {1:-} for (size_t i = 0; i < ui_count; i++) { |
+ {1:│} UI *ui = uis[i]; |
+ {1:│} width = MIN(ui->width, width); |
+ {1:│} height = MIN(ui->height, height); |
+ {1:│} foo = BAR(ui->bazaar, bazaar); |
+ {1:-} for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1:2} ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1:2} } |
+ {1:│} } |
+ {1: }} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]]}
+
+
+ feed([[O<C-u><C-r>"<BS><Esc>]])
+
+ screen:expect{grid=[[
+ {1:-}void ui_refresh(void) |
+ {1:│}^{ |
+ {1:│} int width = INT_MAX, height = INT_MAX; |
+ {1:│} bool ext_widgets[kUIExtCount]; |
+ {1:-} for (UIExtension i = 0; (int)i < kUIExtCount; i++) { |
+ {1:2} ext_widgets[i] = true; |
+ {1:2} } |
+ {1:│} |
+ {1:│} bool inclusive = ui_override(); |
+ {1:-} for (size_t i = 0; i < ui_count; i++) { |
+ {1:2} UI *ui = uis[i]; |
+ {1:2} width = MIN(ui->width, width); |
+ {1:2} height = MIN(ui->height, height); |
+ {1:2} foo = BAR(ui->bazaar, bazaar); |
+ {1:-} for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1:3} ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1:3} } |
+ {1:2} } |
+ {1:│}} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ {1:-}void ui_refresh(void) |
+ {1:│}{ |
+ {1:│} int width = INT_MAX, height = INT_MAX; |
+ {1:│} bool ext_widgets[kUIExtCount]; |
+ {1:-} for (UIExtension i = 0; (int)i < kUIExtCount; i++) { |
+ {1:2} ext_widgets[i] = true; |
+ {1:2} } |
+ {1:│} |
+ {1:│} bool inclusive = ui_override(); |
+ {1:-} for (size_t i = 0; i < ui_count; i++) { |
+ {1:2} UI *ui = uis[i]; |
+ {1:2} width = MIN(ui->width, width); |
+ {1:2} height = MIN(ui->height, height); |
+ {1:2} foo = BAR(ui->bazaar, bazaar); |
+ {1:-} for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1:3} ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1:3} } |
+ {1:2} } |
+ {1:│}} |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {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]])
+ insert(test_text)
+ command('16d')
+
+ command('new')
+ insert(test_text)
+
+ command('windo diffthis')
+ feed('do')
+
+ 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]; |
+ {1: } width = MIN(ui->width, width); |
+ {1: } height = MIN(ui->height, height); |
+ {1: } foo = BAR(ui->bazaar, bazaar); |
+ {1: } for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1: } ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1: } } |
+ {1: } } |
+ {1: }} |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {4:[No Name] [+] }|
+ {1:+ }{2:+-- 9 lines: void ui_refresh(void)·······················}|
+ {1: } for (size_t i = 0; i < ui_count; i++) { |
+ {1: } UI *ui = uis[i]; |
+ {1: } width = MIN(ui->width, width); |
+ {1: } height = MIN(ui->height, height); |
+ {1: } foo = BAR(ui->bazaar, bazaar); |
+ {1: } for (UIExtension j = 0; (int)j < kUIExtCount; j++) { |
+ {1: } ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {1: } ^} |
+ {1: } } |
+ {1: }} |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {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);
+ }
+ }
+}]]
+ local screen
+
+ before_each(function()
+ screen = Screen.new(60, 5)
+ 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};
+ })
+ 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)
+
+ feed('ggVGzf')
+ screen:expect{grid=[[
+ {1:^+-- 19 lines: void qsort(void *base, size_t nel, size_t widt}|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]]}
+ end)
+end)
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index 2a2311c0fa..e037c9e215 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -11,7 +11,7 @@ local eq = helpers.eq
before_each(clear)
-local hl_query = [[
+local hl_query_c = [[
(ERROR) @error
"if" @keyword
@@ -47,7 +47,7 @@ local hl_query = [[
(comment) @comment
]]
-local hl_text = [[
+local hl_text_c = [[
/// Schedule Lua callback on main loop's event queue
static int nlua_schedule(lua_State *const lstate)
{
@@ -64,7 +64,7 @@ static int nlua_schedule(lua_State *const lstate)
return 0;
}]]
-local test_text = [[
+local test_text_c = [[
void ui_refresh(void)
{
int width = INT_MAX, height = INT_MAX;
@@ -85,7 +85,57 @@ void ui_refresh(void)
}
}]]
-describe('treesitter highlighting', function()
+local injection_text_c = [[
+int x = INT_MAX;
+#define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))
+#define foo void main() { \
+ return 42; \
+ }
+]]
+
+local injection_grid_c = [[
+ int x = INT_MAX; |
+ #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) |
+ #define foo void main() { \ |
+ return 42; \ |
+ } |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+]]
+
+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}; \ |
+ } |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+]]
+
+describe('treesitter highlighting (C)', function()
local screen
before_each(function()
@@ -105,13 +155,13 @@ describe('treesitter highlighting', function()
[11] = {foreground = Screen.colors.Cyan4};
}
- exec_lua([[ hl_query = ... ]], hl_query)
+ exec_lua([[ hl_query = ... ]], hl_query_c)
command [[ hi link @error ErrorMsg ]]
command [[ hi link @warning WarningMsg ]]
end)
it('is updated with edits', function()
- insert(hl_text)
+ insert(hl_text_c)
screen:expect{grid=[[
/// Schedule Lua callback on main loop's event queue |
static int nlua_schedule(lua_State *const lstate) |
@@ -274,7 +324,7 @@ describe('treesitter highlighting', function()
end)
it('is updated with :sort', function()
- insert(test_text)
+ insert(test_text_c)
exec_lua [[
local parser = vim.treesitter.get_parser(0, "c")
test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query}})
@@ -351,7 +401,7 @@ describe('treesitter highlighting', function()
[1] = {bold = true, foreground = Screen.colors.SeaGreen4};
}
- insert(test_text)
+ insert(test_text_c)
screen:expect{ grid= [[
int width = INT_MAX, height = INT_MAX; |
@@ -376,7 +426,7 @@ describe('treesitter highlighting', function()
exec_lua [[
parser = vim.treesitter.get_parser(0, "c")
- query = vim.treesitter.parse_query("c", "(declaration) @decl")
+ query = vim.treesitter.query.parse("c", "(declaration) @decl")
local nodes = {}
for _, node in query:iter_captures(parser:parse()[1]:root(), 0, 0, 19) do
@@ -411,85 +461,58 @@ describe('treesitter highlighting', function()
end)
it("supports injected languages", function()
- insert([[
- int x = INT_MAX;
- #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
- #define foo void main() { \
- return 42; \
- }
- ]])
+ insert(injection_text_c)
- screen:expect{grid=[[
- int x = INT_MAX; |
- #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))|
- #define foo void main() { \ |
- return 42; \ |
- } |
- ^ |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- |
- ]]}
+ screen:expect{grid=injection_grid_c}
exec_lua [[
local parser = vim.treesitter.get_parser(0, "c", {
- injections = {c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}
+ injections = {c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))'}
})
local highlighter = vim.treesitter.highlighter
test_hl = highlighter.new(parser, {queries = {c = hl_query}})
]]
- screen:expect{grid=[[
- {3:int} x = {5:INT_MAX}; |
- #define {5:READ_STRING}(x, y) ({3:char_u} *)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:~ }|
- |
- ]]}
+ 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}
+
+ exec_lua [[
+ vim.treesitter.language.register("c", "foo")
+ local parser = vim.treesitter.get_parser(0, "c", {
+ injections = {c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "fOO")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "fOO"))'}
+ })
+ local highlighter = vim.treesitter.highlighter
+ test_hl = highlighter.new(parser, {queries = {c = hl_query}})
+ ]]
+
+ screen:expect{grid=injection_grid_expected_c}
end)
it("supports overriding queries, like ", function()
insert([[
int x = INT_MAX;
- #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
+ #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))
#define foo void main() { \
return 42; \
}
]])
exec_lua [[
- local injection_query = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"
- require('vim.treesitter.query').set_query("c", "highlights", hl_query)
- require('vim.treesitter.query').set_query("c", "injections", injection_query)
+ local injection_query = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))'
+ vim.treesitter.query.set("c", "highlights", hl_query)
+ vim.treesitter.query.set("c", "injections", injection_query)
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, "c"))
]]
screen:expect{grid=[[
{3:int} x = {5:INT_MAX}; |
- #define {5:READ_STRING}(x, y) ({3:char_u} *)read_string((x), ({3:size_t})(y))|
+ #define {5:READ_STRING}(x, y) ({3:char} *)read_string((x), ({3:size_t})(y)) |
#define foo {3:void} main() { \ |
{4:return} {5:42}; \ |
} |
@@ -510,7 +533,7 @@ describe('treesitter highlighting', function()
end)
it("supports highlighting with custom highlight groups", function()
- insert(hl_text)
+ insert(hl_text_c)
exec_lua [[
local parser = vim.treesitter.get_parser(0, "c")
@@ -567,7 +590,7 @@ describe('treesitter highlighting', function()
it("supports highlighting with priority", function()
insert([[
int x = INT_MAX;
- #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
+ #define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))
#define foo void main() { \
return 42; \
}
@@ -575,12 +598,12 @@ describe('treesitter highlighting', function()
exec_lua [[
local parser = vim.treesitter.get_parser(0, "c")
- test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query..'\n((translation_unit) @Error (set! "priority" 101))\n'}})
+ test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query..'\n((translation_unit) @constant (#set! "priority" 101))\n'}})
]]
- -- expect everything to have Error highlight
+ -- expect everything to have Constant highlight
screen:expect{grid=[[
{12:int}{8: x = INT_MAX;} |
- {8:#define READ_STRING(x, y) (}{12:char_u}{8: *)read_string((x), (}{12:size_t}{8:)(y))}|
+ {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: }} |
@@ -599,13 +622,13 @@ describe('treesitter highlighting', function()
|
]], attr_ids={
[1] = {bold = true, foreground = Screen.colors.Blue1};
- [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red};
+ [8] = {foreground = Screen.colors.Magenta1};
-- bold will not be overwritten at the moment
- [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Grey100};
+ [12] = {bold = true, foreground = Screen.colors.Magenta1};
}}
eq({
- {capture='Error', metadata = { priority='101' }, 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)
@@ -692,7 +715,7 @@ describe('treesitter highlighting', function()
end)
it("supports conceal attribute", function()
- insert(hl_text)
+ insert(hl_text_c)
-- conceal can be empty or a single cchar.
exec_lua [=[
@@ -753,3 +776,129 @@ describe('treesitter highlighting', function()
eq(nil, get_hl"@total.nonsense.but.a.lot.of.dots")
end)
end)
+
+describe('treesitter highlighting (help)', function()
+ local screen
+
+ before_each(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};
+ }
+ end)
+
+ it("correctly redraws added/removed injections", function()
+ insert[[
+ >ruby
+ -- comment
+ local this_is = 'actually_lua'
+ <
+ ]]
+
+ exec_lua [[
+ vim.bo.filetype = 'help'
+ vim.treesitter.start()
+ ]]
+
+ screen:expect{grid=[[
+ {1:>ruby} |
+ {1: -- comment} |
+ {1: local this_is = 'actually_lua'} |
+ < |
+ ^ |
+ |
+ ]]}
+
+ helpers.curbufmeths.set_text(0, 1, 0, 5, {'lua'})
+
+ screen:expect{grid=[[
+ {1:>lua} |
+ {1: -- comment} |
+ {1: }{3:local}{1: }{4:this_is}{1: }{3:=}{1: }{5:'actually_lua'} |
+ < |
+ ^ |
+ |
+ ]]}
+
+ helpers.curbufmeths.set_text(0, 1, 0, 4, {'ruby'})
+
+ screen:expect{grid=[[
+ {1:>ruby} |
+ {1: -- comment} |
+ {1: local this_is = 'actually_lua'} |
+ < |
+ ^ |
+ |
+ ]]}
+ end)
+
+end)
+
+describe('treesitter highlighting (nested injections)', function()
+ local screen
+
+ before_each(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};
+ }
+ end)
+
+ it("correctly redraws nested injections (GitHub #25252)", function()
+ insert[=[
+function foo() print("Lua!") end
+
+local lorem = {
+ ipsum = {},
+ bar = {},
+}
+vim.cmd([[
+ augroup RustLSP
+ autocmd CursorHold silent! lua vim.lsp.buf.document_highlight()
+ augroup END
+]])
+ ]=]
+
+ exec_lua [[
+ vim.opt.scrolloff = 0
+ vim.bo.filetype = 'lua'
+ vim.treesitter.start()
+ ]]
+
+ -- invalidate the language tree
+ feed("ggi--[[<ESC>04x")
+
+ 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:}} |
+ |
+ ]]}
+
+ -- spam newline insert/delete to invalidate Lua > Vim > Lua region
+ feed("3jo<ESC>ddko<ESC>ddko<ESC>ddko<ESC>ddk0")
+
+ 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:}} |
+ |
+ ]]}
+ end)
+
+end)
diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua
index df45c9b384..9b871a72fb 100644
--- a/test/functional/treesitter/language_spec.lua
+++ b/test/functional/treesitter/language_spec.lua
@@ -18,27 +18,27 @@ describe('treesitter language API', function()
-- actual message depends on platform
matches("Failed to load parser for language 'borklang': uv_dlopen: .+",
- pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')"))
+ pcall_err(exec_lua, "parser = vim.treesitter.language.add('borklang', { path = 'borkbork.so' })"))
- -- Should not throw an error when silent
- eq(false, exec_lua("return vim.treesitter.require_language('borklang', nil, true)"))
- eq(false, exec_lua("return vim.treesitter.require_language('borklang', 'borkbork.so', true)"))
+ 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(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers",
- pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('borklang')"))
+ pcall_err(exec_lua, "parser = vim.treesitter.language.inspect('borklang')"))
matches("Failed to load parser: uv_dlsym: .+",
- pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "borklang")'))
+ 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.require_language("/foo/", nil, false)'))
+ pcall_err(exec_lua, 'vim.treesitter.language.add("/foo/")'))
end)
it('inspects language', function()
local keys, fields, symbols = unpack(exec_lua([[
- local lang = vim.treesitter.inspect_language('c')
+ local lang = vim.treesitter.language.inspect('c')
local keys, symbols = {}, {}
for k,_ in pairs(lang) do
keys[k] = true
@@ -82,7 +82,7 @@ describe('treesitter language API', function()
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)"))
+ pcall_err(exec_lua, "new_parser = vim.treesitter.get_parser(0, 'borklang')"))
end)
it('retrieve the tree given a range', function ()
diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua
index a82dce47b7..eef75d0e91 100644
--- a/test/functional/treesitter/node_spec.lua
+++ b/test/functional/treesitter/node_spec.lua
@@ -4,6 +4,7 @@ local clear = helpers.clear
local eq = helpers.eq
local exec_lua = helpers.exec_lua
local insert = helpers.insert
+local assert_alive = helpers.assert_alive
before_each(clear)
@@ -14,6 +15,31 @@ end
describe('treesitter node API', function()
clear()
+ it('double free tree', function()
+ insert('F')
+ exec_lua([[
+ vim.treesitter.start(0, 'lua')
+ vim.treesitter.get_node():tree()
+ vim.treesitter.get_node():tree()
+ collectgarbage()
+ ]])
+ assert_alive()
+ end)
+
+ it('double free tree 2', function()
+ exec_lua([[
+ parser = vim.treesitter.get_parser(0, "c")
+ local x = parser:parse()[1]:root():tree()
+ vim.api.nvim_buf_set_text(0, 0,0, 0,0, {'y'})
+ parser:parse()
+ vim.api.nvim_buf_set_text(0, 0,0, 0,1, {'z'})
+ parser:parse()
+ collectgarbage()
+ x:root()
+ ]])
+ assert_alive()
+ end)
+
it('can move between siblings', function()
insert([[
int main(int x, int y, int z) {
@@ -26,7 +52,7 @@ describe('treesitter node API', function()
parser = vim.treesitter.get_parser(0, "c")
tree = parser:parse()[1]
root = tree:root()
- lang = vim.treesitter.inspect_language('c')
+ lang = vim.treesitter.language.inspect('c')
function node_text(node)
return query.get_node_text(node, 0)
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index f006ad4539..6f386115ae 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -1,17 +1,21 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
+local dedent = helpers.dedent
local eq = helpers.eq
local insert = helpers.insert
local exec_lua = helpers.exec_lua
+local pcall_err = helpers.pcall_err
local feed = helpers.feed
local is_os = helpers.is_os
-local skip = helpers.skip
-
-before_each(clear)
describe('treesitter parser API', function()
- clear()
+ before_each(function()
+ clear()
+ exec_lua[[
+ vim.g.__ts_debug = 1
+ ]]
+ end)
it('parses buffer', function()
insert([[
@@ -23,7 +27,7 @@ describe('treesitter parser API', function()
parser = vim.treesitter.get_parser(0, "c")
tree = parser:parse()[1]
root = tree:root()
- lang = vim.treesitter.inspect_language('c')
+ lang = vim.treesitter.language.inspect('c')
]])
eq("<tree>", exec_lua("return tostring(tree)"))
@@ -124,6 +128,18 @@ void ui_refresh(void)
}, res)
end)
+ it('does not get parser for empty filetype', function()
+ 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)'))
+
+ -- 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);
@@ -159,7 +175,7 @@ void ui_refresh(void)
it("supports runtime queries", function()
local ret = exec_lua [[
- return require"vim.treesitter.query".get_query("c", "highlights").captures[1]
+ return vim.treesitter.query.get("c", "highlights").captures[1]
]]
eq('variable', ret)
@@ -170,11 +186,11 @@ void ui_refresh(void)
local function q(n)
return exec_lua ([[
local query, n = ...
- local before = vim.loop.hrtime()
+ local before = vim.uv.hrtime()
for i=1,n,1 do
- cquery = vim.treesitter.parse_query("c", ...)
+ cquery = vim.treesitter.query.parse("c", ...)
end
- local after = vim.loop.hrtime()
+ local after = vim.uv.hrtime()
return after - before
]], long_query, n)
end
@@ -182,15 +198,16 @@ void ui_refresh(void)
local firstrun = q(1)
local manyruns = q(100)
- -- First run should be at least 4x slower.
- assert(400 * manyruns < firstrun, ('firstrun: %d ms, manyruns: %d ms'):format(firstrun / 1000, manyruns / 1000))
+ -- 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))
end)
it('support query and iter by capture', function()
insert(test_text)
local res = exec_lua([[
- cquery = vim.treesitter.parse_query("c", ...)
+ cquery = vim.treesitter.query.parse("c", ...)
parser = vim.treesitter.get_parser(0, "c")
tree = parser:parse()[1]
res = {}
@@ -219,7 +236,7 @@ void ui_refresh(void)
insert(test_text)
local res = exec_lua([[
- cquery = vim.treesitter.parse_query("c", ...)
+ cquery = vim.treesitter.query.parse("c", ...)
parser = vim.treesitter.get_parser(0, "c")
tree = parser:parse()[1]
res = {}
@@ -263,13 +280,13 @@ void ui_refresh(void)
eq('void', res2)
end)
- it('support getting text where start of node is past EOF', function()
+ it('support getting text where start of node is one past EOF', function()
local text = [[
def run
a = <<~E
end]]
insert(text)
- local result = exec_lua([[
+ eq('', exec_lua[[
local fake_node = {}
function fake_node:start()
return 3, 0, 23
@@ -277,9 +294,14 @@ end]]
function fake_node:end_()
return 3, 0, 23
end
- return vim.treesitter.get_node_text(fake_node, 0) == nil
+ function fake_node:range(bytes)
+ if bytes then
+ return 3, 0, 23, 3, 0, 23
+ end
+ return 3, 0, 3, 0
+ end
+ return vim.treesitter.get_node_text(fake_node, 0)
]])
- eq(true, result)
end)
it('support getting empty text if node range is zero width', function()
@@ -296,6 +318,9 @@ end]]
function fake_node:end_()
return 1, 0, 7
end
+ function fake_node:range()
+ return 1, 0, 1, 0
+ end
return vim.treesitter.get_node_text(fake_node, 0) == ''
]])
eq(true, result)
@@ -305,7 +330,7 @@ end]]
insert('char* astring = "\\n"; (1 + 1) * 2 != 2;')
local res = exec_lua([[
- cquery = vim.treesitter.parse_query("c", '([_] @plus (#vim-match? @plus "^\\\\+$"))'..
+ cquery = vim.treesitter.query.parse("c", '([_] @plus (#vim-match? @plus "^\\\\+$"))'..
'([_] @times (#vim-match? @times "^\\\\*$"))'..
'([_] @paren (#vim-match? @paren "^\\\\($"))'..
'([_] @escape (#vim-match? @escape "^\\\\\\\\n$"))'..
@@ -355,7 +380,7 @@ end]]
]])
exec_lua([[
function get_query_result(query_text)
- cquery = vim.treesitter.parse_query("c", query_text)
+ cquery = vim.treesitter.query.parse("c", query_text)
parser = vim.treesitter.get_parser(0, "c")
tree = parser:parse()[1]
res = {}
@@ -395,7 +420,7 @@ end]]
insert('char* astring = "Hello World!";')
local res = exec_lua([[
- cquery = vim.treesitter.parse_query("c", '([_] @quote (#vim-match? @quote "^\\"$")) ([_] @quote (#lua-match? @quote "^\\"$"))')
+ 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 = {}
@@ -428,7 +453,7 @@ end]]
local custom_query = "((identifier) @main (#is-main? @main))"
local res = exec_lua([[
- local query = require"vim.treesitter.query"
+ local query = vim.treesitter.query
local function is_main(match, pattern, bufnr, predicate)
local node = match[ predicate[2] ]
@@ -440,7 +465,7 @@ end]]
query.add_predicate("is-main?", is_main)
- local query = query.parse_query("c", ...)
+ local query = query.parse("c", ...)
local nodes = {}
for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do
@@ -453,7 +478,7 @@ end]]
eq({{0, 4, 0, 8}}, res)
local res_list = exec_lua[[
- local query = require'vim.treesitter.query'
+ local query = vim.treesitter.query
local list = query.list_predicates()
@@ -462,10 +487,9 @@ end]]
return list
]]
- eq({ 'any-of?', 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
+ eq({ 'any-of?', 'contains?', 'eq?', 'has-ancestor?', 'has-parent?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
end)
-
it('allows to set simple ranges', function()
insert(test_text)
@@ -482,22 +506,12 @@ end]]
local root = parser:parse()[1]:root()
parser:set_included_regions({{root:child(0)}})
parser:invalidate()
- return { parser:parse()[1]:root():range() }
+ return { parser:parse(true)[1]:root():range() }
]]
eq({0, 0, 18, 1}, res2)
- local range = exec_lua [[
- local res = {}
- for _, region in ipairs(parser:included_regions()) do
- for _, node in ipairs(region) do
- table.insert(res, {node:range()})
- end
- end
- return res
- ]]
-
- eq(range, { { 0, 0, 18, 1 } })
+ eq({ { { 0, 0, 0, 18, 1, 512 } } }, exec_lua [[ return parser:included_regions() ]])
local range_tbl = exec_lua [[
parser:set_included_regions { { { 0, 0, 17, 1 } } }
@@ -507,12 +521,13 @@ end]]
eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } })
end)
+
it("allows to set complex ranges", function()
insert(test_text)
local res = exec_lua [[
parser = vim.treesitter.get_parser(0, "c")
- query = vim.treesitter.parse_query("c", "(declaration) @decl")
+ query = vim.treesitter.query.parse("c", "(declaration) @decl")
local nodes = {}
for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do
@@ -521,7 +536,7 @@ end]]
parser:set_included_regions({nodes})
- local root = parser:parse()[1]:root()
+ local root = parser:parse(true)[1]:root()
local res = {}
for i=0,(root:named_child_count() - 1) do
@@ -560,7 +575,7 @@ end]]
local parser = vim.treesitter.get_string_parser(str, "c")
local nodes = {}
- local query = vim.treesitter.parse_query("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() })
@@ -582,7 +597,7 @@ end]]
local parser = vim.treesitter.get_string_parser(str, "c")
local nodes = {}
- local query = vim.treesitter.parse_query("c", '((identifier) @foo)')
+ local query = vim.treesitter.query.parse("c", '((identifier) @foo)')
local first_child = parser:parse()[1]:root():child(1)
for _, node in query:iter_captures(first_child, str) do
@@ -606,8 +621,8 @@ end]]
before_each(function()
insert([[
int x = INT_MAX;
-#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
-#define READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y))
+#define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))
+#define READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y))
#define VALUE 123
#define VALUE1 123
#define VALUE2 123
@@ -619,7 +634,8 @@ int x = INT_MAX;
exec_lua([[
parser = vim.treesitter.get_parser(0, "c", {
injections = {
- c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}})
+ c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))'}})
+ parser:parse(true)
]])
eq("table", exec_lua("return type(parser:children().c)"))
@@ -629,8 +645,19 @@ int x = INT_MAX;
{3, 14, 3, 17}, -- VALUE 123
{4, 15, 4, 18}, -- VALUE1 123
{5, 15, 5, 18}, -- VALUE2 123
- {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
- {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y))
+ {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({
+ {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)
@@ -640,7 +667,8 @@ int x = INT_MAX;
exec_lua([[
parser = vim.treesitter.get_parser(0, "c", {
injections = {
- c = "(preproc_def (preproc_arg) @c @combined) (preproc_function_def value: (preproc_arg) @c @combined)"}})
+ c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c") (#set! injection.combined)) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c") (#set! injection.combined))'}})
+ parser:parse(true)
]])
eq("table", exec_lua("return type(parser:children().c)"))
@@ -650,52 +678,54 @@ int x = INT_MAX;
{3, 14, 5, 18}, -- VALUE 123
-- VALUE1 123
-- VALUE2 123
- {1, 26, 2, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
- -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y))
+ {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())
- end)
- end)
-
- describe("when providing parsing information through a directive", function()
- it("should inject a language", function()
- exec_lua([=[
- vim.treesitter.add_directive("inject-clang!", function(match, _, _, pred, metadata)
- metadata.language = "c"
- metadata.combined = true
- metadata.content = pred[2]
- end)
-
- parser = vim.treesitter.get_parser(0, "c", {
- injections = {
- c = "(preproc_def ((preproc_arg) @_c (#inject-clang! @_c)))" ..
- "(preproc_function_def value: ((preproc_arg) @_a (#inject-clang! @_a)))"}})
- ]=])
+ helpers.feed('ggo<esc>')
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
+ {0, 0, 8, 0}, -- root tree
+ {4, 14, 6, 18}, -- VALUE 123
-- VALUE1 123
-- VALUE2 123
- {1, 26, 2, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
- -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y))
+ {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)
- it("should not inject bad languages", function()
- skip(is_os('win'))
- exec_lua([=[
- vim.treesitter.add_directive("inject-bad!", function(match, _, _, pred, metadata)
- metadata.language = "{"
- metadata.combined = true
- metadata.content = pred[2]
- end)
-
+ describe("when using injection.self", function()
+ it("should inject the source language", function()
+ exec_lua([[
parser = vim.treesitter.get_parser(0, "c", {
injections = {
- c = "(preproc_function_def value: ((preproc_arg) @_a (#inject-bad! @_a)))"}})
- ]=])
+ c = '(preproc_def (preproc_arg) @injection.content (#set! injection.self)) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.self))'}})
+ parser:parse(true)
+ ]])
+
+ 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))
+ }, get_ranges())
+
+ helpers.feed('ggo<esc>')
+ 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))
+ }, get_ranges())
end)
end)
@@ -704,22 +734,23 @@ int x = INT_MAX;
exec_lua([[
parser = vim.treesitter.get_parser(0, "c", {
injections = {
- c = "(preproc_def ((preproc_arg) @c (#offset! @c 0 2 0 -1))) (preproc_function_def value: (preproc_arg) @c)"}})
+ c = '(preproc_def ((preproc_arg) @injection.content (#set! injection.language "c") (#offset! @injection.content 0 2 0 -1))) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))'}})
+ parser:parse(true)
]])
eq("table", exec_lua("return type(parser:children().c)"))
eq({
{0, 0, 7, 0}, -- root tree
- {3, 15, 3, 16}, -- VALUE 123
- {4, 16, 4, 17}, -- VALUE1 123
- {5, 16, 5, 17}, -- VALUE2 123
- {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
- {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y))
+ {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[[
- local query = require'vim.treesitter.query'
+ local query = vim.treesitter.query
local list = query.list_directives()
@@ -728,7 +759,7 @@ int x = INT_MAX;
return list
]]
- eq({ 'offset!', 'set!' }, res_list)
+ eq({ 'gsub!', 'offset!', 'set!', 'trim!' }, res_list)
end)
end)
end)
@@ -744,7 +775,8 @@ int x = INT_MAX;
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) @c)"}})
+ injections = { c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c"))'}})
+ parser:parse(true)
local sub_tree = parser:language_for_range({1, 18, 1, 19})
@@ -765,7 +797,7 @@ int x = INT_MAX;
local result = exec_lua([[
local result
- query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! "key" "value"))')
+ 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
@@ -785,10 +817,10 @@ int x = INT_MAX;
]])
local result = exec_lua([[
- local query = require("vim.treesitter.query")
+ local query = vim.treesitter.query
local value
- query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value"))')
+ 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
@@ -807,10 +839,10 @@ int x = INT_MAX;
]])
local result = exec_lua([[
- local query = require("vim.treesitter.query")
+ local query = vim.treesitter.query
local result
- query = vim.treesitter.parse_query("c", '((number_literal) @number (#set! @number "key" "value") (#set! @number "key2" "value2"))')
+ 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
@@ -829,4 +861,355 @@ int x = INT_MAX;
end)
end)
end)
+
+ it('tracks the root range properly (#22911)', function()
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ local query0 = [[
+ (declaration) @declaration
+ (function_definition) @function
+ ]]
+
+ exec_lua([[
+ vim.treesitter.start(0, 'c')
+ ]])
+
+ local function run_query()
+ return exec_lua([[
+ local query = vim.treesitter.query.parse("c", ...)
+ parser = vim.treesitter.get_parser()
+ tree = parser:parse()[1]
+ res = {}
+ for id, node in query:iter_captures(tree:root()) do
+ table.insert(res, {query.captures[id], node:range()})
+ end
+ return res
+ ]], query0)
+ end
+
+ eq({
+ { 'function', 0, 0, 2, 1 },
+ { 'declaration', 1, 2, 1, 12 }
+ }, run_query())
+
+ helpers.command'normal ggO'
+ insert('int a;')
+
+ eq({
+ { 'declaration', 0, 0, 0, 6 },
+ { 'function', 1, 0, 3, 1 },
+ { 'declaration', 2, 2, 2, 12 }
+ }, run_query())
+
+ end)
+
+ it('handles ranges when source is a multiline string (#20419)', function()
+ local source = [==[
+ vim.cmd[[
+ set number
+ set cmdheight=2
+ set lastsatus=2
+ ]]
+
+ set query = [[;; query
+ ((function_call
+ name: [
+ (identifier) @_cdef_identifier
+ (_ _ (identifier) @_cdef_identifier)
+ ]
+ arguments: (arguments (string content: _ @injection.content)))
+ (#set! injection.language "c")
+ (#eq? @_cdef_identifier "cdef"))
+ ]]
+ ]==]
+
+ local r = exec_lua([[
+ local parser = vim.treesitter.get_string_parser(..., 'lua')
+ parser:parse(true)
+ local ranges = {}
+ parser:for_each_tree(function(tstree, tree)
+ ranges[tree:lang()] = { tstree:root():range(true) }
+ end)
+ return ranges
+ ]], source)
+
+ eq({
+ lua = { 0, 6, 6, 16, 4, 438 },
+ query = { 6, 20, 113, 15, 6, 431 },
+ 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 r, source = ...
+ local add_bytes = require('vim.treesitter._range').add_bytes
+ for lang, range in pairs(r) do
+ r[lang] = {range[1], range[2], range[4], range[5]}
+ r[lang] = add_bytes(source, r[lang])
+ end
+ return r
+ ]], r, source)
+
+ eq(rb, r)
+
+ end)
+
+ it("does not produce empty injection ranges (#23409)", function()
+ insert [[
+ Examples: >lua
+ local a = {}
+<
+ ]]
+
+ -- This is not a valid injection since (code) has children and include-children is not set
+ exec_lua [[
+ parser1 = require('vim.treesitter.languagetree').new(0, "vimdoc", {
+ injections = {
+ vimdoc = "((codeblock (language) @injection.language (code) @injection.content))"
+ }
+ })
+ parser1:parse(true)
+ ]]
+
+ eq(0, exec_lua("return #vim.tbl_keys(parser1:children())"))
+
+ exec_lua [[
+ parser2 = require('vim.treesitter.languagetree').new(0, "vimdoc", {
+ injections = {
+ vimdoc = "((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))"
+ }
+ })
+ 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()"))
+
+ end)
+
+ it("parsers injections incrementally", function()
+ insert(dedent[[
+ >lua
+ local a = {}
+ <
+
+ >lua
+ local b = {}
+ <
+
+ >lua
+ local c = {}
+ <
+
+ >lua
+ local d = {}
+ <
+
+ >lua
+ local e = {}
+ <
+
+ >lua
+ local f = {}
+ <
+
+ >lua
+ local g = {}
+ <
+ ]])
+
+ exec_lua [[
+ parser = require('vim.treesitter.languagetree').new(0, "vimdoc", {
+ injections = {
+ vimdoc = "((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))"
+ }
+ })
+ ]]
+
+ --- Do not parse injections by default
+ eq(0, exec_lua [[
+ parser:parse()
+ return #vim.tbl_keys(parser:children())
+ ]])
+
+ --- Only parse injections between lines 0, 2
+ eq(1, exec_lua [[
+ parser:parse({0, 2})
+ return #parser:children().lua:trees()
+ ]])
+
+ eq(2, exec_lua [[
+ parser:parse({2, 6})
+ return #parser:children().lua:trees()
+ ]])
+
+ eq(7, exec_lua [[
+ parser:parse(true)
+ return #parser:children().lua:trees()
+ ]])
+ end)
+
+ it('fails to load queries', function()
+ local function test(exp, cquery)
+ eq(exp, pcall_err(exec_lua, "vim.treesitter.query.parse('c', ...)", cquery))
+ end
+
+ -- Invalid node type
+ test(
+ '.../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)')
+
+ -- Invalid syntax
+ test(
+ '.../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))')
+
+ -- 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))')
+ end)
+
+ describe('is_valid()', function()
+ before_each(function()
+ insert(dedent[[
+ Treesitter integration *treesitter*
+
+ Nvim integrates the `tree-sitter` library for incremental parsing of buffers:
+ https://tree-sitter.github.io/tree-sitter/
+
+ ]])
+
+ feed(':set ft=help<cr>')
+
+ exec_lua [[
+ vim.treesitter.get_parser(0, "vimdoc", {
+ injections = {
+ vimdoc = "((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))"
+ }
+ })
+ ]]
+ end)
+
+ it('is valid excluding, invalid including children initially', function()
+ 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 full parse', function()
+ exec_lua('vim.treesitter.get_parser():parse(true)')
+ 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 fully valid after a parsing a range on parsed tree', 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)
+
+ describe('when adding content with injections', function()
+ before_each(function()
+ feed('G')
+ insert(dedent[[
+ >lua
+ local a = {}
+ <
+
+ ]])
+ end)
+
+ it('is fully invalid after changes', function()
+ eq(false, 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 rangeless parse', function()
+ exec_lua('vim.treesitter.get_parser():parse()')
+ 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[[
+ >lua
+ local a = {}
+ <
+
+ >lua
+ local a = {}
+ <
+
+ ]])
+
+ exec_lua('vim.treesitter.get_parser():parse(true)')
+
+ feed('Gd3k')
+ end)
+
+ it('is fully invalid after changes', function()
+ eq(false, 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 rangeless parse', function()
+ exec_lua('vim.treesitter.get_parser():parse()')
+ 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 modified child tree', 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 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 7f5a864c3d..9c07959098 100644
--- a/test/functional/treesitter/utils_spec.lua
+++ b/test/functional/treesitter/utils_spec.lua
@@ -28,4 +28,21 @@ describe('treesitter utils', function()
eq(true, exec_lua('return vim.treesitter.is_ancestor(ancestor, child)'))
eq(false, exec_lua('return vim.treesitter.is_ancestor(child, ancestor)'))
end)
+
+ it('can detect if a position is contained in a node', function()
+ exec_lua([[
+ node = {
+ range = function()
+ return 0, 4, 0, 8
+ end,
+ }
+ ]])
+
+ eq(false, exec_lua('return vim.treesitter.is_in_node_range(node, 0, 3)'))
+ for i = 4, 7 do
+ eq(true, exec_lua('return vim.treesitter.is_in_node_range(node, 0, ...)', i))
+ end
+ -- End column exclusive
+ eq(false, exec_lua('return vim.treesitter.is_in_node_range(node, 0, 8)'))
+ end)
end)