aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2023-08-10 14:21:56 +0100
committerLewis Russell <me@lewisr.dev>2023-08-12 16:11:36 +0100
commit2ca076e45fb3f1c08f6a1a374834df0701b8d778 (patch)
tree3338df585168c4e6455137757519ca0bcd211c8c /test
parent5a25dcc5a4c73f50902432e32335ab073950cceb (diff)
downloadrneovim-2ca076e45fb3f1c08f6a1a374834df0701b8d778.tar.gz
rneovim-2ca076e45fb3f1c08f6a1a374834df0701b8d778.tar.bz2
rneovim-2ca076e45fb3f1c08f6a1a374834df0701b8d778.zip
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.
Diffstat (limited to 'test')
-rw-r--r--test/functional/treesitter/parser_spec.lua91
-rw-r--r--test/helpers.lua8
2 files changed, 80 insertions, 19 deletions
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)
diff --git a/test/helpers.lua b/test/helpers.lua
index 8f06311a3c..f0e8576a3a 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -570,21 +570,23 @@ function module.concat_tables(...)
end
--- @param str string
---- @param leave_indent? boolean
+--- @param leave_indent? integer
--- @return string
function module.dedent(str, leave_indent)
-- find minimum common indent across lines
- local indent = nil
+ local indent --- @type string?
for line in str:gmatch('[^\n]+') do
local line_indent = line:match('^%s+') or ''
if indent == nil or #line_indent < #indent then
indent = line_indent
end
end
- if indent == nil or #indent == 0 then
+
+ if not indent or #indent == 0 then
-- no minimum common indent
return str
end
+
local left_indent = (' '):rep(leave_indent or 0)
-- create a pattern for the indent
indent = indent:gsub('%s', '[ \t]')