mirror of
https://github.com/nvim-treesitter/nvim-treesitter.git
synced 2026-07-01 19:17:02 -04:00
If setup function was called after `FileType` autocommand triggers the installation of parser was not done. This commit checks if filetype is already know before adding the autocommand.
781 lines
23 KiB
Lua
781 lines
23 KiB
Lua
local api = vim.api
|
|
local fn = vim.fn
|
|
local luv = vim.loop
|
|
|
|
local utils = require "nvim-treesitter.utils"
|
|
local parsers = require "nvim-treesitter.parsers"
|
|
local info = require "nvim-treesitter.info"
|
|
local configs = require "nvim-treesitter.configs"
|
|
local shell = require "nvim-treesitter.shell_command_selectors"
|
|
local compat = require "nvim-treesitter.compat"
|
|
|
|
local M = {}
|
|
|
|
---@class LockfileInfo
|
|
---@field revision string
|
|
|
|
---@type table<string, LockfileInfo>
|
|
local lockfile = {}
|
|
|
|
M.compilers = { vim.fn.getenv "CC", "cc", "gcc", "clang", "cl", "zig" }
|
|
M.prefer_git = fn.has "win32" == 1
|
|
M.command_extra_args = {}
|
|
M.ts_generate_args = nil
|
|
|
|
local started_commands = 0
|
|
local finished_commands = 0
|
|
local failed_commands = 0
|
|
local complete_std_output = {}
|
|
local complete_error_output = {}
|
|
|
|
local function reset_progress_counter()
|
|
if started_commands ~= finished_commands then
|
|
return
|
|
end
|
|
started_commands = 0
|
|
finished_commands = 0
|
|
failed_commands = 0
|
|
complete_std_output = {}
|
|
complete_error_output = {}
|
|
end
|
|
|
|
local function get_job_status()
|
|
return "[nvim-treesitter] ["
|
|
.. finished_commands
|
|
.. "/"
|
|
.. started_commands
|
|
.. (failed_commands > 0 and ", failed: " .. failed_commands or "")
|
|
.. "]"
|
|
end
|
|
|
|
---@param lang string
|
|
---@return function
|
|
local function reattach_if_possible_fn(lang, error_on_fail)
|
|
return function()
|
|
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
|
|
if parsers.get_buf_lang(buf) == lang then
|
|
vim._ts_remove_language(lang)
|
|
local ok, err
|
|
if vim.treesitter.language.add then
|
|
local ft = vim.bo[buf].filetype
|
|
ok, err = pcall(vim.treesitter.language.add, lang, { filetype = ft })
|
|
else
|
|
ok, err = pcall(compat.require_language, lang)
|
|
end
|
|
if not ok and error_on_fail then
|
|
vim.notify("Could not load parser for " .. lang .. ": " .. vim.inspect(err))
|
|
end
|
|
for _, mod in ipairs(require("nvim-treesitter.configs").available_modules()) do
|
|
if ok then
|
|
require("nvim-treesitter.configs").reattach_module(mod, buf, lang)
|
|
else
|
|
require("nvim-treesitter.configs").detach_module(mod, buf)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param lang string
|
|
---@param validate boolean|nil
|
|
---@return InstallInfo
|
|
local function get_parser_install_info(lang, validate)
|
|
local parser_config = parsers.get_parser_configs()[lang]
|
|
|
|
if not parser_config then
|
|
error('Parser not available for language "' .. lang .. '"')
|
|
end
|
|
|
|
local install_info = parser_config.install_info
|
|
|
|
if validate then
|
|
vim.validate {
|
|
url = { install_info.url, "string" },
|
|
files = { install_info.files, "table" },
|
|
}
|
|
end
|
|
|
|
return install_info
|
|
end
|
|
|
|
local function load_lockfile()
|
|
local filename = utils.join_path(utils.get_package_path(), "lockfile.json")
|
|
lockfile = vim.fn.filereadable(filename) == 1 and vim.fn.json_decode(vim.fn.readfile(filename)) or {}
|
|
end
|
|
|
|
local function is_ignored_parser(lang)
|
|
return vim.tbl_contains(configs.get_ignored_parser_installs(), lang)
|
|
end
|
|
|
|
---@param lang string
|
|
---@return string|nil
|
|
local function get_revision(lang)
|
|
if #lockfile == 0 then
|
|
load_lockfile()
|
|
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(configs.get_parser_info_dir(), lang .. ".revision")
|
|
if vim.fn.filereadable(lang_file) == 1 then
|
|
return vim.fn.readfile(lang_file)[1]
|
|
end
|
|
end
|
|
|
|
-- Clean path for use in a prefix comparison
|
|
---@param input string
|
|
---@return string
|
|
local function clean_path(input)
|
|
local pth = vim.fn.fnamemodify(input, ":p")
|
|
if fn.has "win32" == 1 then
|
|
pth = pth:gsub("/", "\\")
|
|
end
|
|
return pth
|
|
end
|
|
|
|
-- Checks if parser is installed with nvim-treesitter
|
|
---@param lang string
|
|
---@return boolean
|
|
local function is_installed(lang)
|
|
local matched_parsers = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true) or {}
|
|
local install_dir = configs.get_parser_install_dir()
|
|
if not install_dir then
|
|
return false
|
|
end
|
|
install_dir = clean_path(install_dir)
|
|
for _, path in ipairs(matched_parsers) do
|
|
local abspath = clean_path(path)
|
|
if vim.startswith(abspath, install_dir) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
---@param lang string
|
|
---@return boolean
|
|
local function needs_update(lang)
|
|
local revision = get_revision(lang)
|
|
return not revision or revision ~= get_installed_revision(lang)
|
|
end
|
|
|
|
---@return string[]
|
|
local function outdated_parsers()
|
|
return vim.tbl_filter(function(lang) ---@param lang string
|
|
return is_installed(lang) and needs_update(lang)
|
|
end, info.installed_parsers())
|
|
end
|
|
|
|
---@param handle userdata
|
|
---@param is_stderr boolean
|
|
local function onread(handle, is_stderr)
|
|
return function(_, data)
|
|
if data then
|
|
if is_stderr then
|
|
complete_error_output[handle] = (complete_error_output[handle] or "") .. data
|
|
else
|
|
complete_std_output[handle] = (complete_std_output[handle] or "") .. data
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.iter_cmd(cmd_list, i, lang, success_message)
|
|
if i == 1 then
|
|
started_commands = started_commands + 1
|
|
end
|
|
if i == #cmd_list + 1 then
|
|
finished_commands = finished_commands + 1
|
|
return print(get_job_status() .. " " .. success_message)
|
|
end
|
|
|
|
local attr = cmd_list[i]
|
|
if attr.info then
|
|
print(get_job_status() .. " " .. attr.info)
|
|
end
|
|
|
|
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
|
|
|
|
if type(attr.cmd) == "function" then
|
|
local ok, err = pcall(attr.cmd)
|
|
if ok then
|
|
M.iter_cmd(cmd_list, i + 1, lang, success_message)
|
|
else
|
|
failed_commands = failed_commands + 1
|
|
finished_commands = finished_commands + 1
|
|
return api.nvim_err_writeln(
|
|
(attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr))) .. "\n" .. vim.inspect(err)
|
|
)
|
|
end
|
|
else
|
|
local handle
|
|
local stdout = luv.new_pipe(false)
|
|
local stderr = luv.new_pipe(false)
|
|
attr.opts.stdio = { nil, stdout, stderr }
|
|
---@type userdata
|
|
handle = luv.spawn(
|
|
attr.cmd,
|
|
attr.opts,
|
|
vim.schedule_wrap(function(code)
|
|
if code ~= 0 then
|
|
stdout:read_stop()
|
|
stderr:read_stop()
|
|
end
|
|
stdout:close()
|
|
stderr:close()
|
|
handle:close()
|
|
if code ~= 0 then
|
|
failed_commands = failed_commands + 1
|
|
finished_commands = finished_commands + 1
|
|
if complete_std_output[handle] and complete_std_output[handle] ~= "" then
|
|
print(complete_std_output[handle])
|
|
end
|
|
|
|
local err_msg = complete_error_output[handle] or ""
|
|
api.nvim_err_writeln(
|
|
"nvim-treesitter["
|
|
.. lang
|
|
.. "]: "
|
|
.. (attr.err or ("Failed to execute the following command:\n" .. vim.inspect(attr)))
|
|
.. "\n"
|
|
.. err_msg
|
|
)
|
|
return
|
|
end
|
|
M.iter_cmd(cmd_list, i + 1, lang, success_message)
|
|
end)
|
|
)
|
|
luv.read_start(stdout, onread(handle, false))
|
|
luv.read_start(stderr, onread(handle, true))
|
|
end
|
|
end
|
|
|
|
---@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)
|
|
end
|
|
end
|
|
|
|
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)
|
|
end
|
|
return command
|
|
end
|
|
|
|
---@param cmd_list Command[]
|
|
---@return boolean
|
|
local function iter_cmd_sync(cmd_list)
|
|
for _, cmd in ipairs(cmd_list) do
|
|
if cmd.info then
|
|
print(cmd.info)
|
|
end
|
|
|
|
if type(cmd.cmd) == "function" then
|
|
cmd.cmd()
|
|
else
|
|
local ret = vim.fn.system(get_command(cmd))
|
|
if vim.v.shell_error ~= 0 then
|
|
print(ret)
|
|
api.nvim_err_writeln(
|
|
(cmd.err and cmd.err .. "\n" or "") .. "Failed to execute the following command:\n" .. vim.inspect(cmd)
|
|
)
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
---@param cache_folder string
|
|
---@param install_folder string
|
|
---@param lang string
|
|
---@param repo InstallInfo
|
|
---@param with_sync boolean
|
|
---@param generate_from_grammar boolean
|
|
local function run_install(cache_folder, install_folder, lang, repo, with_sync, generate_from_grammar)
|
|
parsers.reset_cache()
|
|
|
|
local path_sep = utils.get_path_sep()
|
|
|
|
local project_name = "tree-sitter-" .. lang
|
|
local maybe_local_path = vim.fn.expand(repo.url)
|
|
local from_local_path = vim.fn.isdirectory(maybe_local_path) == 1
|
|
if from_local_path then
|
|
repo.url = maybe_local_path
|
|
end
|
|
|
|
---@type string compile_location only needed for typescript installs.
|
|
local compile_location
|
|
if from_local_path then
|
|
compile_location = repo.url
|
|
if repo.location then
|
|
compile_location = utils.join_path(compile_location, repo.location)
|
|
end
|
|
else
|
|
local repo_location = project_name
|
|
if repo.location then
|
|
repo_location = repo_location .. "/" .. repo.location
|
|
end
|
|
repo_location = repo_location:gsub("/", path_sep)
|
|
compile_location = utils.join_path(cache_folder, repo_location)
|
|
end
|
|
local parser_lib_name = utils.join_path(install_folder, lang) .. ".so"
|
|
|
|
generate_from_grammar = repo.requires_generate_from_grammar or generate_from_grammar
|
|
|
|
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!"
|
|
if repo.requires_generate_from_grammar then
|
|
api.nvim_err_writeln(
|
|
"tree-sitter CLI is needed because `"
|
|
.. lang
|
|
.. "` is marked that it needs "
|
|
.. "to be generated from the grammar definitions to be compatible with nvim!"
|
|
)
|
|
end
|
|
return
|
|
else
|
|
if not M.ts_generate_args then
|
|
local ts_cli_version = utils.ts_cli_version()
|
|
if ts_cli_version and vim.split(ts_cli_version, " ")[1] > "0.20.2" then
|
|
M.ts_generate_args = { "generate", "--no-bindings", "--abi", vim.treesitter.language_version }
|
|
else
|
|
M.ts_generate_args = { "generate", "--no-bindings" }
|
|
end
|
|
end
|
|
end
|
|
if generate_from_grammar and vim.fn.executable "node" ~= 1 then
|
|
api.nvim_err_writeln "Node JS not found: `node` is not executable!"
|
|
return
|
|
end
|
|
local cc = shell.select_executable(M.compilers)
|
|
if not cc then
|
|
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.')
|
|
return
|
|
end
|
|
|
|
local revision = repo.revision
|
|
if not revision then
|
|
revision = get_revision(lang)
|
|
end
|
|
|
|
---@class Command
|
|
---@field cmd string
|
|
---@field info string
|
|
---@field err string
|
|
---@field opts CmdOpts
|
|
|
|
---@class CmdOpts
|
|
---@field args string[]
|
|
---@field cwd string
|
|
|
|
---@type Command[]
|
|
local command_list = {}
|
|
if not from_local_path then
|
|
vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) })
|
|
vim.list_extend(
|
|
command_list,
|
|
shell.select_download_commands(repo, project_name, cache_folder, revision, M.prefer_git)
|
|
)
|
|
end
|
|
if generate_from_grammar then
|
|
if repo.generate_requires_npm then
|
|
if vim.fn.executable "npm" ~= 1 then
|
|
api.nvim_err_writeln("`" .. lang .. "` requires NPM to be installed from grammar.js")
|
|
return
|
|
end
|
|
vim.list_extend(command_list, {
|
|
{
|
|
cmd = "npm",
|
|
info = "Installing NPM dependencies of " .. lang .. " parser",
|
|
err = "Error during `npm install` (required for parser generation of " .. lang .. " with npm dependencies)",
|
|
opts = {
|
|
args = { "install" },
|
|
cwd = compile_location,
|
|
},
|
|
},
|
|
})
|
|
end
|
|
vim.list_extend(command_list, {
|
|
{
|
|
cmd = vim.fn.exepath "tree-sitter",
|
|
info = "Generating source files from grammar.js...",
|
|
err = 'Error during "tree-sitter generate"',
|
|
opts = {
|
|
args = M.ts_generate_args,
|
|
cwd = compile_location,
|
|
},
|
|
},
|
|
})
|
|
end
|
|
vim.list_extend(command_list, {
|
|
shell.select_compile_command(repo, cc, compile_location),
|
|
shell.select_mv_cmd("parser.so", parser_lib_name, compile_location),
|
|
{
|
|
cmd = function()
|
|
vim.fn.writefile({ revision or "" }, utils.join_path(configs.get_parser_info_dir() or "", lang .. ".revision"))
|
|
end,
|
|
},
|
|
{ -- auto-attach modules after installation
|
|
cmd = reattach_if_possible_fn(lang, true),
|
|
},
|
|
})
|
|
if not from_local_path then
|
|
vim.list_extend(command_list, { shell.select_install_rm_cmd(cache_folder, project_name) })
|
|
end
|
|
|
|
if with_sync then
|
|
if iter_cmd_sync(command_list) == true then
|
|
print("Treesitter parser for " .. lang .. " has been installed")
|
|
end
|
|
else
|
|
M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been installed")
|
|
end
|
|
end
|
|
|
|
---@param lang string
|
|
---@param ask_reinstall boolean|string
|
|
---@param cache_folder string
|
|
---@param install_folder string
|
|
---@param with_sync boolean
|
|
---@param generate_from_grammar boolean
|
|
local function install_lang(lang, ask_reinstall, cache_folder, install_folder, with_sync, generate_from_grammar)
|
|
if is_installed(lang) and ask_reinstall ~= "force" then
|
|
if not ask_reinstall then
|
|
return
|
|
end
|
|
|
|
local yesno = fn.input(lang .. " parser already available: would you like to reinstall ? y/n: ")
|
|
print "\n "
|
|
if not string.match(yesno, "^y.*") then
|
|
return
|
|
end
|
|
end
|
|
|
|
local ok, install_info = pcall(get_parser_install_info, lang, true)
|
|
if not ok then
|
|
vim.notify("Installation not possible: " .. install_info, vim.log.levels.ERROR)
|
|
if not parsers.get_parser_configs()[lang] then
|
|
vim.notify(
|
|
"See https://github.com/nvim-treesitter/nvim-treesitter/#adding-parsers on how to add a new parser!",
|
|
vim.log.levels.INFO
|
|
)
|
|
end
|
|
return
|
|
end
|
|
|
|
run_install(cache_folder, install_folder, lang, install_info, with_sync, generate_from_grammar)
|
|
end
|
|
|
|
---@class InstallOptions
|
|
---@field with_sync boolean
|
|
---@field ask_reinstall boolean|string
|
|
---@field generate_from_grammar boolean
|
|
---@field exclude_configured_parsers boolean
|
|
|
|
-- Install a parser
|
|
---@param options? InstallOptions
|
|
---@return function
|
|
local function install(options)
|
|
options = options or {}
|
|
local with_sync = options.with_sync
|
|
local ask_reinstall = options.ask_reinstall
|
|
local generate_from_grammar = options.generate_from_grammar
|
|
local exclude_configured_parsers = options.exclude_configured_parsers
|
|
|
|
return function(...)
|
|
if fn.executable "git" == 0 then
|
|
return api.nvim_err_writeln "Git is required on your system to run this command"
|
|
end
|
|
|
|
local cache_folder, err = utils.get_cache_dir()
|
|
if err then
|
|
return api.nvim_err_writeln(err)
|
|
end
|
|
assert(cache_folder)
|
|
|
|
local install_folder
|
|
install_folder, err = configs.get_parser_install_dir()
|
|
if err then
|
|
return api.nvim_err_writeln(err)
|
|
end
|
|
install_folder = install_folder and clean_path(install_folder)
|
|
assert(install_folder)
|
|
|
|
local languages ---@type string[]
|
|
local ask ---@type boolean|string
|
|
if ... == "all" then
|
|
languages = parsers.available_parsers()
|
|
ask = false
|
|
else
|
|
languages = compat.flatten { ... }
|
|
ask = ask_reinstall
|
|
end
|
|
|
|
if exclude_configured_parsers then
|
|
languages = utils.difference(languages, configs.get_ignored_parser_installs())
|
|
end
|
|
|
|
if #languages > 1 then
|
|
reset_progress_counter()
|
|
end
|
|
|
|
for _, lang in ipairs(languages) do
|
|
install_lang(lang, ask, cache_folder, install_folder, with_sync, generate_from_grammar)
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.setup_auto_install()
|
|
local function try_install_curr_lang()
|
|
local lang = parsers.get_buf_lang()
|
|
if parsers.get_parser_configs()[lang] and not is_installed(lang) and not is_ignored_parser(lang) then
|
|
install() { lang }
|
|
end
|
|
end
|
|
|
|
try_install_curr_lang()
|
|
|
|
vim.api.nvim_create_autocmd("FileType", {
|
|
pattern = { "*" },
|
|
group = vim.api.nvim_create_augroup("NvimTreesitter-auto_install", { clear = true }),
|
|
callback = try_install_curr_lang,
|
|
})
|
|
end
|
|
|
|
function M.update(options)
|
|
options = options or {}
|
|
return function(...)
|
|
M.lockfile = {}
|
|
reset_progress_counter()
|
|
if ... and ... ~= "all" then
|
|
---@type string[]
|
|
local languages = compat.flatten { ... }
|
|
local installed = 0
|
|
for _, lang in ipairs(languages) do
|
|
if (not is_installed(lang)) or (needs_update(lang)) then
|
|
installed = installed + 1
|
|
install {
|
|
ask_reinstall = "force",
|
|
with_sync = options.with_sync,
|
|
}(lang)
|
|
end
|
|
end
|
|
if installed == 0 then
|
|
utils.notify "Parsers are up-to-date!"
|
|
end
|
|
else
|
|
local parsers_to_update = outdated_parsers() or info.installed_parsers()
|
|
if #parsers_to_update == 0 then
|
|
utils.notify "All parsers are up-to-date!"
|
|
end
|
|
for _, lang in pairs(parsers_to_update) do
|
|
install {
|
|
ask_reinstall = "force",
|
|
exclude_configured_parsers = true,
|
|
with_sync = options.with_sync,
|
|
}(lang)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.uninstall(...)
|
|
if vim.tbl_contains({ "all" }, ...) then
|
|
reset_progress_counter()
|
|
local installed = info.installed_parsers()
|
|
M.uninstall(installed)
|
|
elseif ... then
|
|
local ensure_installed_parsers = configs.get_ensure_installed_parsers()
|
|
if ensure_installed_parsers == "all" then
|
|
ensure_installed_parsers = parsers.available_parsers()
|
|
end
|
|
ensure_installed_parsers = utils.difference(ensure_installed_parsers, configs.get_ignored_parser_installs())
|
|
|
|
---@type string[]
|
|
local languages = compat.flatten { ... }
|
|
for _, lang in ipairs(languages) do
|
|
local install_dir, err = configs.get_parser_install_dir()
|
|
if err then
|
|
return api.nvim_err_writeln(err)
|
|
end
|
|
install_dir = install_dir and clean_path(install_dir)
|
|
|
|
if vim.tbl_contains(ensure_installed_parsers, lang) then
|
|
vim.notify(
|
|
"Uninstalling "
|
|
.. lang
|
|
.. '. But the parser is still configured in "ensure_installed" setting of nvim-treesitter.'
|
|
.. " Please consider updating your config!",
|
|
vim.log.levels.ERROR
|
|
)
|
|
end
|
|
|
|
local parser_lib = utils.join_path(install_dir, lang) .. ".so"
|
|
local all_parsers = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true)
|
|
if vim.fn.filereadable(parser_lib) == 1 then
|
|
local command_list = {
|
|
shell.select_rm_file_cmd(parser_lib, "Uninstalling parser for " .. lang),
|
|
{
|
|
cmd = function()
|
|
local all_parsers_after_deletion = vim.api.nvim_get_runtime_file("parser/" .. lang .. ".so", true)
|
|
if #all_parsers_after_deletion > 0 then
|
|
vim.notify(
|
|
"Tried to uninstall parser for "
|
|
.. lang
|
|
.. "! But the parser is still installed (not by nvim-treesitter):"
|
|
.. table.concat(all_parsers_after_deletion, ", "),
|
|
vim.log.levels.ERROR
|
|
)
|
|
end
|
|
end,
|
|
},
|
|
{ -- auto-reattach or detach modules after uninstallation
|
|
cmd = reattach_if_possible_fn(lang, false),
|
|
},
|
|
}
|
|
M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been uninstalled")
|
|
elseif #all_parsers > 0 then
|
|
vim.notify(
|
|
"Parser for "
|
|
.. lang
|
|
.. " is installed! But not by nvim-treesitter! Please manually remove the following files: "
|
|
.. table.concat(all_parsers, ", "),
|
|
vim.log.levels.ERROR
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function M.write_lockfile(verbose, skip_langs)
|
|
local sorted_parsers = {} ---@type Parser[]
|
|
-- Load previous lockfile
|
|
load_lockfile()
|
|
skip_langs = skip_langs or {}
|
|
|
|
for k, v in pairs(parsers.get_parser_configs()) do
|
|
table.insert(sorted_parsers, { name = k, parser = v })
|
|
end
|
|
|
|
---@param a Parser
|
|
---@param b Parser
|
|
table.sort(sorted_parsers, function(a, b)
|
|
return a.name < b.name
|
|
end)
|
|
|
|
for _, v in ipairs(sorted_parsers) do
|
|
if not vim.tbl_contains(skip_langs, v.name) then
|
|
-- I'm sure this can be done in aync way with iter_cmd
|
|
local sha ---@type string
|
|
if v.parser.install_info.branch then
|
|
sha = vim.split(
|
|
vim.fn.systemlist(
|
|
"git ls-remote " .. v.parser.install_info.url .. " | grep refs/heads/" .. v.parser.install_info.branch
|
|
)[1],
|
|
"\t"
|
|
)[1]
|
|
else
|
|
sha = vim.split(vim.fn.systemlist("git ls-remote " .. v.parser.install_info.url)[1], "\t")[1]
|
|
end
|
|
lockfile[v.name] = { revision = sha }
|
|
if verbose then
|
|
print(v.name .. ": " .. sha)
|
|
end
|
|
else
|
|
print("Skipping " .. v.name)
|
|
end
|
|
end
|
|
|
|
if verbose then
|
|
print(vim.inspect(lockfile))
|
|
end
|
|
vim.fn.writefile(
|
|
vim.fn.split(vim.fn.json_encode(lockfile), "\n"),
|
|
utils.join_path(utils.get_package_path(), "lockfile.json")
|
|
)
|
|
end
|
|
|
|
M.ensure_installed = install { exclude_configured_parsers = true }
|
|
M.ensure_installed_sync = install { with_sync = true, exclude_configured_parsers = true }
|
|
|
|
M.commands = {
|
|
TSInstall = {
|
|
run = install { ask_reinstall = true },
|
|
["run!"] = install { ask_reinstall = "force" },
|
|
args = {
|
|
"-nargs=+",
|
|
"-bang",
|
|
"-complete=custom,nvim_treesitter#installable_parsers",
|
|
},
|
|
},
|
|
TSInstallFromGrammar = {
|
|
run = install { generate_from_grammar = true, ask_reinstall = true },
|
|
["run!"] = install { generate_from_grammar = true, ask_reinstall = "force" },
|
|
args = {
|
|
"-nargs=+",
|
|
"-bang",
|
|
"-complete=custom,nvim_treesitter#installable_parsers",
|
|
},
|
|
},
|
|
TSInstallSync = {
|
|
run = install { with_sync = true, ask_reinstall = true },
|
|
["run!"] = install { with_sync = true, ask_reinstall = "force" },
|
|
args = {
|
|
"-nargs=+",
|
|
"-bang",
|
|
"-complete=custom,nvim_treesitter#installable_parsers",
|
|
},
|
|
},
|
|
TSUpdate = {
|
|
run = M.update {},
|
|
args = {
|
|
"-nargs=*",
|
|
"-complete=custom,nvim_treesitter#installed_parsers",
|
|
},
|
|
},
|
|
TSUpdateSync = {
|
|
run = M.update { with_sync = true },
|
|
args = {
|
|
"-nargs=*",
|
|
"-complete=custom,nvim_treesitter#installed_parsers",
|
|
},
|
|
},
|
|
TSUninstall = {
|
|
run = M.uninstall,
|
|
args = {
|
|
"-nargs=+",
|
|
"-complete=custom,nvim_treesitter#installed_parsers",
|
|
},
|
|
},
|
|
}
|
|
|
|
return M
|