From c032e83b22994332dd8769ef34cb817906a63cac Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 26 Jan 2023 09:42:23 +0100 Subject: fix(treesitter): validate language name Problem: Some injections (like markdown) allow specifying arbitrary language names for code blocks, which may be lead to errors when looking for a corresponding parser in runtime path. Solution: Validate that the language name only contains alphanumeric characters and `_` (e.g., for `c_sharp`) and error otherwise. --- test/functional/treesitter/language_spec.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index f95b05a1cc..df45c9b384 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -31,6 +31,11 @@ describe('treesitter language API', function() pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "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)')) + end) + it('inspects language', function() local keys, fields, symbols = unpack(exec_lua([[ local lang = vim.treesitter.inspect_language('c') -- cgit From bb8845340b1b9c2180fb19f049ff9deff5857d99 Mon Sep 17 00:00:00 2001 From: figsoda Date: Thu, 21 Jul 2022 12:08:37 +0100 Subject: feat(treesitter): allow capture text to be transformed Co-authored-by: Lewis Russell --- test/functional/treesitter/parser_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index f006ad4539..0f187f3d52 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -728,7 +728,7 @@ int x = INT_MAX; return list ]] - eq({ 'offset!', 'set!' }, res_list) + eq({ 'gsub!', 'offset!', 'set!' }, res_list) end) end) end) -- cgit From e1d5ad1cb87d43c3d75619e239312d4ab2029b45 Mon Sep 17 00:00:00 2001 From: figsoda Date: Mon, 26 Dec 2022 16:10:59 -0500 Subject: feat(treesitter): add metadata option for get_node_text --- test/functional/treesitter/parser_spec.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 0f187f3d52..2d0d57fbd0 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -277,6 +277,9 @@ end]] function fake_node:end_() return 3, 0, 23 end + function fake_node:range() + return 3, 0, 3, 0 + end return vim.treesitter.get_node_text(fake_node, 0) == nil ]]) eq(true, result) @@ -296,6 +299,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) -- cgit From 8a985d12dd6b4a5a4ba825939f36b7b1a324d849 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 9 Feb 2023 16:08:22 +0000 Subject: fix(treesitter): don't trample parsers when filetype!=lang This allows vim.treesitter.show_tree() to work on buffers where the filetype does not match the parser language name e.g, bash/sh. --- test/functional/treesitter/language_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index df45c9b384..51d8eb62f3 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -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 () -- cgit From 8714a4009c0f0be0bb27a6b3eb486eeb3d9f3049 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 21 Feb 2023 17:09:18 +0000 Subject: feat(treesitter): add filetype -> lang API Problem: vim.treesitter does not know how to map a specific filetype to a parser. This creates problems since in a few places (including in vim.treesitter itself), the filetype is incorrectly used in place of lang. Solution: Add an API to enable this: - Add vim.treesitter.language.add() as a replacement for vim.treesitter.language.require_language(). - Optional arguments are now passed via an opts table. - Also takes a filetype (or list of filetypes) so we can keep track of what filetypes are associated with which langs. - Deprecated vim.treesitter.language.require_language(). - Add vim.treesitter.language.get_lang() which returns the associated lang for a given filetype. - Add vim.treesitter.language.register() to associate filetypes to a lang without loading the parser. --- test/functional/treesitter/language_spec.lua | 11 ++++++----- test/functional/treesitter/parser_spec.lua | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 51d8eb62f3..a8831b9a9b 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -18,22 +18,23 @@ 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.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 vim.treesitter.add('borklang', { silent = true })")) + + eq(false, exec_lua("return vim.treesitter.add('borklang', { path = 'borkbork.so', silent = true })")) eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", pcall_err(exec_lua, "parser = vim.treesitter.inspect_language('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.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.add("/foo/", nil, false)')) end) it('inspects language', function() diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 2d0d57fbd0..2430021617 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -182,8 +182,9 @@ void ui_refresh(void) local firstrun = q(1) local manyruns = q(100) + local factor = is_os('win') and 3 or 4 -- First run should be at least 4x slower. - assert(400 * manyruns < firstrun, ('firstrun: %d ms, manyruns: %d ms'):format(firstrun / 1000, manyruns / 1000)) + assert(factor * 100 * manyruns < firstrun, ('firstrun: %d ms, manyruns: %d ms'):format(firstrun / 1000, manyruns / 1000)) end) it('support query and iter by capture', function() -- cgit From 75e53341f37eeeda7d9be7f934249f7e5e4397e9 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 23 Feb 2023 15:19:52 +0000 Subject: perf(treesitter): smarter languagetree invalidation Problem: Treesitter injections are slow because all injected trees are invalidated on every change. Solution: Implement smarter invalidation to avoid reparsing injected regions. - In on_bytes, try and update self._regions as best we can. This PR just offsets any regions after the change. - Add valid flags for each region in self._regions. - Call on_bytes recursively for all children. - We still need to run the query every time for the top level tree. I don't know how to avoid this. However, if the new injection ranges don't change, then we re-use the old trees and avoid reparsing children. This should result in roughly a 2-3x reduction in tree parsing when the comment injections are enabled. --- test/functional/treesitter/parser_spec.lua | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 2430021617..fdd6403859 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -639,6 +639,17 @@ int x = INT_MAX; {1, 26, 1, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) }, get_ranges()) + + helpers.feed('ggo') + 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, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + {3, 29, 3, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) end) end) @@ -660,6 +671,18 @@ int x = INT_MAX; {1, 26, 2, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) }, get_ranges()) + + helpers.feed('ggo') + eq("table", exec_lua("return type(parser:children().c)")) + eq(2, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 0, 8, 0}, -- root tree + {4, 14, 6, 18}, -- VALUE 123 + -- VALUE1 123 + -- VALUE2 123 + {2, 26, 3, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) end) end) @@ -688,6 +711,18 @@ int x = INT_MAX; {1, 26, 2, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) }, get_ranges()) + + helpers.feed('ggo') + eq("table", exec_lua("return type(parser:children().c)")) + eq(2, exec_lua("return #parser:children().c:trees()")) + eq({ + {0, 0, 8, 0}, -- root tree + {4, 14, 6, 18}, -- VALUE 123 + -- VALUE1 123 + -- VALUE2 123 + {2, 26, 3, 68} -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + }, get_ranges()) end) it("should not inject bad languages", function() -- cgit From 1df3f5ec6aca24cbe7b78ead5c37ad06a65c84e8 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 23 Feb 2023 17:05:20 +0000 Subject: feat(treesitter): upstream foldexpr from nvim-treesitter --- test/functional/treesitter/parser_spec.lua | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index fdd6403859..67aad4d1b7 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -871,4 +871,38 @@ int x = INT_MAX; end) end) end) + + it("can fold via foldexpr", function() + insert(test_text) + exec_lua([[vim.treesitter.get_parser(0, "c")]]) + + local levels = exec_lua([[ + local res = {} + for i = 1, vim.api.nvim_buf_line_count(0) do + res[i] = vim.treesitter.foldexpr(i) + end + return res + ]]) + + 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' }, levels) + end) end) -- cgit From c57af5d41cd039194dbd9c6fb5b68b377d2a5b59 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 24 Feb 2023 09:50:59 +0000 Subject: feat(treesitter)!: remove silent option from language.add() Simply use `pcall` if you want to silence an error. --- test/functional/treesitter/language_spec.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index a8831b9a9b..2cf18242ff 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -20,10 +20,9 @@ describe('treesitter language API', function() matches("Failed to load parser for language 'borklang': uv_dlopen: .+", pcall_err(exec_lua, "parser = vim.treesitter.add('borklang', { path = 'borkbork.so' })")) - -- Should not throw an error when silent - eq(false, exec_lua("return vim.treesitter.add('borklang', { silent = true })")) + eq(false, exec_lua("return pcall(vim.treesitter.add, 'borklang')")) - eq(false, exec_lua("return vim.treesitter.add('borklang', { path = 'borkbork.so', silent = true })")) + eq(false, exec_lua("return pcall(vim.treesitter.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')")) -- cgit From fb1db80f5ab707e188be3c60539fa38eaf996f24 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 2 Mar 2023 14:42:15 +0800 Subject: test(treesitter/parser_spec): correct time unit (#22471) --- test/functional/treesitter/parser_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 67aad4d1b7..fd9822d1c0 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -182,9 +182,9 @@ void ui_refresh(void) local firstrun = q(1) local manyruns = q(100) - local factor = is_os('win') and 3 or 4 - -- First run should be at least 4x slower. - assert(factor * 100 * manyruns < firstrun, ('firstrun: %d ms, manyruns: %d ms'):format(firstrun / 1000, manyruns / 1000)) + -- First run should be at least 400x slower than an 100 subsequent runs. + local factor = is_os('win') and 300 or 400 + assert(factor * manyruns < firstrun, ('firstrun: %f ms, manyruns: %f ms'):format(firstrun / 1e6, manyruns / 1e6)) end) it('support query and iter by capture', function() -- cgit From 6d4f48182131c36d57589eefd4cefe3c70256d04 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 3 Mar 2023 09:44:02 +0000 Subject: fix(treesitter): disallow empty filetypes Fixes #22473 --- test/functional/treesitter/language_spec.lua | 7 ++++++- test/functional/treesitter/parser_spec.lua | 14 +++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 2cf18242ff..747aea54b7 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -33,7 +33,12 @@ describe('treesitter language API', function() 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.add("/foo/", nil, false)')) + pcall_err(exec_lua, 'vim.treesitter.add("/foo/")')) + end) + + it('shows error for invalid filetype', function() + eq('.../language.lua:0: \'\' is not a valid filetype', + pcall_err(exec_lua, [[vim.treesitter.add('foo', { filetype = '' })]])) end) it('inspects language', function() diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index fd9822d1c0..27f2e81ab2 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -4,6 +4,7 @@ local clear = helpers.clear 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 @@ -124,6 +125,16 @@ void ui_refresh(void) }, res) end) + it('does not get parser for empty filetype', function() + insert(test_text); + + eq(".../language.lua:0: '' is not a valid 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); @@ -874,9 +885,10 @@ int x = INT_MAX; it("can fold via foldexpr", function() insert(test_text) - exec_lua([[vim.treesitter.get_parser(0, "c")]]) local levels = exec_lua([[ + vim.opt.filetype = 'c' + vim.treesitter.get_parser(0, "c") local res = {} for i = 1, vim.api.nvim_buf_line_count(0) do res[i] = vim.treesitter.foldexpr(i) -- cgit From be0461e3c216c2e4e2c3397c739b7727a5bf6df8 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 8 Mar 2023 23:45:43 +0000 Subject: fix(treesitter): is_in_node_range (#22582) TS ranges are end column exclusive, so fix is_in_node_range to account for that. --- test/functional/treesitter/utils_spec.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'test/functional/treesitter') 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) -- cgit From adfa9de8ebc4bce96d212280eccddc0306d1b013 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 Mar 2023 10:12:57 +0000 Subject: fix(treesitter): do not error on empty filetype Ignore instead --- test/functional/treesitter/language_spec.lua | 5 ----- 1 file changed, 5 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 747aea54b7..48e7b4b018 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -36,11 +36,6 @@ describe('treesitter language API', function() pcall_err(exec_lua, 'vim.treesitter.add("/foo/")')) end) - it('shows error for invalid filetype', function() - eq('.../language.lua:0: \'\' is not a valid filetype', - pcall_err(exec_lua, [[vim.treesitter.add('foo', { filetype = '' })]])) - end) - it('inspects language', function() local keys, fields, symbols = unpack(exec_lua([[ local lang = vim.treesitter.inspect_language('c') -- cgit From c5b9643bf1b0f6d5166b4abf6a7c3f29532aefeb Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 Mar 2023 10:25:10 +0000 Subject: fix(treesitter): better lang handling of get_parser() --- test/functional/treesitter/parser_spec.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 27f2e81ab2..dd36c6c9e7 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -128,7 +128,9 @@ void ui_refresh(void) it('does not get parser for empty filetype', function() insert(test_text); - eq(".../language.lua:0: '' is not a valid filetype", + 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 -- cgit From 46b73bf22cb951151de9bf0712d42e194000b677 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 9 Mar 2023 15:28:55 +0000 Subject: perf(treesitter): more efficient foldexpr --- test/functional/treesitter/parser_spec.lua | 51 +++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index dd36c6c9e7..0f00fcfe0d 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -888,18 +888,20 @@ int x = INT_MAX; it("can fold via foldexpr", function() insert(test_text) - local levels = exec_lua([[ - vim.opt.filetype = 'c' - vim.treesitter.get_parser(0, "c") - local res = {} - for i = 1, vim.api.nvim_buf_line_count(0) do - res[i] = vim.treesitter.foldexpr(i) - end - return res - ]]) + 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 + + exec_lua([[vim.treesitter.get_parser(0, "c")]]) eq({ - [1] = '>1', + [1] = '>1', [2] = '1', [3] = '1', [4] = '1', @@ -917,6 +919,33 @@ int x = INT_MAX; [16] = '3', [17] = '3', [18] = '2', - [19] = '1' }, levels) + [19] = '1' }, get_fold_levels()) + + helpers.command('1,2d') + helpers.poke_eventloop() + + exec_lua([[vim.treesitter.get_parser():parse()]]) + + helpers.poke_eventloop() + helpers.sleep(100) + + 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()) end) end) -- cgit From 58bbc2ea0b3dfed13471e8cc0447d7598be24276 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 Mar 2023 16:40:27 +0000 Subject: refactor(treesitter): add Range type aliase for Range4|Range6 --- test/functional/treesitter/parser_spec.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 0f00fcfe0d..c6ca65f9a1 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -196,7 +196,7 @@ void ui_refresh(void) local manyruns = q(100) -- First run should be at least 400x slower than an 100 subsequent runs. - local factor = is_os('win') and 300 or 400 + local factor = is_os('win') and 200 or 400 assert(factor * manyruns < firstrun, ('firstrun: %f ms, manyruns: %f ms'):format(firstrun / 1e6, manyruns / 1e6)) end) @@ -277,13 +277,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 @@ -291,12 +291,14 @@ end]] function fake_node:end_() return 3, 0, 23 end - function fake_node:range() + 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) == nil + 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() -- cgit From b466e1d289180fad212a93b173034787fd6be9a8 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Mon, 13 Mar 2023 04:15:24 +0100 Subject: test: unskip working Windows tests (#22537) Some tests that were previously not working have started to work again for unspecified reasons, so let's enable them. --- test/functional/treesitter/parser_spec.lua | 2 -- 1 file changed, 2 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index c6ca65f9a1..e872861d2a 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -7,7 +7,6 @@ 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) @@ -741,7 +740,6 @@ int x = INT_MAX; 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 = "{" -- cgit From cbbf8bd666c8419fdab80a0887948c8a36279c19 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 24 Mar 2023 14:43:14 +0000 Subject: feat(treesitter)!: deprecate top level indexes to modules (#22761) The following top level Treesitter functions have been moved: - vim.treesitter.inspect_language() -> vim.treesitter.language.inspect() - vim.treesitter.get_query_files() -> vim.treesitter.query.get_files() - vim.treesitter.set_query() -> vim.treesitter.query.set() - vim.treesitter.query.set_query() -> vim.treesitter.query.set() - vim.treesitter.get_query() -> vim.treesitter.query.get() - vim.treesitter.query.get_query() -> vim.treesitter.query.get() - vim.treesitter.parse_query() -> vim.treesitter.query.parse() - vim.treesitter.query.parse_query() -> vim.treesitter.query.parse() - vim.treesitter.add_predicate() -> vim.treesitter.query.add_predicate() - vim.treesitter.add_directive() -> vim.treesitter.query.add_directive() - vim.treesitter.list_predicates() -> vim.treesitter.query.list_predicates() - vim.treesitter.list_directives() -> vim.treesitter.query.list_directives() - vim.treesitter.query.get_range() -> vim.treesitter.get_range() - vim.treesitter.query.get_node_text() -> vim.treesitter.get_node_text() --- test/functional/treesitter/highlight_spec.lua | 6 ++-- test/functional/treesitter/language_spec.lua | 14 ++++----- test/functional/treesitter/node_spec.lua | 2 +- test/functional/treesitter/parser_spec.lua | 44 +++++++++++++-------------- 4 files changed, 33 insertions(+), 33 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 2a2311c0fa..44e6500008 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -376,7 +376,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 @@ -481,8 +481,8 @@ describe('treesitter highlighting', function() 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) + 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")) ]] diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 48e7b4b018..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.add('borklang', { path = 'borkbork.so' })")) + pcall_err(exec_lua, "parser = vim.treesitter.language.add('borklang', { path = 'borkbork.so' })")) - eq(false, exec_lua("return pcall(vim.treesitter.add, 'borklang')")) + eq(false, exec_lua("return pcall(vim.treesitter.language.add, 'borklang')")) - eq(false, exec_lua("return pcall(vim.treesitter.add, 'borklang', { path = 'borkbork.so' })")) + eq(false, exec_lua("return pcall(vim.treesitter.language.add, 'borklang', { path = 'borkbork.so' })")) eq(".../language.lua:0: no parser for 'borklang' language, see :help treesitter-parsers", - pcall_err(exec_lua, "parser = vim.treesitter.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.add("c", { symbol_name = "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.add("/foo/")')) + 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 diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index a82dce47b7..5ff73d3a8d 100644 --- a/test/functional/treesitter/node_spec.lua +++ b/test/functional/treesitter/node_spec.lua @@ -26,7 +26,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 e872861d2a..72a8cd9e9b 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -23,7 +23,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("", exec_lua("return tostring(tree)")) @@ -171,7 +171,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) @@ -184,7 +184,7 @@ void ui_refresh(void) local query, n = ... local before = vim.loop.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() return after - before @@ -203,7 +203,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 = {} @@ -232,7 +232,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 = {} @@ -326,7 +326,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$"))'.. @@ -376,7 +376,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 = {} @@ -416,7 +416,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 = {} @@ -449,7 +449,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] ] @@ -461,7 +461,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 @@ -474,7 +474,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() @@ -533,7 +533,7 @@ end]] 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 @@ -581,7 +581,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() }) @@ -603,7 +603,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 @@ -703,7 +703,7 @@ int x = INT_MAX; 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) + vim.treesitter.query.add_directive("inject-clang!", function(match, _, _, pred, metadata) metadata.language = "c" metadata.combined = true metadata.content = pred[2] @@ -741,7 +741,7 @@ int x = INT_MAX; it("should not inject bad languages", function() exec_lua([=[ - vim.treesitter.add_directive("inject-bad!", function(match, _, _, pred, metadata) + vim.treesitter.query.add_directive("inject-bad!", function(match, _, _, pred, metadata) metadata.language = "{" metadata.combined = true metadata.content = pred[2] @@ -774,7 +774,7 @@ int x = INT_MAX; 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() @@ -820,7 +820,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 @@ -840,10 +840,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 @@ -862,10 +862,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 -- cgit From 90fdaf55c913d07bc80eec6442b02d8c001659b4 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 1 Apr 2023 12:11:24 +0200 Subject: fix(tests): adapt treesitter/highlight_spec priority test Still relied on the old `@Foo`->`Foo` capture to highlight mechanism; use capture with default highlight instead. --- test/functional/treesitter/highlight_spec.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 44e6500008..faeb4befcd 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -575,14 +575,14 @@ 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 foo }{12:void}{8: main() { \} | - {8: }{12:return}{8: 42; \} | + {8:#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))}| + {8:#define foo void main() { \} | + {8: return 42; \} | {8: }} | ^ | {1:~ }| @@ -599,13 +599,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) -- cgit From d510bfbc8e447b1a60d5ec7faaa8f440eb4ef56f Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 2 Apr 2023 10:11:42 +0200 Subject: refactor: remove char_u (#22829) Closes https://github.com/neovim/neovim/issues/459 --- test/functional/treesitter/highlight_spec.lua | 14 ++++++------ test/functional/treesitter/parser_spec.lua | 32 +++++++++++++-------------- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index faeb4befcd..4e1efec404 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -413,7 +413,7 @@ describe('treesitter highlighting', function() it("supports injected languages", 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; \ } @@ -421,7 +421,7 @@ describe('treesitter highlighting', function() screen:expect{grid=[[ 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; \ | } | @@ -450,7 +450,7 @@ describe('treesitter highlighting', function() 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}; \ | } | @@ -473,7 +473,7 @@ describe('treesitter highlighting', function() 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; \ } @@ -489,7 +489,7 @@ describe('treesitter highlighting', function() 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}; \ | } | @@ -567,7 +567,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; \ } @@ -580,7 +580,7 @@ describe('treesitter highlighting', function() -- expect everything to have Constant highlight screen:expect{grid=[[ {12:int}{8: x = INT_MAX;} | - {8:#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))}| + {8:#define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))} | {8:#define foo void main() { \} | {8: return 42; \} | {8: }} | diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 72a8cd9e9b..ea9958b12a 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -627,8 +627,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 @@ -650,8 +650,8 @@ 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') @@ -661,8 +661,8 @@ int x = INT_MAX; {4, 14, 4, 17}, -- VALUE 123 {5, 15, 5, 18}, -- VALUE1 123 {6, 15, 6, 18}, -- VALUE2 123 - {2, 26, 2, 65}, -- READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) - {3, 29, 3, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) + {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) @@ -682,8 +682,8 @@ 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()) helpers.feed('ggo') @@ -694,8 +694,8 @@ int x = INT_MAX; {4, 14, 6, 18}, -- VALUE 123 -- VALUE1 123 -- VALUE2 123 - {2, 26, 3, 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) @@ -722,8 +722,8 @@ 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()) helpers.feed('ggo') @@ -734,8 +734,8 @@ int x = INT_MAX; {4, 14, 6, 18}, -- VALUE 123 -- VALUE1 123 -- VALUE2 123 - {2, 26, 3, 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) @@ -768,8 +768,8 @@ int x = INT_MAX; {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)) + {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() -- cgit From e29bc03c046b3a137c2e36b4d34c119b277d62b2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 6 Apr 2023 15:16:44 +0100 Subject: fix(treesitter): do not track ranges of the root tree (#22912) Fixes #22911 --- test/functional/treesitter/parser_spec.lua | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index ea9958b12a..9afce0b3a0 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -948,4 +948,48 @@ int x = INT_MAX; [16] = '1', [17] = '0' }, get_fold_levels()) 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) end) -- cgit From 19a793545f15bb7e0bac2fc8f705c600e8f9c9bb Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 30 Apr 2023 16:11:38 +0100 Subject: fix(treesitter): redraw added/removed injections properly (#23287) When injections are added or removed make sure to: - invoke 'changedtree' callbacks for when new trees are added. - invoke 'changedtree' callbacks for when trees are invalidated - redraw regions when languagetree children are removed --- test/functional/treesitter/highlight_spec.lua | 82 +++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 10 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 4e1efec404..dc303c564f 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,7 @@ void ui_refresh(void) } }]] -describe('treesitter highlighting', function() +describe('treesitter highlighting (C)', function() local screen before_each(function() @@ -105,13 +105,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 +274,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 +351,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; | @@ -510,7 +510,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") @@ -692,7 +692,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 +753,65 @@ 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) -- cgit From 9ff59517cbf309d31f979a49b7dc82b237ecfcc4 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 13 May 2023 12:56:21 +0200 Subject: fix(treesitter): update c queries --- test/functional/treesitter/parser_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 9afce0b3a0..60042ab2fd 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -483,7 +483,7 @@ 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) -- cgit From 6b19170d44ca56cf65542ee184d2bc89c6d622a9 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 16 May 2023 16:41:47 +0100 Subject: fix(treesitter): correctly calculate bytes for text sources (#23655) Fixes #20419 --- test/functional/treesitter/parser_spec.lua | 56 +++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 60042ab2fd..14de07639b 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -486,7 +486,6 @@ end]] 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) @@ -528,6 +527,7 @@ end]] eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) end) + it("allows to set complex ranges", function() insert(test_text) @@ -992,4 +992,58 @@ int x = INT_MAX; }, 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() + 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) end) -- cgit From 2db719f6c2b677fcbc197b02fe52764a851523b2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 3 Jun 2023 11:06:00 +0100 Subject: feat(lua): rename vim.loop -> vim.uv (#22846) --- test/functional/treesitter/parser_spec.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 14de07639b..e463382d07 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -182,11 +182,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.query.parse("c", ...) end - local after = vim.loop.hrtime() + local after = vim.uv.hrtime() return after - before ]], long_query, n) end -- cgit From c7e7f1d4b4b62c75bb54e652f25c6c6b8785a7f4 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Wed, 28 Jun 2023 03:05:09 +0900 Subject: fix(treesitter): make foldexpr work without highlighting (#24167) Problem: Treesitter fold is not updated if treesitter hightlight is not active. More precisely, updating folds requires `LanguageTree:parse()`. Solution: Call `parse()` before computing folds and compute folds when lines are added/removed. This doesn't guarantee correctness of the folds, because some changes that don't add/remove line won't update the folds even if they should (e.g. adding pair of braces). But it is good enough for most cases, while not introducing big overhead. Also, if highlighting is active, it is likely that `TSHighlighter._on_buf` already ran `parse()` (or vice versa). --- test/functional/treesitter/parser_spec.lua | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index e463382d07..d2f40bfe00 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -922,12 +922,6 @@ int x = INT_MAX; [19] = '1' }, get_fold_levels()) helpers.command('1,2d') - helpers.poke_eventloop() - - exec_lua([[vim.treesitter.get_parser():parse()]]) - - helpers.poke_eventloop() - helpers.sleep(100) eq({ [1] = '0', @@ -947,6 +941,29 @@ int x = INT_MAX; [15] = '2', [16] = '1', [17] = '0' }, get_fold_levels()) + + helpers.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('tracks the root range properly (#22911)', function() -- cgit From 11844dde81c41bded54f2383b57f8eef406f2736 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 1 Jul 2023 11:08:06 +0200 Subject: feat(treesitter): bundle markdown parser and queries (#22481) * bundle split Markdown parser from https://github.com/MDeiml/tree-sitter-markdown * add queries from https://github.com/nvim-treesitter/nvim-treesitter/tree/main * upstream `#trim!` and `#inject-language!` directives Co-authored-by: dundargoc --- test/functional/treesitter/parser_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index d2f40bfe00..7cfe5b69de 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -783,7 +783,7 @@ int x = INT_MAX; return list ]] - eq({ 'gsub!', 'offset!', 'set!' }, res_list) + eq({ 'gsub!', 'inject-language!', 'offset!', 'set!', 'trim!' }, res_list) end) end) end) -- cgit From c44d819ae1f29cd34ee3b2350b5c702caed949c3 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Fri, 7 Jul 2023 19:12:46 +0900 Subject: fix(treesitter): update folds in all relevant windows (#24230) Problem: When using treesitter foldexpr, * :diffput/get open diff folds, and * folds are not updated in other windows that contain the updated buffer. Solution: Update folds in all windows that contain the updated buffer and use expr foldmethod. --- test/functional/treesitter/fold_spec.lua | 361 +++++++++++++++++++++++++++++ test/functional/treesitter/parser_spec.lua | 81 ------- 2 files changed, 361 insertions(+), 81 deletions(-) create mode 100644 test/functional/treesitter/fold_spec.lua (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua new file mode 100644 index 0000000000..9ed86e87f1 --- /dev/null +++ b/test/functional/treesitter/fold_spec.lua @@ -0,0 +1,361 @@ +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"]]) + + 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) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 7cfe5b69de..ae26b92f52 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -885,87 +885,6 @@ int x = INT_MAX; end) end) - it("can fold via foldexpr", function() - insert(test_text) - - 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 - - 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()) - - helpers.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()) - - helpers.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('tracks the root range properly (#22911)', function() insert([[ int main() { -- cgit From 41cefe513054edd1c12f235125220dbc6b4d9451 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 27 Jul 2023 12:45:08 +0200 Subject: build(deps): bump tree-sitter-c to v0.20.4 (#24495) --- test/functional/treesitter/parser_spec.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index ae26b92f52..f0144e6f6d 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -194,8 +194,8 @@ void ui_refresh(void) local firstrun = q(1) local manyruns = q(100) - -- First run should be at least 400x slower than an 100 subsequent runs. - local factor = is_os('win') and 200 or 400 + -- 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) @@ -765,9 +765,9 @@ int x = INT_MAX; 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 + {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()) -- cgit From 0211f889b9538f7df5fbcb06273d273fb071efff Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 7 Aug 2023 18:22:36 +0100 Subject: fix(treesitter): make sure injections don't return empty ranges (#24595) When an injection has not set include children, make sure not to add the injection if no ranges are determined. This could happen when there is an injection with a child that has the same range as itself. e.g. consider this Makefile snippet ```make foo: $(VAR) ``` Line 2 has an injection for bash and a make variable reference. If include-children isn't set (default), then there is no range on line 2 to inject since the variable reference needs to be excluded. This caused the language tree to return an empty range, which the parser now interprets to mean the full buffer. This caused makefiles to have completely broken highlighting. --- test/functional/treesitter/parser_spec.lua | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index f0144e6f6d..da84f435c9 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -982,4 +982,38 @@ int x = INT_MAX; 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() + ]] + + 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() + ]] + + 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) + end) -- cgit From 31c4ed26bc278282898123ad21bb6fead401fd6f Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 11 Aug 2023 17:05:17 +0200 Subject: feat(treesitter): add injection language fallback (#24659) * feat(treesitter): add injection language fallback Problem: injection languages are often specified via aliases (e.g., filetype or in upper case), requiring custom directives. Solution: include lookup logic (try as parser name, then filetype, then lowercase) in LanguageTree itself and remove `#inject-language` directive. Co-authored-by: Lewis Russell --- test/functional/treesitter/parser_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index da84f435c9..834998bae7 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -783,7 +783,7 @@ int x = INT_MAX; return list ]] - eq({ 'gsub!', 'inject-language!', 'offset!', 'set!', 'trim!' }, res_list) + eq({ 'gsub!', 'offset!', 'set!', 'trim!' }, res_list) end) end) end) -- cgit From 2ca076e45fb3f1c08f6a1a374834df0701b8d778 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 10 Aug 2023 14:21:56 +0100 Subject: feat(treesitter)!: incremental injection parsing Problem: Treesitter highlighting is slow for large files with lots of injections. Solution: Only parse injections we are going to render during a redraw cycle. --- - `LanguageTree:parse()` will no longer parse injections by default and now requires an explicit range argument to be passed. - `TSHighlighter` now parses injections incrementally during on_win callbacks for the line range being rendered. - Plugins which require certain injections to be parsed must run `parser:parse({ start_row, end_row })` before using the tree. --- test/functional/treesitter/parser_spec.lua | 91 ++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 16 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 834998bae7..cc833a67cc 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -1,6 +1,7 @@ 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 @@ -502,22 +503,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 } } } @@ -542,7 +533,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 @@ -638,9 +629,11 @@ int x = INT_MAX; describe("when parsing regions independently", function() it("should inject a language", function() exec_lua([[ + vim.g.__ts_debug = 1 parser = vim.treesitter.get_parser(0, "c", { injections = { c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}}) + parser:parse(true) ]]) eq("table", exec_lua("return type(parser:children().c)")) @@ -673,6 +666,7 @@ int x = INT_MAX; parser = vim.treesitter.get_parser(0, "c", { injections = { c = "(preproc_def (preproc_arg) @c @combined) (preproc_function_def value: (preproc_arg) @c @combined)"}}) + parser:parse(true) ]]) eq("table", exec_lua("return type(parser:children().c)")) @@ -713,6 +707,7 @@ int x = INT_MAX; injections = { c = "(preproc_def ((preproc_arg) @_c (#inject-clang! @_c)))" .. "(preproc_function_def value: ((preproc_arg) @_a (#inject-clang! @_a)))"}}) + parser:parse(true) ]=]) eq("table", exec_lua("return type(parser:children().c)")) @@ -760,6 +755,7 @@ int x = INT_MAX; 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)"}}) + parser:parse(true) ]]) eq("table", exec_lua("return type(parser:children().c)")) @@ -800,6 +796,7 @@ int x = INT_MAX; local result = exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { injections = { c = "(preproc_def (preproc_arg) @c)"}}) + parser:parse(true) local sub_tree = parser:language_for_range({1, 18, 1, 19}) @@ -951,7 +948,7 @@ int x = INT_MAX; local r = exec_lua([[ local parser = vim.treesitter.get_string_parser(..., 'lua') - parser:parse() + parser:parse(true) local ranges = {} parser:for_each_tree(function(tstree, tree) ranges[tree:lang()] = { tstree:root():range(true) } @@ -997,7 +994,7 @@ int x = INT_MAX; vimdoc = "((codeblock (language) @injection.language (code) @injection.content))" } }) - parser1:parse() + parser1:parse(true) ]] eq(0, exec_lua("return #vim.tbl_keys(parser1:children())")) @@ -1008,7 +1005,7 @@ int x = INT_MAX; vimdoc = "((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))" } }) - parser2:parse() + parser2:parse(true) ]] eq(1, exec_lua("return #vim.tbl_keys(parser2:children())")) @@ -1016,4 +1013,66 @@ int x = INT_MAX; 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) + end) -- cgit From 8179d68dc1a90f47bfb307d73e71adc98883ae00 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 13 Aug 2023 08:03:56 +0100 Subject: fix(treesitter): logger memory leak --- test/functional/treesitter/parser_spec.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index cc833a67cc..ae3b0483c5 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -9,10 +9,13 @@ local pcall_err = helpers.pcall_err local feed = helpers.feed local is_os = helpers.is_os -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([[ @@ -629,7 +632,6 @@ int x = INT_MAX; describe("when parsing regions independently", function() it("should inject a language", function() exec_lua([[ - vim.g.__ts_debug = 1 parser = vim.treesitter.get_parser(0, "c", { injections = { c = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)"}}) -- cgit From 3836eeb90182cc610ea15bf6bd83cbc720bb7c64 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 12 Aug 2023 18:12:49 +0200 Subject: feat(treesitter): update C queries from upstream --- test/functional/treesitter/highlight_spec.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index dc303c564f..c88c5cd3e5 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -580,9 +580,9 @@ describe('treesitter highlighting (C)', function() -- expect everything to have Constant highlight screen:expect{grid=[[ {12:int}{8: x = INT_MAX;} | - {8:#define READ_STRING(x, y) (char *)read_string((x), (size_t)(y))} | - {8:#define foo void main() { \} | - {8: return 42; \} | + {8:#define READ_STRING(x, y) (}{12:char}{8: *)read_string((x), (}{12:size_t}{8:)(y))} | + {8:#define foo }{12:void}{8: main() { \} | + {8: }{12:return}{8: 42; \} | {8: }} | ^ | {1:~ }| -- cgit From fc0ee871de2f56dbb80879c912203a6372c54e1c Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 12 Aug 2023 17:54:04 +0200 Subject: fix(treesitter)!: remove deprecated legacy injection format --- test/functional/treesitter/highlight_spec.lua | 4 +- test/functional/treesitter/parser_spec.lua | 63 ++------------------------- 2 files changed, 6 insertions(+), 61 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index c88c5cd3e5..0528370e2a 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -442,7 +442,7 @@ describe('treesitter highlighting (C)', function() 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}}) @@ -480,7 +480,7 @@ describe('treesitter highlighting (C)', function() ]]) exec_lua [[ - local injection_query = "(preproc_def (preproc_arg) @c) (preproc_function_def value: (preproc_arg) @c)" + 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) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index ae3b0483c5..ae7cf58293 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -634,7 +634,7 @@ 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) ]]) @@ -667,7 +667,7 @@ 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) ]]) @@ -696,67 +696,12 @@ int x = INT_MAX; end) end) - describe("when providing parsing information through a directive", function() - it("should inject a language", function() - exec_lua([=[ - vim.treesitter.query.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)))"}}) - parser:parse(true) - ]=]) - - eq("table", exec_lua("return type(parser:children().c)")) - eq(2, exec_lua("return #parser:children().c:trees()")) - eq({ - {0, 0, 7, 0}, -- root tree - {3, 14, 5, 18}, -- VALUE 123 - -- VALUE1 123 - -- VALUE2 123 - {1, 26, 2, 66} -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) - }, get_ranges()) - - helpers.feed('ggo') - eq("table", exec_lua("return type(parser:children().c)")) - eq(2, exec_lua("return #parser:children().c:trees()")) - eq({ - {0, 0, 8, 0}, -- root tree - {4, 14, 6, 18}, -- VALUE 123 - -- VALUE1 123 - -- VALUE2 123 - {2, 26, 3, 66} -- READ_STRING(x, y) (char *)read_string((x), (size_t)(y)) - -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) - }, get_ranges()) - end) - - it("should not inject bad languages", function() - exec_lua([=[ - vim.treesitter.query.add_directive("inject-bad!", function(match, _, _, pred, metadata) - metadata.language = "{" - metadata.combined = true - metadata.content = pred[2] - end) - - parser = vim.treesitter.get_parser(0, "c", { - injections = { - c = "(preproc_function_def value: ((preproc_arg) @_a (#inject-bad! @_a)))"}}) - ]=]) - end) - end) - describe("when using the offset directive", function() it("should shift the range by the directive amount", function() exec_lua([[ parser = vim.treesitter.get_parser(0, "c", { 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) ]]) @@ -797,7 +742,7 @@ 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}) -- cgit From c6ec7fa8d741d6301701067ecd095bf02e7a741a Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Tue, 22 Aug 2023 00:51:38 -0400 Subject: feat(treesitter): add 'injection.self' and 'injection.parent' Co-authored-by: ObserverOfTime --- test/functional/treesitter/parser_spec.lua | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index ae7cf58293..56af0c8738 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -696,6 +696,39 @@ int x = INT_MAX; end) 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_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') + 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) + describe("when using the offset directive", function() it("should shift the range by the directive amount", function() exec_lua([[ -- cgit From 6e45567b498ca8455aaf3628c10de997ac070ee1 Mon Sep 17 00:00:00 2001 From: nwounkn Date: Tue, 29 Aug 2023 13:48:23 +0500 Subject: fix(treesitter): fix TSNode:tree() double free (#24796) Problem: `push_tree`, every time its called for the same TSTree with `do_copy=false` argument, creates a new userdata for it. Each userdata, when garbage collected, frees the same TSTree C object. Solution: Add flag to userdata, which indicates, should C object, which userdata points to, be freed, when userdata is garbage collected. --- test/functional/treesitter/node_spec.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index 5ff73d3a8d..72508ba958 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,17 @@ 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('can move between siblings', function() insert([[ int main(int x, int y, int z) { -- cgit From 50a03c0e9975925e3198a2741c5b9fc0ad727e84 Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 29 Aug 2023 11:21:57 +0200 Subject: fix(treesitter): fix another TSNode:tree() double free Unfortunately the gc=false objects can refer to a dangling tree if the gc=true tree was freed first. This reuses the same tree object as the node itself is keeping alive via the uservalue of the node userdata. (wrapped in a table due to lua 5.1 restrictions) --- test/functional/treesitter/node_spec.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index 72508ba958..eef75d0e91 100644 --- a/test/functional/treesitter/node_spec.lua +++ b/test/functional/treesitter/node_spec.lua @@ -26,6 +26,20 @@ describe('treesitter node API', function() 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) { -- cgit From 845d5b8b64190e0e09a6a6dd97bdbc0e6f96eb02 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Wed, 19 Jul 2023 05:02:49 -0400 Subject: feat(treesitter): improve query error message --- test/functional/treesitter/parser_spec.lua | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 56af0c8738..37dde37a64 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -1055,4 +1055,44 @@ int x = INT_MAX; ]]) 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 1:30. Invalid capture name "ok.capture":\n'.. + '((identifier) @id (#eq? @id @ok.capture))\n'.. + ' ^', + '((identifier) @id (#eq? @id @ok.capture))') + end) end) -- cgit From dd0e77d48a843fc9730fe4ef7567330daf8dfb81 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Aug 2023 14:11:15 +0100 Subject: fix(query_error): multiline bug --- test/functional/treesitter/parser_spec.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 37dde37a64..8222bb59c4 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -1090,9 +1090,9 @@ int x = INT_MAX; -- Invalid capture name test( - '.../query.lua:0: Query error at 1:30. Invalid capture name "ok.capture":\n'.. - '((identifier) @id (#eq? @id @ok.capture))\n'.. - ' ^', - '((identifier) @id (#eq? @id @ok.capture))') + '.../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) end) -- cgit From 908843df61fc80f160392edc0af0d1672a8f9a68 Mon Sep 17 00:00:00 2001 From: L Lllvvuu Date: Wed, 13 Sep 2023 16:51:54 -0700 Subject: fix(languagetree): apply `resolve_lang` to `metadata['injection.language']` `resolve_lang` is applied to `@injection.language` when it's supplied as a capture: https://github.com/neovim/neovim/blob/f5953edbac14febce9d4f8a3c35bdec1eae26fbe/runtime/lua/vim/treesitter/languagetree.lua#L766-L768 If we want to support `metadata['injection.language']` (as per #22518 and [tree-sitter upstream](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection)) then the behavior should be consistent. Fixes: nvim-treesitter/nvim-treesitter#4918 --- test/functional/treesitter/highlight_spec.lua | 117 +++++++++++++++----------- 1 file changed, 70 insertions(+), 47 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 0528370e2a..0aa0cdd6d6 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -85,6 +85,56 @@ void ui_refresh(void) } }]] +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 @@ -411,34 +461,9 @@ describe('treesitter highlighting (C)', function() end) it("supports injected languages", function() - insert([[ - int x = INT_MAX; - #define READ_STRING(x, y) (char *)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 *)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", { @@ -448,26 +473,24 @@ describe('treesitter highlighting (C)', function() 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} *)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() -- cgit From e353c869cea4541d00d627ec82724d3f247225a3 Mon Sep 17 00:00:00 2001 From: L Lllvvuu Date: Tue, 19 Sep 2023 21:41:07 -0700 Subject: fix(languagetree): don't treat unparsed nodes as occupying full range This is incorrect in the following scenario: 1. The language tree is Lua > Vim > Lua. 2. An edit simultaneously wipes out the `_regions` of all nodes, while taking the Vim injection off-screen. 3. The Vim injection is not re-parsed, so the child Lua `_regions` is still `nil`. 4. The child Lua is assumed, incorrectly, to occupy the whole document. 5. This causes the injections to be parsed again, resulting in Lua > Vim > Lua > Vim. 6. Now, by the same process, Vim ends up with its range assumed over the whole document. Now the parse is broken and results in broken highlighting and poor performance. It should be fine to instead treat an unparsed node as occupying nothing (i.e. effectively non-existent). Since, either: - The parent was just parsed, hence defining `_regions` - The parent was not just parsed, in which case this node doesn't need to be parsed either. Also, the name `has_regions` is confusing; it seems to simply mean the opposite of "root" or "full_document". However, this PR does not touch it. --- test/functional/treesitter/highlight_spec.lua | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 0aa0cdd6d6..e037c9e215 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -838,3 +838,67 @@ describe('treesitter highlighting (help)', function() 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--[[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("3joddkoddkoddkoddk0") + + 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) -- cgit From 9ce1623837a817c3f4f5deff9c8ba862578b6009 Mon Sep 17 00:00:00 2001 From: Till Bungert Date: Sun, 1 Oct 2023 21:10:51 +0200 Subject: feat(treesitter): add foldtext with treesitter highlighting (#25391) --- test/functional/treesitter/fold_spec.lua | 172 +++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua index 9ed86e87f1..8cf9a91bbd 100644 --- a/test/functional/treesitter/fold_spec.lua +++ b/test/functional/treesitter/fold_spec.lua @@ -359,3 +359,175 @@ void ui_refresh(void) 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); + } + } +}]] + + it('displays highlighted content', function() + local screen = Screen.new(60, 21) + screen:attach() + + 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 = [[ +{1:^void}{2: }{3:qsort}{4:(}{1:void}{2: }{5:*}{3:base}{4:,}{2: }{1:size_t}{2: }{3:nel}{4:,}{2: }{1:size_t}{2: }{3:width}{4:,}{2: }{1:int}{2: }{4:(}{5:*}{3:compa}| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| +{6:~ }| + | +]], + attr_ids = { + [1] = { + foreground = Screen.colors.SeaGreen4, + background = Screen.colors.LightGrey, + bold = true, + }, + [2] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Blue4 }, + [3] = { background = Screen.colors.LightGrey, foreground = Screen.colors.DarkCyan }, + [4] = { background = Screen.colors.LightGrey, foreground = Screen.colors.SlateBlue }, + [5] = { + foreground = Screen.colors.Brown, + background = Screen.colors.LightGrey, + bold = true, + }, + [6] = { foreground = Screen.colors.Blue, bold = true }, + }, + }) + end) + + it('handles deep nested captures', function() + local screen = Screen.new(60, 21) + screen:attach() + + 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('ggjVGkzf') + + screen:expect({ + grid = [[ +function FoldInfo.new() | +{1:^ }{2:return}{1: }{3:setmetatable({}{1:·····································}| + | +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| +{4:~ }| + | +]], + attr_ids = { + [1] = { foreground = Screen.colors.Blue4, background = Screen.colors.LightGray }, + [2] = { + foreground = Screen.colors.Brown, + bold = true, + background = Screen.colors.LightGray, + }, + [3] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.LightGray }, + [4] = { bold = true, foreground = Screen.colors.Blue }, + }, + }) + end) + + it('falls back to default', function() + local screen = Screen.new(60, 21) + screen:attach() + + 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}| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| +{2:~ }| + | +]], + attr_ids = { + [1] = { foreground = Screen.colors.Blue4, background = Screen.colors.LightGray }, + [2] = { bold = true, foreground = Screen.colors.Blue }, + }, + }) + end) +end) -- cgit From 3af59a415c98afc42755308e56912b302ad5eb3d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 3 Oct 2023 13:07:03 +0800 Subject: fix(treesitter): make Visual hl work consistently with foldtext (#25484) Problem: Visual highlight is inconsistent on a folded line with treesitter foldtext. Solution: Don't added Folded highlight as it is already in background. --- test/functional/treesitter/fold_spec.lua | 179 +++++++++++-------------------- 1 file changed, 60 insertions(+), 119 deletions(-) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua index 8cf9a91bbd..a8abbc002b 100644 --- a/test/functional/treesitter/fold_spec.lua +++ b/test/functional/treesitter/fold_spec.lua @@ -381,64 +381,42 @@ void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, con } } }]] + local screen - it('displays highlighted content', function() - local screen = Screen.new(60, 21) + 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 = [[ -{1:^void}{2: }{3:qsort}{4:(}{1:void}{2: }{5:*}{3:base}{4:,}{2: }{1:size_t}{2: }{3:nel}{4:,}{2: }{1:size_t}{2: }{3:width}{4:,}{2: }{1:int}{2: }{4:(}{5:*}{3:compa}| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| -{6:~ }| - | -]], - attr_ids = { - [1] = { - foreground = Screen.colors.SeaGreen4, - background = Screen.colors.LightGrey, - bold = true, - }, - [2] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Blue4 }, - [3] = { background = Screen.colors.LightGrey, foreground = Screen.colors.DarkCyan }, - [4] = { background = Screen.colors.LightGrey, foreground = Screen.colors.SlateBlue }, - [5] = { - foreground = Screen.colors.Brown, - background = Screen.colors.LightGrey, - bold = true, - }, - [6] = { foreground = Screen.colors.Blue, bold = true }, - }, - }) + 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() - local screen = Screen.new(60, 21) - screen:attach() - command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext() updatetime=50]]) insert([[ function FoldInfo.new() @@ -448,86 +426,49 @@ function FoldInfo.new() levels0 = {}, levels = {}, }, FoldInfo) -end - ]]) +end]]) exec_lua([[vim.treesitter.get_parser(0, "lua")]]) - feed('ggjVGkzf') - - screen:expect({ - grid = [[ -function FoldInfo.new() | -{1:^ }{2:return}{1: }{3:setmetatable({}{1:·····································}| - | -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| -{4:~ }| - | -]], - attr_ids = { - [1] = { foreground = Screen.colors.Blue4, background = Screen.colors.LightGray }, - [2] = { - foreground = Screen.colors.Brown, - bold = true, - background = Screen.colors.LightGray, - }, - [3] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.LightGray }, - [4] = { bold = true, foreground = Screen.colors.Blue }, - }, - }) + 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') + 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() - local screen = Screen.new(60, 21) - screen:attach() - 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}| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| -{2:~ }| - | -]], - attr_ids = { - [1] = { foreground = Screen.colors.Blue4, background = Screen.colors.LightGray }, - [2] = { bold = true, foreground = Screen.colors.Blue }, - }, - }) + screen:expect{grid=[[ + {1:^+-- 19 lines: void qsort(void *base, size_t nel, size_t widt}| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} end) end) -- cgit From 72ed99319dd662f0e35b58e888b57f98ac3b3eec Mon Sep 17 00:00:00 2001 From: Dmytro Soltys Date: Mon, 27 Nov 2023 13:34:32 +0100 Subject: fix(treesitter): don't invalidate parser when discovering injections When parsing with a range, languagetree looks up injections and adds them if needed. This explicitly invalidates parser, making `is_valid` report `false` both when including and excluding children. This is an attempt to describe desired behaviour of `is_valid` in tests, with what ended up being a single line change to satisfy them. --- test/functional/treesitter/parser_spec.lua | 117 +++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) (limited to 'test/functional/treesitter') diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 8222bb59c4..6f386115ae 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -1095,4 +1095,121 @@ int x = INT_MAX; ' ^', '((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') + + 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) -- cgit