feat(treesitter)!: use return values in language.add()

Problem: No clear way to check whether parsers are available for a given
language.

Solution: Make `language.add()` return `true` if a parser was
successfully added and `nil` otherwise. Use explicit `assert` instead of
relying on thrown errors.
This commit is contained in:
Christian Clason
2024-09-15 14:19:08 +02:00
parent 041d98fe8d
commit 99e0facf3a
6 changed files with 49 additions and 32 deletions

View File

@@ -62,8 +62,22 @@ function M.require_language(lang, path, silent, symbol_name)
return installed
end
M.add(lang, opts)
return true
return M.add(lang, opts)
end
--- Load wasm or native parser (wrapper)
--- todo(clason): move to C
---
---@param path string Path of parser library
---@param lang string Language name
---@param symbol_name? string Internal symbol name for the language to load (default lang)
---@return boolean? True if parser is loaded
local function loadparser(path, lang, symbol_name)
if vim.endswith(path, '.wasm') then
return vim._ts_add_language_from_wasm and vim._ts_add_language_from_wasm(path, lang)
else
return vim._ts_add_language_from_object(path, lang, symbol_name)
end
end
---@class vim.treesitter.language.add.Opts
@@ -77,10 +91,18 @@ end
--- Load parser with name {lang}
---
--- Parsers are searched in the `parser` runtime directory, or the provided {path}
--- Parsers are searched in the `parser` runtime directory, or the provided {path}.
--- Can be used to check for available parsers before enabling treesitter features, e.g.,
--- ```lua
--- if vim.treesitter.language.add('markdown') then
--- vim.treesitter.start(bufnr, 'markdown')
--- end
--- ```
---
---@param lang string Name of the parser (alphanumerical and `_` only)
---@param opts? vim.treesitter.language.add.Opts Options:
---@return boolean? True if parser is loaded
---@return string? Error if parser cannot be loaded
function M.add(lang, opts)
opts = opts or {}
local path = opts.path
@@ -96,30 +118,25 @@ function M.add(lang, opts)
lang = lang:lower()
if vim._ts_has_language(lang) then
return
return true
end
if path == nil then
-- allow only safe language names when looking for libraries to load
if not (lang and lang:match('[%w_]+') == lang) then
error("'" .. lang .. "' is not a valid language name")
return nil, string.format('Invalid language name "%s"', lang)
end
local fname = 'parser/' .. lang .. '.*'
local paths = api.nvim_get_runtime_file(fname, false)
if #paths == 0 then
error("no parser for '" .. lang .. "' language, see :help treesitter-parsers")
return nil, string.format('No parser for language "%s"', lang)
end
path = paths[1]
end
if vim.endswith(path, '.wasm') then
if not vim._ts_add_language_from_wasm then
error(string.format("Unable to load wasm parser '%s': not built with ENABLE_WASMTIME ", path))
end
vim._ts_add_language_from_wasm(path, lang)
else
vim._ts_add_language_from_object(path, lang, symbol_name)
end
return loadparser(path, lang, symbol_name) or nil,
string.format('Cannot load parser %s for language "%s"', path, lang)
end
--- @param x string|string[]

View File

@@ -108,7 +108,7 @@ LanguageTree.__index = LanguageTree
---@param opts vim.treesitter.LanguageTree.new.Opts?
---@return vim.treesitter.LanguageTree parser object
function LanguageTree.new(source, lang, opts)
language.add(lang)
assert(language.add(lang))
opts = opts or {}
if source == 0 then
@@ -734,7 +734,7 @@ 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)
-- TODO(clason): replace by refactored `ts.has_parser` API (without side effects)
--- The result of this function is cached to prevent nvim_get_runtime_file from being
--- called too often
--- @param lang string parser name

View File

@@ -247,8 +247,7 @@ end)
---
---@see [vim.treesitter.query.get()]
M.parse = memoize('concat-2', function(lang, query)
language.add(lang)
assert(language.add(lang))
local ts_query = vim._ts_parse_query(lang, query)
return Query.new(lang, ts_query)
end)