mirror of
https://github.com/nvim-treesitter/nvim-treesitter.git
synced 2026-07-04 12:30:01 -04:00
feat!: use tree-sitter build
This commit is contained in:
parent
c17de56890
commit
214cfcf851
13 changed files with 443 additions and 898 deletions
|
|
@ -9,9 +9,6 @@ error('Cannot require a meta file')
|
|||
---Commit hash of parser to download (compatible with queries)
|
||||
---@field revision string
|
||||
---
|
||||
---Files to include when compiling (`src/parser.c` and optionally `src/scanner.c')
|
||||
---@field files string[]
|
||||
---
|
||||
---Branch of parser repo to download (if not default branch)
|
||||
---@field branch? string
|
||||
---
|
||||
|
|
|
|||
|
|
@ -104,15 +104,6 @@ function M.get_available(tier)
|
|||
languages
|
||||
)
|
||||
end
|
||||
if vim.fn.executable('tree-sitter') == 0 then
|
||||
languages = vim.tbl_filter(
|
||||
--- @param p string
|
||||
function(p)
|
||||
return parsers[p].install_info ~= nil and not parsers[p].install_info.generate
|
||||
end,
|
||||
languages
|
||||
)
|
||||
end
|
||||
return languages
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
local install = require('nvim-treesitter.install')
|
||||
local parsers = require('nvim-treesitter.parsers')
|
||||
local config = require('nvim-treesitter.config')
|
||||
local util = require('nvim-treesitter.util')
|
||||
|
|
@ -8,82 +7,80 @@ local health = vim.health
|
|||
local M = {}
|
||||
|
||||
local NVIM_TREESITTER_MINIMUM_ABI = 13
|
||||
local TREE_SITTER_MIN_VER = { 0, 22, 6 }
|
||||
|
||||
---@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 ].*')
|
||||
---@param name string
|
||||
---@return table?
|
||||
local function check_exe(name)
|
||||
if vim.fn.executable(name) == 1 then
|
||||
local path = vim.fn.exepath(name)
|
||||
local out = vim.trim(vim.fn.system({ name, '--version' }))
|
||||
local version = vim.version.parse(out)
|
||||
return { path = path, version = version, out = out }
|
||||
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')
|
||||
do -- nvim check
|
||||
if vim.fn.has('nvim-0.10') ~= 1 then
|
||||
health.error('Nvim-treesitter requires the latest Neovim nightly')
|
||||
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
|
||||
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
|
||||
.. ').'
|
||||
)
|
||||
do -- treesitter check
|
||||
local ts = check_exe('tree-sitter')
|
||||
if ts then
|
||||
if vim.version.ge(ts.version, TREE_SITTER_MIN_VER) then
|
||||
health.ok(string.format('tree-sitter %s (%s)', ts.version, ts.path))
|
||||
else
|
||||
health.error(
|
||||
string.format('tree-sitter CLI v%d.%d.%d is required', unpack(TREE_SITTER_MIN_VER))
|
||||
)
|
||||
end
|
||||
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.'
|
||||
)
|
||||
health.error('tree-sitter CLI not found')
|
||||
end
|
||||
end
|
||||
|
||||
do -- curl+tar or git check
|
||||
local curl = check_exe('curl')
|
||||
local tar = check_exe('tar')
|
||||
|
||||
if curl and tar and vim.uv.os_uname().sysname ~= 'Windows_NT' then
|
||||
health.ok(string.format('tar %s (%s)', tar.version, tar.path))
|
||||
health.ok(string.format('curl %s (%s)\n%s', curl.version, curl.path, curl.out))
|
||||
else
|
||||
local git = check_exe('git')
|
||||
if git then
|
||||
health.ok(string.format('git %s (%s)', git.version, git.path))
|
||||
else
|
||||
health.error('Either curl and tar or git must be installed and on `$PATH`')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -23,19 +23,8 @@ local uv_symlink = a.wrap(uv.fs_symlink, 4)
|
|||
--- @type fun(path: string): string?
|
||||
local uv_unlink = a.wrap(uv.fs_unlink, 2)
|
||||
|
||||
local M = {}
|
||||
|
||||
local max_jobs = 10
|
||||
|
||||
local iswin = uv.os_uname().sysname == 'Windows_NT'
|
||||
local ismac = uv.os_uname().sysname == 'Darwin'
|
||||
|
||||
--- @diagnostic disable-next-line:missing-parameter
|
||||
M.compilers = { 'cc', 'gcc', 'clang', 'cl', 'zig' }
|
||||
if uv.os_getenv('CC') then
|
||||
table.insert(M.compilers, 1, uv.os_getenv('CC'))
|
||||
end
|
||||
|
||||
local function system(cmd, opts)
|
||||
log.trace('running job: (cwd=%s) %s', opts.cwd, table.concat(cmd, ' '))
|
||||
local r = a.wrap(vim.system, 3)(cmd, opts) --[[@as vim.SystemCompleted]]
|
||||
|
|
@ -50,6 +39,10 @@ local function system(cmd, opts)
|
|||
return r
|
||||
end
|
||||
|
||||
local iswin = uv.os_uname().sysname == 'Windows_NT'
|
||||
|
||||
local M = {}
|
||||
|
||||
---
|
||||
--- PARSER INFO
|
||||
---
|
||||
|
|
@ -99,18 +92,6 @@ end
|
|||
--- PARSER MANAGEMENT FUNCTIONS
|
||||
---
|
||||
|
||||
local function istring(c)
|
||||
return type(c) == 'string'
|
||||
end
|
||||
|
||||
local function cc_err()
|
||||
log.error(
|
||||
'No C compiler found! "'
|
||||
.. table.concat(vim.tbl_filter(istring, M.compilers), '", "')
|
||||
.. '" are not executable.'
|
||||
)
|
||||
end
|
||||
|
||||
--- @param x string
|
||||
--- @return boolean
|
||||
local function executable(x)
|
||||
|
|
@ -122,10 +103,6 @@ end
|
|||
--- @param compile_location string
|
||||
--- @return string? err
|
||||
local function do_generate(logger, repo, compile_location)
|
||||
if not executable('tree-sitter') then
|
||||
return logger:error('tree-sitter CLI not found: `tree-sitter` is not executable')
|
||||
end
|
||||
|
||||
logger:info(
|
||||
string.format(
|
||||
'Generating parser.c from %s...',
|
||||
|
|
@ -134,7 +111,7 @@ local function do_generate(logger, repo, compile_location)
|
|||
)
|
||||
|
||||
local r = system({
|
||||
fn.exepath('tree-sitter'),
|
||||
'tree-sitter',
|
||||
'generate',
|
||||
'--no-bindings',
|
||||
'--abi',
|
||||
|
|
@ -232,10 +209,6 @@ end
|
|||
---@param project_dir string
|
||||
---@return string? err
|
||||
local function do_download_git(logger, repo, project_name, cache_dir, revision, project_dir)
|
||||
if not executable('git') then
|
||||
return logger:error('git not found!')
|
||||
end
|
||||
|
||||
logger:info('Downloading ' .. project_name .. '...')
|
||||
|
||||
local r = system({
|
||||
|
|
@ -266,86 +239,6 @@ local function do_download_git(logger, repo, project_name, cache_dir, revision,
|
|||
end
|
||||
end
|
||||
|
||||
--- @type table<string,table<string,boolean>>
|
||||
local cc_args_cache = vim.defaulttable()
|
||||
|
||||
--- @param cc string
|
||||
--- @param arg string
|
||||
--- @return boolean
|
||||
local function test_cc_arg(cc, arg)
|
||||
if cc_args_cache[cc][arg] == nil then
|
||||
cc_args_cache[cc][arg] = system({ cc, '-xc', '-', arg }, {
|
||||
stdin = 'int main(void) { return 0; }',
|
||||
}).code == 0
|
||||
end
|
||||
return cc_args_cache[cc][arg]
|
||||
end
|
||||
|
||||
---@param executables string[]
|
||||
---@return string?
|
||||
function M.select_executable(executables)
|
||||
return vim.tbl_filter(executable, executables)[1]
|
||||
end
|
||||
|
||||
-- Returns the compiler arguments based on the compiler and OS
|
||||
---@param repo InstallInfo
|
||||
---@param compiler string
|
||||
---@return string[]
|
||||
local function select_compiler_args(repo, compiler)
|
||||
if compiler:find('cl$') or compiler:find('cl.exe$') then
|
||||
return {
|
||||
'/Fe:',
|
||||
'parser.so',
|
||||
'/Isrc',
|
||||
repo.files,
|
||||
'-Os',
|
||||
'/utf-8',
|
||||
'/LD',
|
||||
}
|
||||
end
|
||||
|
||||
if compiler:find('zig$') or compiler:find('zig.exe$') then
|
||||
return {
|
||||
'cc',
|
||||
'-o',
|
||||
'parser.so',
|
||||
repo.files,
|
||||
'-lc',
|
||||
'-Isrc',
|
||||
'-shared',
|
||||
'-Os',
|
||||
}
|
||||
end
|
||||
|
||||
local args = {
|
||||
'-o',
|
||||
'parser.so',
|
||||
'-I./src',
|
||||
repo.files,
|
||||
'-Os',
|
||||
ismac and '-bundle' or '-shared',
|
||||
}
|
||||
|
||||
--- @param arg string
|
||||
local function add_cc_arg(arg)
|
||||
if test_cc_arg(compiler, arg) then
|
||||
args[#args + 1] = arg
|
||||
end
|
||||
end
|
||||
|
||||
if not iswin then
|
||||
add_cc_arg('-Wall')
|
||||
add_cc_arg('-Wextra')
|
||||
add_cc_arg('-fPIC')
|
||||
|
||||
-- Make sure we don't compile in any unresolved symbols, otherwise nvim will
|
||||
-- just exit (not even crash)
|
||||
add_cc_arg('-Werror=implicit-function-declaration')
|
||||
end
|
||||
|
||||
return args
|
||||
end
|
||||
|
||||
---@param repo InstallInfo
|
||||
---@return boolean
|
||||
local function can_download_tar(repo)
|
||||
|
|
@ -355,21 +248,40 @@ local function can_download_tar(repo)
|
|||
return can_use_tar and (is_github or is_gitlab) and not iswin
|
||||
end
|
||||
|
||||
-- Returns the compile command based on the OS and user options
|
||||
---@param logger Logger
|
||||
---@param repo InstallInfo
|
||||
---@param cc string
|
||||
---@param compile_location string
|
||||
---@return string? err
|
||||
local function do_compile(logger, repo, cc, compile_location)
|
||||
local args = vim.iter(select_compiler_args(repo, cc)):flatten():totable()
|
||||
local cmd = vim.list_extend({ cc }, args)
|
||||
local function do_compile(logger, compile_location)
|
||||
logger:info(string.format('Compiling parser'))
|
||||
|
||||
logger:info('Compiling parser')
|
||||
|
||||
local r = system(cmd, { cwd = compile_location })
|
||||
local r = system({
|
||||
'tree-sitter',
|
||||
'build',
|
||||
'-o',
|
||||
'parser.so',
|
||||
}, { cwd = compile_location })
|
||||
if r.code > 0 then
|
||||
return logger:error('Error during compilation: %s', r.stderr)
|
||||
return logger:error('Error during "tree-sitter build": %s', r.stderr)
|
||||
end
|
||||
end
|
||||
|
||||
---@param logger Logger
|
||||
---@param compile_location string
|
||||
---@param target_location string
|
||||
---@return string? err
|
||||
local function do_install(logger, compile_location, target_location)
|
||||
logger:info(string.format('Installing parser'))
|
||||
|
||||
if iswin then -- why can't you just be normal?!
|
||||
local tempfile = target_location .. tostring(uv.hrtime())
|
||||
uv_rename(target_location, tempfile) -- parser may be in use: rename...
|
||||
uv_unlink(tempfile) -- ...and mark for garbage collection
|
||||
end
|
||||
|
||||
local err = uv_copyfile(compile_location, target_location)
|
||||
a.main()
|
||||
if err then
|
||||
return logger:error('Error during parser installation: %s', err)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -383,12 +295,6 @@ local function install_lang0(lang, cache_dir, install_dir, generate)
|
|||
|
||||
local repo = get_parser_install_info(lang)
|
||||
if repo then
|
||||
local cc = M.select_executable(M.compilers)
|
||||
if not cc then
|
||||
cc_err()
|
||||
return
|
||||
end
|
||||
|
||||
local project_name = 'tree-sitter-' .. lang
|
||||
|
||||
local revision = repo.revision
|
||||
|
|
@ -414,7 +320,7 @@ local function install_lang0(lang, cache_dir, install_dir, generate)
|
|||
compile_location = fs.joinpath(compile_location, repo.location)
|
||||
end
|
||||
|
||||
do
|
||||
do -- generate parser from grammar
|
||||
if repo.generate or generate then
|
||||
local err = do_generate(logger, repo, compile_location)
|
||||
if err then
|
||||
|
|
@ -423,24 +329,25 @@ local function install_lang0(lang, cache_dir, install_dir, generate)
|
|||
end
|
||||
end
|
||||
|
||||
do
|
||||
local err = do_compile(logger, repo, cc, compile_location)
|
||||
do -- compile parser
|
||||
local err = do_compile(logger, compile_location)
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
end
|
||||
|
||||
local parser_lib_name = fs.joinpath(install_dir, lang) .. '.so'
|
||||
do -- install parser
|
||||
local parser_lib_name = fs.joinpath(compile_location, 'parser.so')
|
||||
local install_location = fs.joinpath(install_dir, lang) .. '.so'
|
||||
local err = do_install(logger, parser_lib_name, install_location)
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
|
||||
local err = uv_copyfile(fs.joinpath(compile_location, 'parser.so'), parser_lib_name)
|
||||
a.main()
|
||||
if err then
|
||||
return logger:error(err)
|
||||
local revfile = fs.joinpath(config.get_install_dir('parser-info') or '', lang .. '.revision')
|
||||
util.write_file(revfile, revision or '')
|
||||
end
|
||||
|
||||
local revfile = fs.joinpath(config.get_install_dir('parser-info') or '', lang .. '.revision')
|
||||
util.write_file(revfile, revision or '')
|
||||
|
||||
if not repo.path then
|
||||
util.delete(fs.joinpath(cache_dir, project_name))
|
||||
end
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue