mirror of
https://github.com/nvim-treesitter/nvim-treesitter.git
synced 2026-07-21 12:50:09 -04:00
Problem: Many parsers require node/npm to evaluate the `grammar.js` before being able to generate a parser from it. Solution: Generate from `grammar.json` instead, which is fully resolved. Drops `node` and `npm` as (optional) requirements for nvim-treesitter. Note that this requires parsers to commit the generated json iff the grammar requires evaluation (which is currently the case for all tracked languages).
182 lines
5.4 KiB
Lua
182 lines
5.4 KiB
Lua
local install = require('nvim-treesitter.install')
|
|
local parsers = require('nvim-treesitter.parsers')
|
|
local config = require('nvim-treesitter.config')
|
|
local util = require('nvim-treesitter.util')
|
|
local tsq = vim.treesitter.query
|
|
local health = vim.health
|
|
|
|
local M = {}
|
|
|
|
local NVIM_TREESITTER_MINIMUM_ABI = 13
|
|
|
|
---@return string|nil
|
|
local function ts_cli_version()
|
|
if vim.fn.executable('tree-sitter') == 1 then
|
|
local result = assert(vim.system({ 'tree-sitter', '-V' }):wait().stdout)
|
|
return vim.split(result, '\n')[1]:match('[^tree%psitter ].*')
|
|
end
|
|
end
|
|
|
|
local function install_health()
|
|
health.start('Requirements')
|
|
|
|
if vim.fn.has('nvim-0.10') ~= 1 then
|
|
health.error('Nvim-treesitter requires Neovim Nightly')
|
|
end
|
|
|
|
if vim.fn.executable('tree-sitter') == 0 then
|
|
health.warn(
|
|
'`tree-sitter` executable not found (parser generator, only needed for :TSInstallFromGrammar,'
|
|
.. ' not required for :TSInstall)'
|
|
)
|
|
else
|
|
health.ok(
|
|
'`tree-sitter` found '
|
|
.. (ts_cli_version() or '(unknown version)')
|
|
.. ' (only needed for `:TSInstallFromGrammar`)'
|
|
)
|
|
end
|
|
|
|
if vim.fn.executable('git') == 0 then
|
|
health.warn(
|
|
'`git` executable not found.',
|
|
'Install it with your package manager and check that your `$PATH` is set correctly.'
|
|
)
|
|
else
|
|
health.ok('`git` executable found.')
|
|
end
|
|
|
|
local cc = install.select_executable(install.compilers)
|
|
if not cc then
|
|
health.error('`cc` executable not found.', {
|
|
'Check that any of '
|
|
.. table.concat(install.compilers, ', ')
|
|
.. ' is in your $PATH'
|
|
.. ' or set `$CC` or `require"nvim-treesitter.install".compilers` explicitly.',
|
|
})
|
|
else
|
|
local version = assert(vim.system({ cc, cc == 'cl' and '' or '--version' }):wait().stdout)
|
|
health.ok(
|
|
'`'
|
|
.. cc
|
|
.. '` executable found, selected from: '
|
|
.. table.concat(install.compilers, ', ')
|
|
.. (version and ('\nVersion: ' .. version) or '')
|
|
)
|
|
end
|
|
if vim.treesitter.language_version then
|
|
if vim.treesitter.language_version >= NVIM_TREESITTER_MINIMUM_ABI then
|
|
health.ok(
|
|
'Neovim was compiled with tree-sitter runtime ABI version '
|
|
.. vim.treesitter.language_version
|
|
.. ' (required >='
|
|
.. NVIM_TREESITTER_MINIMUM_ABI
|
|
.. ').'
|
|
)
|
|
else
|
|
health.error(
|
|
'Neovim was compiled with tree-sitter runtime ABI version '
|
|
.. vim.treesitter.language_version
|
|
.. '.\n'
|
|
.. 'nvim-treesitter expects at least ABI version '
|
|
.. NVIM_TREESITTER_MINIMUM_ABI
|
|
.. '\n'
|
|
.. 'Please make sure that Neovim is linked against a recent tree-sitter library when building'
|
|
.. ' or raise an issue at your Neovim packager. Parsers must be compatible with runtime ABI.'
|
|
)
|
|
end
|
|
end
|
|
|
|
health.start('OS Info')
|
|
for k, v in pairs(vim.uv.os_uname()) do
|
|
health.info(k .. ': ' .. v)
|
|
end
|
|
|
|
local installdir = config.get_install_dir('')
|
|
health.start('Install directory for parsers and queries')
|
|
health.info(installdir)
|
|
if vim.uv.fs_access(installdir, 'w') then
|
|
health.ok('is writable.')
|
|
else
|
|
health.error('is not writable.')
|
|
end
|
|
if
|
|
vim.iter(vim.api.nvim_list_runtime_paths()):any(function(p)
|
|
if installdir == vim.fs.normalize(p) .. '/' then
|
|
return true
|
|
end
|
|
end)
|
|
then
|
|
health.ok('is in runtimepath.')
|
|
else
|
|
health.error('is not in runtimepath.')
|
|
end
|
|
end
|
|
|
|
local function query_status(lang, query_group)
|
|
local ok, err = pcall(tsq.get, lang, query_group)
|
|
if not ok then
|
|
return 'x', err
|
|
elseif not err then
|
|
return '.'
|
|
else
|
|
return '✓'
|
|
end
|
|
end
|
|
|
|
function M.check()
|
|
--- @type {[1]: string, [2]: string, [3]: string}[]
|
|
local error_collection = {}
|
|
-- Installation dependency checks
|
|
install_health()
|
|
|
|
-- Parser installation checks
|
|
health.start('Installed languages' .. string.rep(' ', 5) .. 'H L F I J')
|
|
local languages = config.installed_parsers()
|
|
for _, lang in pairs(languages) do
|
|
local parser = parsers.configs[lang]
|
|
local out = lang .. string.rep(' ', 22 - #lang)
|
|
if parser.install_info then
|
|
for _, query_group in pairs(M.bundled_queries) do
|
|
local status, err = query_status(lang, query_group)
|
|
out = out .. status .. ' '
|
|
if err then
|
|
table.insert(error_collection, { lang, query_group, err })
|
|
end
|
|
end
|
|
end
|
|
if parser.requires then
|
|
for _, p in pairs(parser.requires) do
|
|
if not vim.list_contains(languages, p) then
|
|
table.insert(error_collection, { lang, 'queries', 'dependency ' .. p .. ' missing' })
|
|
end
|
|
end
|
|
end
|
|
health.info(vim.fn.trim(out, ' ', 2))
|
|
end
|
|
health.start(' Legend: H[ighlight], L[ocals], F[olds], I[ndents], In[J]ections')
|
|
|
|
if #error_collection > 0 then
|
|
health.start('The following errors have been detected in query files:')
|
|
for _, p in ipairs(error_collection) do
|
|
local lang, type = p[1], p[2]
|
|
local lines = {}
|
|
table.insert(lines, lang .. '(' .. type .. '): ')
|
|
local files = tsq.get_files(lang, type)
|
|
if #files > 0 then
|
|
for _, file in ipairs(files) do
|
|
local query = util.read_file(file)
|
|
local _, file_err = pcall(tsq.parse, lang, query)
|
|
if file_err then
|
|
table.insert(lines, file)
|
|
end
|
|
end
|
|
end
|
|
health.error(table.concat(lines, ''))
|
|
end
|
|
end
|
|
end
|
|
|
|
M.bundled_queries = { 'highlights', 'locals', 'folds', 'indents', 'injections' }
|
|
|
|
return M
|