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.
This commit is contained in:
L Lllvvuu 2023-09-19 21:41:07 -07:00 committed by Lewis Russell
parent b7763d7f6b
commit e353c869ce
2 changed files with 66 additions and 2 deletions

View File

@ -650,8 +650,8 @@ function LanguageTree:included_regions()
return self._regions
end
if not self._has_regions or next(self._trees) == nil then
-- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1}
if not self._has_regions then
-- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range)
return { {} }
end

View File

@ -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--[[<ESC>04x")
screen:expect{grid=[[
{2:^function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
|
{2:local} {3:lorem} {2:=} {1:{} |
{3:ipsum} {2:=} {1:{},} |
{3:bar} {2:=} {1:{},} |
{1:}} |
|
]]}
-- spam newline insert/delete to invalidate Lua > Vim > Lua region
feed("3jo<ESC>ddko<ESC>ddko<ESC>ddko<ESC>ddk0")
screen:expect{grid=[[
{2:function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
|
{2:local} {3:lorem} {2:=} {1:{} |
^ {3:ipsum} {2:=} {1:{},} |
{3:bar} {2:=} {1:{},} |
{1:}} |
|
]]}
end)
end)