diff options
author | Lewis Russell <lewis6991@gmail.com> | 2023-08-07 18:22:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-07 18:22:36 +0100 |
commit | 0211f889b9538f7df5fbcb06273d273fb071efff (patch) | |
tree | b50f0ab1471d60b21eca8e31361745f4ef2a5a69 | |
parent | ce792db5b806b41d5d687e3b09da9ab2b6be0307 (diff) | |
download | rneovim-0211f889b9538f7df5fbcb06273d273fb071efff.tar.gz rneovim-0211f889b9538f7df5fbcb06273d273fb071efff.tar.bz2 rneovim-0211f889b9538f7df5fbcb06273d273fb071efff.zip |
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.
-rw-r--r-- | runtime/lua/vim/treesitter/languagetree.lua | 16 | ||||
-rw-r--r-- | test/functional/treesitter/parser_spec.lua | 34 |
2 files changed, 47 insertions, 3 deletions
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 0d4a1a54dd..4b2628609a 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -110,6 +110,10 @@ function LanguageTree.new(source, lang, opts) ---@type LanguageTreeOpts opts = opts or {} + if source == 0 then + source = vim.api.nvim_get_current_buf() + end + local injections = opts.injections or {} local self = setmetatable({ _source = source, @@ -561,11 +565,13 @@ end ---@param node TSNode ---@param source string|integer ---@param metadata TSMetadata +---@param include_children boolean ---@return Range6[] local function get_node_ranges(node, source, metadata, include_children) local range = vim.treesitter.get_range(node, source, metadata) + local child_count = node:named_child_count() - if include_children then + if include_children or child_count == 0 then return { range } end @@ -573,7 +579,8 @@ local function get_node_ranges(node, source, metadata, include_children) local srow, scol, sbyte, erow, ecol, ebyte = Range.unpack6(range) - for i = 0, node:named_child_count() - 1 do + -- We are excluding children so we need to mask out their ranges + for i = 0, child_count - 1 do local child = node:named_child(i) local c_srow, c_scol, c_sbyte, c_erow, c_ecol, c_ebyte = child:range(true) if c_srow > srow or c_scol > scol then @@ -604,7 +611,10 @@ end ---@param combined boolean ---@param ranges Range6[] local function add_injection(t, tree_index, pattern, lang, combined, ranges) - assert(type(lang) == 'string') + if #ranges == 0 then + -- Make sure not to add an empty range set as this is interpreted to mean the whole buffer. + return + end -- Each tree index should be isolated from the other nodes. if not t[tree_index] then 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) |