From 72ed99319dd662f0e35b58e888b57f98ac3b3eec Mon Sep 17 00:00:00 2001 From: Dmytro Soltys Date: Mon, 27 Nov 2023 13:34:32 +0100 Subject: [PATCH] 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. --- runtime/lua/vim/treesitter/languagetree.lua | 2 - test/functional/treesitter/parser_spec.lua | 117 ++++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 670f2797b7..0171b416cd 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -508,7 +508,6 @@ function LanguageTree:add_child(lang) end self._children[lang] = child - self:invalidate() self:_do_callback('child_added', self._children[lang]) return self._children[lang] @@ -524,7 +523,6 @@ function LanguageTree:remove_child(lang) if child then self._children[lang] = nil child:destroy() - self:invalidate() self:_do_callback('child_removed', child) end end 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)