mirror of
https://github.com/neovim/neovim.git
synced 2026-04-27 17:54:10 +00:00
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 <me@lewisr.dev>
This commit is contained in:
@@ -635,6 +635,29 @@ local function add_injection(t, tree_index, pattern, lang, combined, ranges)
|
||||
table.insert(t[tree_index][lang][pattern].regions, ranges)
|
||||
end
|
||||
|
||||
-- TODO(clason): replace by refactored `ts.has_parser` API (without registering)
|
||||
---@param lang string parser name
|
||||
---@return boolean # true if parser for {lang} exists on rtp
|
||||
local has_parser = function(lang)
|
||||
return vim._ts_has_language(lang)
|
||||
or #vim.api.nvim_get_runtime_file('parser/' .. lang .. '.*', false) > 0
|
||||
end
|
||||
|
||||
--- Return parser name for language (if exists) or filetype (if registered and exists)
|
||||
---
|
||||
---@param alias string language or filetype name
|
||||
---@return string? # resolved parser name
|
||||
local function resolve_lang(alias)
|
||||
if has_parser(alias) then
|
||||
return alias
|
||||
end
|
||||
|
||||
local lang = vim.treesitter.language.get_lang(alias)
|
||||
if lang and has_parser(lang) then
|
||||
return lang
|
||||
end
|
||||
end
|
||||
|
||||
---@private
|
||||
--- Extract injections according to:
|
||||
--- https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
|
||||
@@ -649,10 +672,10 @@ function LanguageTree:_get_injection(match, metadata)
|
||||
|
||||
for id, node in pairs(match) do
|
||||
local name = self._injection_query.captures[id]
|
||||
|
||||
-- Lang should override any other language tag
|
||||
if name == 'injection.language' then
|
||||
lang = vim.treesitter.get_node_text(node, self._source, { metadata = metadata[id] })
|
||||
local text = vim.treesitter.get_node_text(node, self._source, { metadata = metadata[id] })
|
||||
lang = resolve_lang(text) or resolve_lang(text:lower())
|
||||
elseif name == 'injection.content' then
|
||||
ranges = get_node_ranges(node, self._source, metadata[id], include_children)
|
||||
end
|
||||
|
||||
@@ -541,33 +541,6 @@ local directive_handlers = {
|
||||
metadata.range = { start_row, start_col, end_row, end_col }
|
||||
end
|
||||
end,
|
||||
-- Set injection language from node text, interpreted first as language and then as filetype
|
||||
-- Example: (#inject-language! @_lang)
|
||||
['inject-language!'] = function(match, _, bufnr, pred, metadata)
|
||||
local id = pred[2]
|
||||
local node = match[id]
|
||||
if not node then
|
||||
return
|
||||
end
|
||||
|
||||
-- TODO(clason): replace by refactored `ts.has_parser` API
|
||||
local has_parser = function(lang)
|
||||
return vim._ts_has_language(lang)
|
||||
or #vim.api.nvim_get_runtime_file('parser/' .. lang .. '.*', false) > 0
|
||||
end
|
||||
|
||||
local alias = vim.treesitter.get_node_text(node, bufnr, { metadata = metadata[id] })
|
||||
if not alias then
|
||||
return
|
||||
elseif has_parser(alias) then
|
||||
metadata['injection.language'] = alias
|
||||
else
|
||||
local lang = vim.treesitter.language.get_lang(alias)
|
||||
if lang and has_parser(lang) then
|
||||
metadata['injection.language'] = lang
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
--- Adds a new predicate to be used in queries
|
||||
|
||||
Reference in New Issue
Block a user