2020-04-19 14:43:55 +02:00
|
|
|
local api = vim.api
|
2023-06-12 09:54:30 -06:00
|
|
|
local uv = vim.loop
|
2020-04-19 14:43:55 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local utils = require('nvim-treesitter.utils')
|
|
|
|
|
local parsers = require('nvim-treesitter.parsers')
|
|
|
|
|
local config = require('nvim-treesitter.config')
|
|
|
|
|
local shell = require('nvim-treesitter.shell_cmds')
|
2020-04-19 16:00:26 +02:00
|
|
|
|
2020-05-08 11:22:59 +02:00
|
|
|
local M = {}
|
2023-02-24 03:07:52 -05:00
|
|
|
|
|
|
|
|
---@class LockfileInfo
|
|
|
|
|
---@field revision string
|
|
|
|
|
|
|
|
|
|
---@type table<string, LockfileInfo>
|
2020-08-26 21:05:27 +02:00
|
|
|
local lockfile = {}
|
2020-04-19 14:43:55 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
M.compilers = { uv.os_getenv('CC'), 'cc', 'gcc', 'clang', 'cl', 'zig' }
|
|
|
|
|
M.prefer_git = uv.os_uname().sysname == 'Windows_NT'
|
2021-10-01 18:18:11 +02:00
|
|
|
M.command_extra_args = {}
|
2022-01-22 11:02:55 +01:00
|
|
|
M.ts_generate_args = nil
|
2020-09-19 16:12:26 +02:00
|
|
|
|
2020-09-26 22:56:46 +02:00
|
|
|
local started_commands = 0
|
|
|
|
|
local finished_commands = 0
|
|
|
|
|
local failed_commands = 0
|
2023-06-12 09:54:30 -06:00
|
|
|
local stdout_output = {}
|
|
|
|
|
local stderr_output = {}
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
--- JOB API functions
|
|
|
|
|
---
|
2020-09-26 22:56:46 +02:00
|
|
|
|
|
|
|
|
local function reset_progress_counter()
|
|
|
|
|
if started_commands ~= finished_commands then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
started_commands = 0
|
|
|
|
|
finished_commands = 0
|
|
|
|
|
failed_commands = 0
|
2023-06-12 09:54:30 -06:00
|
|
|
stdout_output = {}
|
|
|
|
|
stderr_output = {}
|
2020-09-26 22:56:46 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function get_job_status()
|
2023-06-12 09:54:30 -06:00
|
|
|
return '[nvim-treesitter] ['
|
2021-07-04 16:12:17 -05:00
|
|
|
.. finished_commands
|
2023-06-12 09:54:30 -06:00
|
|
|
.. '/'
|
2021-07-04 16:12:17 -05:00
|
|
|
.. started_commands
|
2023-06-12 09:54:30 -06:00
|
|
|
.. (failed_commands > 0 and ', failed: ' .. failed_commands or '')
|
|
|
|
|
.. ']'
|
2020-09-26 22:56:46 +02:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
---@param cmd Command
|
|
|
|
|
---@return string command
|
|
|
|
|
local function get_command(cmd)
|
|
|
|
|
local options = ''
|
|
|
|
|
if cmd.opts and cmd.opts.args then
|
|
|
|
|
if M.command_extra_args[cmd.cmd] then
|
|
|
|
|
vim.list_extend(cmd.opts.args, M.command_extra_args[cmd.cmd])
|
|
|
|
|
end
|
|
|
|
|
for _, opt in ipairs(cmd.opts.args) do
|
|
|
|
|
options = string.format('%s %s', options, opt)
|
2022-12-27 12:35:43 +01:00
|
|
|
end
|
|
|
|
|
end
|
2020-11-21 18:49:19 +01:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local command = string.format('%s %s', cmd.cmd, options)
|
|
|
|
|
if cmd.opts and cmd.opts.cwd then
|
|
|
|
|
command = shell.make_directory_change_for_command(cmd.opts.cwd, command)
|
2022-11-30 20:07:51 +01:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
return command
|
2022-11-30 20:07:51 +01:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
---@param cmd_list Command[]
|
2022-10-31 10:52:52 +00:00
|
|
|
---@return boolean
|
2023-06-12 09:54:30 -06:00
|
|
|
local function iter_cmd_sync(cmd_list)
|
|
|
|
|
for _, cmd in ipairs(cmd_list) do
|
|
|
|
|
if cmd.info then
|
|
|
|
|
vim.notify(cmd.info)
|
2022-11-14 10:43:11 +01:00
|
|
|
end
|
2021-06-24 23:20:29 +10:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
if type(cmd.cmd) == 'function' then
|
|
|
|
|
cmd.cmd()
|
|
|
|
|
else
|
|
|
|
|
local ret = vim.fn.system(get_command(cmd))
|
|
|
|
|
if vim.v.shell_error ~= 0 then
|
|
|
|
|
vim.notify(ret)
|
|
|
|
|
api.nvim_err_writeln(
|
|
|
|
|
(cmd.err and cmd.err .. '\n' or '')
|
|
|
|
|
.. 'Failed to execute the following command:\n'
|
|
|
|
|
.. vim.inspect(cmd)
|
|
|
|
|
)
|
|
|
|
|
return false
|
2021-04-30 16:46:38 +02:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
|
|
|
|
|
return true
|
2021-04-30 16:46:38 +02:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local function iter_cmd(cmd_list, i, lang, success_message)
|
2020-09-26 22:56:46 +02:00
|
|
|
if i == 1 then
|
|
|
|
|
started_commands = started_commands + 1
|
|
|
|
|
end
|
|
|
|
|
if i == #cmd_list + 1 then
|
|
|
|
|
finished_commands = finished_commands + 1
|
2023-06-12 09:54:30 -06:00
|
|
|
return vim.notify(get_job_status() .. ' ' .. success_message)
|
2020-09-26 22:56:46 +02:00
|
|
|
end
|
2020-04-20 22:01:52 +02:00
|
|
|
|
2020-04-27 15:27:59 +02:00
|
|
|
local attr = cmd_list[i]
|
2021-07-04 16:12:17 -05:00
|
|
|
if attr.info then
|
2023-06-12 09:54:30 -06:00
|
|
|
vim.notify(get_job_status() .. ' ' .. attr.info)
|
2021-07-04 16:12:17 -05:00
|
|
|
end
|
2020-04-20 22:01:52 +02:00
|
|
|
|
2021-10-01 18:18:11 +02:00
|
|
|
if attr.opts and attr.opts.args and M.command_extra_args[attr.cmd] then
|
|
|
|
|
vim.list_extend(attr.opts.args, M.command_extra_args[attr.cmd])
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
if type(attr.cmd) == 'function' then
|
2020-11-21 18:49:19 +01:00
|
|
|
local ok, err = pcall(attr.cmd)
|
|
|
|
|
if ok then
|
2023-06-12 09:54:30 -06:00
|
|
|
iter_cmd(cmd_list, i + 1, lang, success_message)
|
2020-11-21 18:49:19 +01:00
|
|
|
else
|
2020-09-26 22:56:46 +02:00
|
|
|
failed_commands = failed_commands + 1
|
|
|
|
|
finished_commands = finished_commands + 1
|
2021-07-04 16:12:17 -05:00
|
|
|
return api.nvim_err_writeln(
|
2023-06-12 09:54:30 -06:00
|
|
|
(attr.err or ('Failed to execute the following command:\n' .. vim.inspect(attr)))
|
|
|
|
|
.. '\n'
|
|
|
|
|
.. vim.inspect(err)
|
2021-07-04 16:12:17 -05:00
|
|
|
)
|
2020-09-11 15:11:28 +02:00
|
|
|
end
|
2020-11-21 18:49:19 +01:00
|
|
|
else
|
|
|
|
|
local handle
|
2023-06-12 09:54:30 -06:00
|
|
|
local stdout = uv.new_pipe(false)
|
|
|
|
|
local stderr = uv.new_pipe(false)
|
2021-07-04 16:12:17 -05:00
|
|
|
attr.opts.stdio = { nil, stdout, stderr }
|
2022-10-31 10:52:52 +00:00
|
|
|
---@type userdata
|
2023-06-12 09:54:30 -06:00
|
|
|
handle = uv.spawn(
|
2021-07-04 16:12:17 -05:00
|
|
|
attr.cmd,
|
|
|
|
|
attr.opts,
|
|
|
|
|
vim.schedule_wrap(function(code)
|
|
|
|
|
if code ~= 0 then
|
|
|
|
|
stdout:read_stop()
|
|
|
|
|
stderr:read_stop()
|
2021-04-30 16:46:38 +02:00
|
|
|
end
|
2021-07-04 16:12:17 -05:00
|
|
|
stdout:close()
|
|
|
|
|
stderr:close()
|
|
|
|
|
handle:close()
|
|
|
|
|
if code ~= 0 then
|
|
|
|
|
failed_commands = failed_commands + 1
|
|
|
|
|
finished_commands = finished_commands + 1
|
2023-06-12 09:54:30 -06:00
|
|
|
if stdout_output[handle] and stdout_output[handle] ~= '' then
|
|
|
|
|
vim.notify(stdout_output[handle])
|
2021-07-04 16:12:17 -05:00
|
|
|
end
|
2021-04-30 16:46:38 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local err_msg = stderr_output[handle] or ''
|
2021-07-04 16:12:17 -05:00
|
|
|
api.nvim_err_writeln(
|
2023-06-12 09:54:30 -06:00
|
|
|
'nvim-treesitter['
|
2021-07-04 16:12:17 -05:00
|
|
|
.. lang
|
2023-06-12 09:54:30 -06:00
|
|
|
.. ']: '
|
|
|
|
|
.. (attr.err or ('Failed to execute the following command:\n' .. vim.inspect(attr)))
|
|
|
|
|
.. '\n'
|
2021-07-04 16:12:17 -05:00
|
|
|
.. err_msg
|
|
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
iter_cmd(cmd_list, i + 1, lang, success_message)
|
2021-07-04 16:12:17 -05:00
|
|
|
end)
|
|
|
|
|
)
|
2023-06-12 09:54:30 -06:00
|
|
|
uv.read_start(stdout, function(_, data)
|
|
|
|
|
if data then
|
|
|
|
|
stdout_output[handle] = (stdout_output[handle] or '') .. data
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
uv.read_start(stderr, function(_, data)
|
|
|
|
|
if data then
|
|
|
|
|
stderr_output[handle] = (stderr_output[handle] or '') .. data
|
|
|
|
|
end
|
|
|
|
|
end)
|
2020-11-21 18:49:19 +01:00
|
|
|
end
|
2020-04-19 14:43:55 +02:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
---
|
|
|
|
|
--- PARSER INFO
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
---@param lang string
|
|
|
|
|
---@param validate boolean|nil
|
|
|
|
|
---@return InstallInfo
|
|
|
|
|
local function get_parser_install_info(lang, validate)
|
|
|
|
|
local parser_config = parsers.configs[lang]
|
|
|
|
|
|
|
|
|
|
if not parser_config then
|
|
|
|
|
error('Parser not available for language "' .. lang .. '"')
|
2020-06-29 16:07:16 +02:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local install_info = parser_config.install_info
|
|
|
|
|
|
|
|
|
|
if validate then
|
|
|
|
|
vim.validate({
|
|
|
|
|
url = { install_info.url, 'string' },
|
|
|
|
|
files = { install_info.files, 'table' },
|
|
|
|
|
})
|
2021-02-26 20:41:05 +01:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
|
|
|
|
|
return install_info
|
2020-06-29 16:07:16 +02:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
---@param lang string
|
|
|
|
|
---@return string|nil
|
|
|
|
|
local function get_revision(lang)
|
|
|
|
|
if #lockfile == 0 then
|
|
|
|
|
local filename = utils.get_package_path('lockfile.json')
|
|
|
|
|
local file = assert(io.open(filename, 'r'))
|
|
|
|
|
lockfile = vim.json.decode(file:read('*all'))
|
|
|
|
|
file:close()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local install_info = get_parser_install_info(lang)
|
|
|
|
|
if install_info.revision then
|
|
|
|
|
return install_info.revision
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if lockfile[lang] then
|
|
|
|
|
return lockfile[lang].revision
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@param lang string
|
|
|
|
|
---@return string|nil
|
|
|
|
|
local function get_installed_revision(lang)
|
|
|
|
|
local lang_file = utils.join_path(config.get_install_dir('parser-info'), lang .. '.revision')
|
|
|
|
|
local file = assert(io.open(lang_file, 'r'))
|
|
|
|
|
local revision = file:read('*a')
|
|
|
|
|
file:close()
|
|
|
|
|
return revision
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
---@param lang string
|
2023-02-24 03:07:52 -05:00
|
|
|
---@return boolean
|
2023-06-12 09:54:30 -06:00
|
|
|
local function needs_update(lang)
|
|
|
|
|
local revision = get_revision(lang)
|
|
|
|
|
return not revision or revision ~= get_installed_revision(lang)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function M.info()
|
|
|
|
|
local installed = config.installed_parsers()
|
|
|
|
|
local parser_list = parsers.get_available()
|
|
|
|
|
|
|
|
|
|
local max_len = 0
|
|
|
|
|
for _, lang in pairs(parser_list) do
|
|
|
|
|
if #lang > max_len then
|
|
|
|
|
max_len = #lang
|
2020-06-29 16:07:16 +02:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
end
|
2020-06-29 16:07:16 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
for _, lang in pairs(parser_list) do
|
|
|
|
|
local parser = (lang .. string.rep(' ', max_len - #lang + 1))
|
|
|
|
|
local output
|
|
|
|
|
if vim.list_contains(installed, lang) then
|
|
|
|
|
output = { parser .. '[✓] installed', 'DiagnosticOk' }
|
|
|
|
|
elseif #api.nvim_get_runtime_file('parser/' .. lang .. '.*', true) > 0 then
|
|
|
|
|
output = { parser .. '[·] not installed (but available from runtimepath)', 'DiagnosticInfo' }
|
2020-11-21 18:49:19 +01:00
|
|
|
else
|
2023-06-12 09:54:30 -06:00
|
|
|
output = { parser .. '[✗] not installed' }
|
2020-06-29 16:07:16 +02:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
api.nvim_echo({ output }, false, {})
|
2020-06-29 16:07:16 +02:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
---
|
|
|
|
|
--- PARSER MANAGEMENT FUNCTIONS
|
|
|
|
|
---
|
|
|
|
|
|
2022-10-31 10:52:52 +00:00
|
|
|
---@param lang string
|
2023-06-12 09:54:30 -06:00
|
|
|
---@param cache_dir string
|
|
|
|
|
---@param install_dir string
|
|
|
|
|
---@param force boolean
|
2022-10-31 10:52:52 +00:00
|
|
|
---@param with_sync boolean
|
|
|
|
|
---@param generate_from_grammar boolean
|
2023-06-12 09:54:30 -06:00
|
|
|
local function install_lang(lang, cache_dir, install_dir, force, with_sync, generate_from_grammar)
|
|
|
|
|
if vim.list_contains(config.installed_parsers(), lang) then
|
|
|
|
|
if not force then
|
|
|
|
|
local yesno =
|
|
|
|
|
vim.fn.input(lang .. ' parser already available: would you like to reinstall ? y/n: ')
|
|
|
|
|
print('\n ')
|
|
|
|
|
if yesno:sub(1, 1) ~= 'y' then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-08-11 14:36:13 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local repo = get_parser_install_info(lang)
|
2020-09-05 15:28:14 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local project_name = 'tree-sitter-' .. lang
|
|
|
|
|
local maybe_local_path = vim.fs.normalize(repo.url)
|
2020-10-13 19:24:07 +02:00
|
|
|
local from_local_path = vim.fn.isdirectory(maybe_local_path) == 1
|
|
|
|
|
if from_local_path then
|
|
|
|
|
repo.url = maybe_local_path
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-24 03:07:52 -05:00
|
|
|
---@type string compile_location only needed for typescript installs.
|
2020-10-13 19:24:07 +02:00
|
|
|
local compile_location
|
|
|
|
|
if from_local_path then
|
|
|
|
|
compile_location = repo.url
|
2023-03-16 11:31:20 -04:00
|
|
|
if repo.location then
|
|
|
|
|
compile_location = utils.join_path(compile_location, repo.location)
|
|
|
|
|
end
|
2020-10-13 19:24:07 +02:00
|
|
|
else
|
2022-06-25 11:51:21 +02:00
|
|
|
local repo_location = project_name
|
|
|
|
|
if repo.location then
|
2023-06-12 09:54:30 -06:00
|
|
|
repo_location = utils.join_path(repo_location, repo.location)
|
2022-06-25 11:51:21 +02:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
compile_location = utils.join_path(cache_dir, repo_location)
|
2020-10-13 19:24:07 +02:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
local parser_lib_name = utils.join_path(install_dir, lang) .. '.so'
|
2020-08-25 20:00:47 +02:00
|
|
|
|
2021-03-07 16:43:32 +01:00
|
|
|
generate_from_grammar = repo.requires_generate_from_grammar or generate_from_grammar
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
if generate_from_grammar and vim.fn.executable('tree-sitter') ~= 1 then
|
|
|
|
|
api.nvim_err_writeln('tree-sitter CLI not found: `tree-sitter` is not executable')
|
2021-03-08 12:52:59 +01:00
|
|
|
if repo.requires_generate_from_grammar then
|
2021-07-04 16:12:17 -05:00
|
|
|
api.nvim_err_writeln(
|
2023-06-12 09:54:30 -06:00
|
|
|
'tree-sitter CLI is needed because the parser for `'
|
2021-07-04 16:12:17 -05:00
|
|
|
.. lang
|
2023-06-12 09:54:30 -06:00
|
|
|
.. '` needs to be generated from grammar'
|
2021-07-04 16:12:17 -05:00
|
|
|
)
|
2021-03-08 12:52:59 +01:00
|
|
|
end
|
2020-10-13 19:24:07 +02:00
|
|
|
return
|
2022-01-22 11:02:55 +01:00
|
|
|
else
|
|
|
|
|
if not M.ts_generate_args then
|
2023-06-12 09:54:30 -06:00
|
|
|
M.ts_generate_args = { 'generate', '--no-bindings', '--abi', vim.treesitter.language_version }
|
2022-01-22 11:02:55 +01:00
|
|
|
end
|
2020-10-13 19:24:07 +02:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
if generate_from_grammar and vim.fn.executable('node') ~= 1 then
|
|
|
|
|
api.nvim_err_writeln('Node JS not found: `node` is not executable')
|
2021-06-03 00:26:25 +02:00
|
|
|
return
|
2021-04-01 11:37:07 +02:00
|
|
|
end
|
2020-10-06 02:53:00 +05:30
|
|
|
local cc = shell.select_executable(M.compilers)
|
2020-08-25 20:00:47 +02:00
|
|
|
if not cc then
|
2023-06-12 09:54:30 -06:00
|
|
|
api.nvim_err_writeln(
|
|
|
|
|
'No C compiler found! "'
|
|
|
|
|
.. table.concat(
|
|
|
|
|
vim.tbl_filter(function(c) ---@param c string
|
|
|
|
|
return type(c) == 'string'
|
|
|
|
|
end, M.compilers),
|
|
|
|
|
'", "'
|
|
|
|
|
)
|
|
|
|
|
.. '" are not executable.'
|
|
|
|
|
)
|
2020-08-25 20:00:47 +02:00
|
|
|
return
|
|
|
|
|
end
|
2022-06-23 11:41:40 +01:00
|
|
|
|
|
|
|
|
local revision = repo.revision
|
2023-03-11 16:05:03 -05:00
|
|
|
if not revision then
|
2022-06-23 11:41:40 +01:00
|
|
|
revision = get_revision(lang)
|
|
|
|
|
end
|
2020-08-25 20:00:47 +02:00
|
|
|
|
2023-02-24 03:07:52 -05:00
|
|
|
---@class Command
|
|
|
|
|
---@field cmd string
|
|
|
|
|
---@field info string
|
|
|
|
|
---@field err string
|
|
|
|
|
---@field opts CmdOpts
|
|
|
|
|
|
|
|
|
|
---@class CmdOpts
|
|
|
|
|
---@field args string[]
|
|
|
|
|
---@field cwd string
|
|
|
|
|
|
|
|
|
|
---@type Command[]
|
2020-08-26 21:05:27 +02:00
|
|
|
local command_list = {}
|
2020-10-13 19:24:07 +02:00
|
|
|
if not from_local_path then
|
2023-06-12 09:54:30 -06:00
|
|
|
vim.list_extend(command_list, {
|
|
|
|
|
{
|
|
|
|
|
cmd = function()
|
|
|
|
|
vim.fn.delete(utils.join_path(cache_dir, project_name), 'rf')
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
})
|
2021-10-01 18:18:11 +02:00
|
|
|
vim.list_extend(
|
|
|
|
|
command_list,
|
2023-06-12 09:54:30 -06:00
|
|
|
shell.select_download_commands(repo, project_name, cache_dir, revision, M.prefer_git)
|
2021-10-01 18:18:11 +02:00
|
|
|
)
|
2020-10-13 19:24:07 +02:00
|
|
|
end
|
|
|
|
|
if generate_from_grammar then
|
2021-03-07 17:05:16 +01:00
|
|
|
if repo.generate_requires_npm then
|
2023-06-12 09:54:30 -06:00
|
|
|
if vim.fn.executable('npm') ~= 1 then
|
|
|
|
|
api.nvim_err_writeln('`' .. lang .. '` requires NPM to be installed from grammar.js')
|
2021-03-16 05:02:03 +01:00
|
|
|
return
|
|
|
|
|
end
|
2021-03-07 17:05:16 +01:00
|
|
|
vim.list_extend(command_list, {
|
|
|
|
|
{
|
2023-06-12 09:54:30 -06:00
|
|
|
cmd = 'npm',
|
|
|
|
|
info = 'Installing NPM dependencies of ' .. lang .. ' parser',
|
|
|
|
|
err = 'Error during `npm install` (required for parser generation of '
|
|
|
|
|
.. lang
|
|
|
|
|
.. ' with npm dependencies)',
|
2021-03-07 17:05:16 +01:00
|
|
|
opts = {
|
2023-06-12 09:54:30 -06:00
|
|
|
args = { 'install' },
|
2021-07-04 16:12:17 -05:00
|
|
|
cwd = compile_location,
|
|
|
|
|
},
|
|
|
|
|
},
|
2021-03-07 17:05:16 +01:00
|
|
|
})
|
|
|
|
|
end
|
2020-10-13 19:24:07 +02:00
|
|
|
vim.list_extend(command_list, {
|
|
|
|
|
{
|
2023-06-12 09:54:30 -06:00
|
|
|
cmd = vim.fn.exepath('tree-sitter'),
|
|
|
|
|
info = 'Generating source files from grammar.js...',
|
2020-10-13 19:24:07 +02:00
|
|
|
err = 'Error during "tree-sitter generate"',
|
|
|
|
|
opts = {
|
2022-01-22 11:02:55 +01:00
|
|
|
args = M.ts_generate_args,
|
2021-07-04 16:12:17 -05:00
|
|
|
cwd = compile_location,
|
|
|
|
|
},
|
|
|
|
|
},
|
2020-10-13 19:24:07 +02:00
|
|
|
})
|
|
|
|
|
end
|
2020-08-26 21:05:27 +02:00
|
|
|
vim.list_extend(command_list, {
|
2021-09-09 22:11:07 +02:00
|
|
|
shell.select_compile_command(repo, cc, compile_location),
|
2020-11-21 18:49:19 +01:00
|
|
|
{
|
|
|
|
|
cmd = function()
|
2023-06-12 09:54:30 -06:00
|
|
|
uv.fs_copyfile(utils.join_path(compile_location, 'parser.so'), parser_lib_name)
|
2021-07-04 16:12:17 -05:00
|
|
|
end,
|
|
|
|
|
},
|
2023-06-12 09:54:30 -06:00
|
|
|
{
|
|
|
|
|
cmd = function()
|
|
|
|
|
local file = assert(
|
|
|
|
|
io.open(
|
|
|
|
|
utils.join_path(config.get_install_dir('parser-info') or '', lang .. '.revision'),
|
|
|
|
|
'w'
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
file:write(revision or '')
|
|
|
|
|
file:close()
|
|
|
|
|
end,
|
2021-12-14 19:42:36 +01:00
|
|
|
},
|
2020-08-26 21:05:27 +02:00
|
|
|
})
|
2020-10-13 19:24:07 +02:00
|
|
|
if not from_local_path then
|
2023-06-12 09:54:30 -06:00
|
|
|
vim.list_extend(command_list, {
|
|
|
|
|
{
|
|
|
|
|
cmd = function()
|
|
|
|
|
vim.fn.delete(utils.join_path(cache_dir, project_name), 'rf')
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
})
|
2020-10-13 19:24:07 +02:00
|
|
|
end
|
2020-04-19 16:00:26 +02:00
|
|
|
|
2020-06-29 16:07:16 +02:00
|
|
|
if with_sync then
|
2020-07-12 17:31:34 +02:00
|
|
|
if iter_cmd_sync(command_list) == true then
|
2023-06-12 09:54:30 -06:00
|
|
|
vim.notify('Parser for ' .. lang .. ' has been installed')
|
2020-06-29 16:07:16 +02:00
|
|
|
end
|
|
|
|
|
else
|
2023-06-12 09:54:30 -06:00
|
|
|
iter_cmd(command_list, 1, lang, 'Parser for ' .. lang .. ' has been installed')
|
2021-07-02 21:57:07 +10:00
|
|
|
end
|
2020-06-29 16:16:18 +02:00
|
|
|
end
|
|
|
|
|
|
2023-02-24 03:07:52 -05:00
|
|
|
---@class InstallOptions
|
|
|
|
|
---@field with_sync boolean
|
2023-06-12 09:54:30 -06:00
|
|
|
---@field force boolean
|
2023-02-24 03:07:52 -05:00
|
|
|
---@field generate_from_grammar boolean
|
2023-06-12 09:54:30 -06:00
|
|
|
---@field skip table
|
2023-02-24 03:07:52 -05:00
|
|
|
|
|
|
|
|
-- Install a parser
|
2023-06-12 09:54:30 -06:00
|
|
|
---@param languages? string[]|string
|
2023-02-24 03:07:52 -05:00
|
|
|
---@param options? InstallOptions
|
2023-06-12 09:54:30 -06:00
|
|
|
function M.install(languages, options)
|
2021-03-24 09:12:03 -05:00
|
|
|
options = options or {}
|
|
|
|
|
local with_sync = options.with_sync
|
2023-06-12 09:54:30 -06:00
|
|
|
local force = options.force
|
2021-03-24 09:12:03 -05:00
|
|
|
local generate_from_grammar = options.generate_from_grammar
|
2023-06-12 09:54:30 -06:00
|
|
|
local skip = options.skip
|
2021-03-24 09:12:03 -05:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
reset_progress_counter()
|
2020-04-19 14:43:55 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
if vim.fn.executable('git') == 0 then
|
|
|
|
|
api.nvim_err_writeln('Git is required on your system to run this command')
|
|
|
|
|
return
|
|
|
|
|
end
|
2020-04-19 14:43:55 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local cache_dir = vim.fn.stdpath('cache')
|
|
|
|
|
local install_dir = config.get_install_dir('parser')
|
2020-06-25 12:37:01 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
if languages == 'all' then
|
|
|
|
|
force = true
|
|
|
|
|
end
|
2021-03-24 09:12:03 -05:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
languages = config.norm_languages(languages, skip)
|
2020-09-26 22:56:46 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
for _, lang in ipairs(languages) do
|
|
|
|
|
install_lang(lang, cache_dir, install_dir, force, with_sync, generate_from_grammar)
|
|
|
|
|
uv.fs_symlink(
|
|
|
|
|
utils.get_package_path('runtime', 'queries', lang),
|
|
|
|
|
utils.join_path(config.get_install_dir('queries'), lang),
|
|
|
|
|
{ dir = true, junction = true } -- needed on Windows (non-junction links require admin)
|
|
|
|
|
)
|
2020-04-30 22:11:44 +02:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
---@class UpdateOptions
|
|
|
|
|
---@field with_sync boolean
|
|
|
|
|
|
|
|
|
|
---@param languages? string[]|string
|
|
|
|
|
---@param options? UpdateOptions
|
|
|
|
|
function M.update(languages, options)
|
|
|
|
|
options = options or {}
|
2024-10-07 11:46:28 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
reset_progress_counter()
|
|
|
|
|
M.lockfile = {}
|
2024-10-07 11:46:28 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
languages = config.norm_languages(languages or 'all', { ignored = true, missing = true })
|
|
|
|
|
languages = vim.iter.filter(needs_update, languages) --- @type string[]
|
|
|
|
|
|
|
|
|
|
if #languages > 0 then
|
|
|
|
|
M.install(languages, {
|
|
|
|
|
force = true,
|
|
|
|
|
with_sync = options.with_sync,
|
|
|
|
|
})
|
|
|
|
|
else
|
|
|
|
|
vim.notify('All parsers are up-to-date')
|
|
|
|
|
end
|
2022-07-08 09:36:54 +02:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
--- @param lang string
|
|
|
|
|
--- @param parser string
|
|
|
|
|
--- @param queries string
|
|
|
|
|
local function uninstall(lang, parser, queries)
|
|
|
|
|
if vim.fn.filereadable(parser) ~= 1 then
|
|
|
|
|
return
|
2020-07-31 20:27:06 +02:00
|
|
|
end
|
2023-06-12 09:54:30 -06:00
|
|
|
|
|
|
|
|
iter_cmd({
|
|
|
|
|
{
|
|
|
|
|
cmd = function()
|
|
|
|
|
uv.fs_unlink(parser)
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
cmd = function()
|
|
|
|
|
uv.fs_unlink(queries)
|
|
|
|
|
end,
|
|
|
|
|
},
|
|
|
|
|
}, 1, lang, 'Parser for ' .. lang .. ' has been uninstalled')
|
2020-07-31 20:27:06 +02:00
|
|
|
end
|
|
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
--- @param languages string[]|string
|
|
|
|
|
function M.uninstall(languages)
|
|
|
|
|
reset_progress_counter()
|
2021-06-24 23:20:29 +10:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
languages = config.norm_languages(languages or 'all', { missing = true })
|
2022-08-01 22:28:36 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
local parser_dir = config.get_install_dir('parser')
|
|
|
|
|
local query_dir = config.get_install_dir('queries')
|
|
|
|
|
local installed = config.installed_parsers()
|
2020-08-01 18:32:12 +02:00
|
|
|
|
2023-06-12 09:54:30 -06:00
|
|
|
for _, lang in ipairs(languages) do
|
|
|
|
|
if not vim.list_contains(installed, lang) then
|
|
|
|
|
vim.notify(
|
|
|
|
|
'Parser for ' .. lang .. ' is is not managed by nvim-treesitter',
|
|
|
|
|
vim.log.levels.ERROR
|
|
|
|
|
)
|
2020-12-10 16:32:20 +00:00
|
|
|
else
|
2023-06-12 09:54:30 -06:00
|
|
|
local parser = utils.join_path(parser_dir, lang) .. '.so'
|
|
|
|
|
local queries = utils.join_path(query_dir, lang)
|
|
|
|
|
uninstall(lang, parser, queries)
|
2020-08-26 21:05:27 +02:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-19 14:43:55 +02:00
|
|
|
return M
|