diff options
Diffstat (limited to 'lua/nvim-treesitter/install.lua')
| -rw-r--r-- | lua/nvim-treesitter/install.lua | 335 |
1 files changed, 184 insertions, 151 deletions
diff --git a/lua/nvim-treesitter/install.lua b/lua/nvim-treesitter/install.lua index c2bdc934..25ec2be1 100644 --- a/lua/nvim-treesitter/install.lua +++ b/lua/nvim-treesitter/install.lua @@ -2,16 +2,16 @@ 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 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 M = {} local lockfile = {} -M.compilers = { vim.fn.getenv('CC'), "cc", "gcc", "clang", "cl" } +M.compilers = { vim.fn.getenv "CC", "cc", "gcc", "clang", "cl" } local started_commands = 0 local finished_commands = 0 @@ -31,27 +31,31 @@ local function reset_progress_counter() end local function get_job_status() - return "[nvim-treesitter] ["..finished_commands.."/"..started_commands - ..(failed_commands > 0 and ", failed: "..failed_commands or "").."]" + return "[nvim-treesitter] [" + .. finished_commands + .. "/" + .. started_commands + .. (failed_commands > 0 and ", failed: " .. failed_commands or "") + .. "]" end local function get_revision(lang) if #lockfile == 0 then - local filename = utils.join_path(utils.get_package_path(), 'lockfile.json') + 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 return (lockfile[lang] and lockfile[lang].revision) end local function get_installed_revision(lang) - local lang_file = utils.join_path(utils.get_parser_info_dir(), lang..'.revision') + local lang_file = utils.join_path(utils.get_parser_info_dir(), lang .. ".revision") if vim.fn.filereadable(lang_file) == 1 then return vim.fn.readfile(lang_file)[1] end end local function is_installed(lang) - return #api.nvim_get_runtime_file('parser/'..lang..'.so', false) > 0 + return #api.nvim_get_runtime_file("parser/" .. lang .. ".so", false) > 0 end local function needs_update(lang) @@ -61,17 +65,16 @@ end local function outdated_parsers() return vim.tbl_filter(function(lang) return needs_update(lang) - end, - info.installed_parsers()) + end, info.installed_parsers()) end local function onread(handle, is_stderr) return function(err, data) if data then if is_stderr then - complete_error_output[handle] = (complete_error_output[handle] or '')..data + complete_error_output[handle] = (complete_error_output[handle] or "") .. data else - complete_std_output[handle] = (complete_std_output[handle] or '')..data + complete_std_output[handle] = (complete_std_output[handle] or "") .. data end end end @@ -83,52 +86,62 @@ function M.iter_cmd(cmd_list, i, lang, success_message) end if i == #cmd_list + 1 then finished_commands = finished_commands + 1 - return print(get_job_status().." "..success_message) + 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.info then + print(get_job_status() .. " " .. attr.info) + end - if type(attr.cmd) == 'function' then + 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)) + 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} - 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]) + attr.opts.stdio = { nil, stdout, stderr } + 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)) + 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 @@ -142,7 +155,7 @@ local function get_command(cmd) end end - local final = string.format('%s %s', cmd.cmd, options) + local final = string.format("%s %s", cmd.cmd, options) if cmd.opts and cmd.opts.cwd then final = shell.make_directory_change_for_command(cmd.opts.cwd, final) end @@ -155,19 +168,18 @@ local function iter_cmd_sync(cmd_list) print(cmd.info) end - if type(cmd.cmd) == 'function' then + 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)) + 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 @@ -178,7 +190,7 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync, local path_sep = utils.get_path_sep() - local project_name = 'tree-sitter-'..lang + 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 @@ -190,33 +202,40 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync, if from_local_path then compile_location = repo.url else - local repo_location = string.gsub(repo.location or project_name, '/', path_sep) - compile_location = cache_folder..path_sep..repo_location + local repo_location = string.gsub(repo.location or project_name, "/", path_sep) + compile_location = cache_folder .. path_sep .. repo_location end - local parser_lib_name = install_folder..path_sep..lang..".so" + local parser_lib_name = install_folder .. path_sep .. 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 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!') + 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 end - if generate_from_grammar and vim.fn.executable('node') ~= 1 then - api.nvim_err_writeln('Node JS not found: `node` is not executable!') + 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) return type(c) == 'string' end, M.compilers), '", "') - ..'" are not executable.') + api.nvim_err_writeln('No C compiler found! "' .. table.concat( + vim.tbl_filter(function(c) + return type(c) == "string" + end, M.compilers), + '", "' + ) .. '" are not executable.') return end - local revision = configs.get_update_strategy() == 'lockfile' and get_revision(lang) + local revision = configs.get_update_strategy() == "lockfile" and get_revision(lang) local command_list = {} if not from_local_path then @@ -225,83 +244,86 @@ local function run_install(cache_folder, install_folder, lang, repo, with_sync, 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') + 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)', + 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 - } - } + args = { "install" }, + cwd = compile_location, + }, + }, }) end vim.list_extend(command_list, { { - cmd = vim.fn.exepath('tree-sitter'), - info = 'Generating source files from grammar.js...', + cmd = vim.fn.exepath "tree-sitter", + info = "Generating source files from grammar.js...", err = 'Error during "tree-sitter generate"', opts = { - args = {'generate'}, - cwd = compile_location - } - } + args = { "generate" }, + cwd = compile_location, + }, + }, }) end vim.list_extend(command_list, { { cmd = cc, - info = 'Compiling...', - err = 'Error during compilation', + info = "Compiling...", + err = "Error during compilation", opts = { args = vim.tbl_flatten(shell.select_compiler_args(repo, cc)), - cwd = compile_location - } + cwd = compile_location, + }, }, - shell.select_mv_cmd('parser.so', parser_lib_name, compile_location), + shell.select_mv_cmd("parser.so", parser_lib_name, compile_location), { cmd = function() - vim.fn.writefile({revision or ''}, utils.join_path(utils.get_parser_info_dir(), lang..'.revision')) - end - } + vim.fn.writefile({ revision or "" }, utils.join_path(utils.get_parser_info_dir(), lang .. ".revision")) + end, + }, }) 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_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') + print("Treesitter parser for " .. lang .. " has been installed") end else - M.iter_cmd(command_list, 1, lang, 'Treesitter parser for '..lang..' has been installed') + M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been installed") end end 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 - 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 ') -- mandatory to avoid messing up command line - if not string.match(yesno, '^y.*') 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 parser_config = parsers.get_parser_configs()[lang] if not parser_config then - return api.nvim_err_writeln('Parser not available for language '..lang) + return api.nvim_err_writeln("Parser not available for language " .. lang) end local install_info = parser_config.install_info vim.validate { - url={ install_info.url, 'string' }, - files={ install_info.files, 'table' } + url = { install_info.url, "string" }, + files = { install_info.files, "table" }, } run_install(cache_folder, install_folder, lang, install_info, with_sync, generate_from_grammar) @@ -314,27 +336,31 @@ local function install(options) 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') + 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 + if err then + return api.nvim_err_writeln(err) + end local install_folder, err = utils.get_parser_install_dir() - if err then return api.nvim_err_writeln(err) end + if err then + return api.nvim_err_writeln(err) + end local languages local ask - if ... == 'all' then + if ... == "all" then languages = parsers.available_parsers() ask = false - elseif ... == 'maintained' then + elseif ... == "maintained" then languages = parsers.maintained_parsers() ask = false else - languages = vim.tbl_flatten({...}) + languages = vim.tbl_flatten { ... } ask = ask_reinstall end @@ -354,69 +380,73 @@ end function M.update(options) options = options or {} - return function (...) + return function(...) M.lockfile = {} reset_progress_counter() - if ... and ... ~= 'all' then - local languages = vim.tbl_flatten({...}) + if ... and ... ~= "all" then + local languages = vim.tbl_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 + 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 - print('Parsers are up-to-date!') + print "Parsers are up-to-date!" end else - local parsers_to_update = configs.get_update_strategy() == 'lockfile' - and outdated_parsers() + local parsers_to_update = configs.get_update_strategy() == "lockfile" and outdated_parsers() or info.installed_parsers() if #parsers_to_update == 0 then - print('All parsers are up-to-date!') + print "All parsers are up-to-date!" end for _, lang in pairs(parsers_to_update) do - install({ - ask_reinstall = 'force', + install { + ask_reinstall = "force", exclude_configured_parsers = true, - with_sync = options.with_sync - })(lang) + with_sync = options.with_sync, + }(lang) end end end end function M.uninstall(...) - local path_sep = '/' - if fn.has('win32') == 1 then - path_sep = '\\' + local path_sep = "/" + if fn.has "win32" == 1 then + path_sep = "\\" end - if vim.tbl_contains({'all', 'maintained'}, ...) then + if vim.tbl_contains({ "all", "maintained" }, ...) then reset_progress_counter() local installed = info.installed_parsers() if ... == "maintained" then local maintained = parsers.maintained_parsers() - installed = vim.tbl_filter(function(l) return vim.tbl_contains(maintained, l) end, installed) + installed = vim.tbl_filter(function(l) + return vim.tbl_contains(maintained, l) + end, installed) end for _, langitem in pairs(installed) do M.uninstall(langitem) end elseif ... then - local languages = vim.tbl_flatten({...}) + local languages = vim.tbl_flatten { ... } for _, lang in ipairs(languages) do local install_dir, err = utils.get_parser_install_dir() - if err then return api.nvim_err_writeln(err) end + if err then + return api.nvim_err_writeln(err) + end - local parser_lib = install_dir..path_sep..lang..".so" + local parser_lib = install_dir .. path_sep .. lang .. ".so" local command_list = { - shell.select_rm_file_cmd(parser_lib, "Uninstalling parser for "..lang) + shell.select_rm_file_cmd(parser_lib, "Uninstalling parser for " .. lang), } - M.iter_cmd(command_list, 1, lang, 'Treesitter parser for '..lang..' has been uninstalled') + M.iter_cmd(command_list, 1, lang, "Treesitter parser for " .. lang .. " has been uninstalled") end end end @@ -428,38 +458,41 @@ function M.write_lockfile(verbose, skip_langs) skip_langs = skip_langs or {} for k, v in pairs(parsers.get_parser_configs()) do - table.insert(sorted_parsers, {name = k, parser = v}) + table.insert(sorted_parsers, { name = k, parser = v }) end - table.sort(sorted_parsers, function(a, b) return a.name < b.name end) + 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 = vim.split(vim.fn.systemlist('git ls-remote '..v.parser.install_info.url)[1], '\t')[1] + local sha = vim.split(vim.fn.systemlist("git ls-remote " .. v.parser.install_info.url)[1], "\t")[1] lockfile[v.name] = { revision = sha } if verbose then - print(v.name..': '..sha) + print(v.name .. ": " .. sha) end else - print('Skipping '..v.name) + 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")) + 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 = install { exclude_configured_parsers = true } M.commands = { TSInstall = { - run = install({ ask_reinstall = true }), - ['run!'] = install({ ask_reinstall = 'force' }), + run = install { ask_reinstall = true }, + ["run!"] = install { ask_reinstall = "force" }, args = { "-nargs=+", "-bang", @@ -467,8 +500,8 @@ M.commands = { }, }, TSInstallFromGrammar = { - run = install({ generate_from_grammar = true, ask_reinstall = true }), - ['run!'] = install({ generate_from_grammar = true, ask_reinstall = 'force' }), + run = install { generate_from_grammar = true, ask_reinstall = true }, + ["run!"] = install { generate_from_grammar = true, ask_reinstall = "force" }, args = { "-nargs=+", "-bang", @@ -476,8 +509,8 @@ M.commands = { }, }, TSInstallSync = { - run = install( { with_sync = true, ask_reinstall = true }), - ['run!'] = install( { with_sync = true, ask_reinstall = 'force' }), + run = install { with_sync = true, ask_reinstall = true }, + ["run!"] = install { with_sync = true, ask_reinstall = "force" }, args = { "-nargs=+", "-bang", @@ -485,14 +518,14 @@ M.commands = { }, }, TSUpdate = { - run = M.update({}), + run = M.update {}, args = { "-nargs=*", "-complete=custom,nvim_treesitter#installed_parsers", }, }, TSUpdateSync = { - run = M.update({ with_sync = true }), + run = M.update { with_sync = true }, args = { "-nargs=*", "-complete=custom,nvim_treesitter#installed_parsers", |
