diff options
Diffstat (limited to 'lua/nvim-lsp-installer/core')
| -rw-r--r-- | lua/nvim-lsp-installer/core/async/uv.lua | 2 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/clients/crates.lua | 23 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/clients/eclipse.lua | 12 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/clients/github.lua | 92 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/fetch.lua | 98 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/installer/context.lua | 3 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/cargo/client.lua | 14 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/cargo/init.lua | 32 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/gem/init.lua | 4 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/github/client.lua | 88 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/npm/init.lua | 2 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/powershell/init.lua | 46 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/std/init.lua | 44 |
13 files changed, 236 insertions, 224 deletions
diff --git a/lua/nvim-lsp-installer/core/async/uv.lua b/lua/nvim-lsp-installer/core/async/uv.lua index a2249be9..a25aeb0b 100644 --- a/lua/nvim-lsp-installer/core/async/uv.lua +++ b/lua/nvim-lsp-installer/core/async/uv.lua @@ -1,6 +1,6 @@ local a = require "nvim-lsp-installer.core.async" ----@type Record<UvMethod, async fun(...)> +---@type table<UvMethod, async fun(...)> local M = setmetatable({}, { __index = function(_, method) ---@async diff --git a/lua/nvim-lsp-installer/core/clients/crates.lua b/lua/nvim-lsp-installer/core/clients/crates.lua deleted file mode 100644 index f12a8356..00000000 --- a/lua/nvim-lsp-installer/core/clients/crates.lua +++ /dev/null @@ -1,23 +0,0 @@ -local fetch = require "nvim-lsp-installer.core.fetch" -local M = {} - ----@alias Crate {crate: {id: string, max_stable_version: string, max_version: string, newest_version: string}} - ----@param crate string ----@param callback fun(err: string|nil, data: Crate|nil) -function M.fetch_crate(crate, callback) - fetch(("https://crates.io/api/v1/crates/%s"):format(crate), function(err, data) - if err then - callback(err, nil) - return - end - local ok, response = pcall(vim.json.decode, data) - if not ok then - callback("Failed to deserialize crates.io API response.", nil) - return - end - callback(nil, response) - end) -end - -return M diff --git a/lua/nvim-lsp-installer/core/clients/eclipse.lua b/lua/nvim-lsp-installer/core/clients/eclipse.lua index a788f2e2..0d169d9d 100644 --- a/lua/nvim-lsp-installer/core/clients/eclipse.lua +++ b/lua/nvim-lsp-installer/core/clients/eclipse.lua @@ -7,15 +7,9 @@ function M._parse_jdtls_version_string(version) return vim.trim(version):gsub("^jdt%-language%-server%-", ""):gsub("%.tar%.gz$", "") end ----@param callback fun(err: string|nil, data: string|nil) -function M.fetch_latest_jdtls_version(callback) - fetch("https://download.eclipse.org/jdtls/snapshots/latest.txt", function(err, data) - if err then - callback(err, nil) - else - callback(nil, M._parse_jdtls_version_string(data)) - end - end) +---@async +function M.fetch_latest_jdtls_version() + return fetch("https://download.eclipse.org/jdtls/snapshots/latest.txt"):map(M._parse_jdtls_version_string) end return M diff --git a/lua/nvim-lsp-installer/core/clients/github.lua b/lua/nvim-lsp-installer/core/clients/github.lua deleted file mode 100644 index b78a8964..00000000 --- a/lua/nvim-lsp-installer/core/clients/github.lua +++ /dev/null @@ -1,92 +0,0 @@ -local fetch = require "nvim-lsp-installer.core.fetch" -local Data = require "nvim-lsp-installer.data" -local log = require "nvim-lsp-installer.log" - -local list_find_first = Data.list_find_first - -local M = {} - ----@alias GitHubRelease {tag_name:string, prerelease: boolean, draft: boolean} ----@alias GitHubTag {name: string} - ----@param repo string The GitHub repo ("username/repo"). ----@param callback fun(error: string|nil, data: GitHubRelease[]|nil) -function M.fetch_releases(repo, callback) - log.fmt_trace("Fetching GitHub releases for repo=%s", repo) - fetch(("https://api.github.com/repos/%s/releases"):format(repo), { - custom_fetcher = { - cmd = "gh", - args = { "api", ("repos/%s/releases"):format(repo) }, - }, - }, function(err, response) - if err then - log.fmt_error("Failed to fetch releases for repo=%s", repo) - return callback("Failed to fetch GitHub releases.", nil) - end - callback(nil, vim.json.decode(response)) - end) -end - ----@alias FetchLatestGithubReleaseOpts {tag_name_pattern:string} ----@param repo string The GitHub repo ("username/repo"). ----@param opts FetchLatestGithubReleaseOpts|nil ----@param callback fun(error: string|nil, data: GitHubRelease|nil) -function M.fetch_latest_release(repo, opts, callback) - M.fetch_releases(repo, function(err, releases) - if err then - callback(err, nil) - return - end - - local latest_release = list_find_first(releases, function(_release) - ---@type GitHubRelease - local release = _release - local is_stable_release = not release.prerelease and not release.draft - if opts.tag_name_pattern then - return is_stable_release and release.tag_name:match(opts.tag_name_pattern) - end - return is_stable_release - end) - - if not latest_release then - log.fmt_info("Failed to find latest release. repo=%s, opts=%s", repo, opts) - return callback("Failed to find latest release.", nil) - end - - log.fmt_debug("Resolved latest version repo=%s, tag_name=%s", repo, latest_release.tag_name) - callback(nil, latest_release) - end) -end - ----@param repo string The GitHub repo ("username/repo"). ----@param callback fun(err: string|nil, tags: GitHubTag[]|nil) -function M.fetch_tags(repo, callback) - fetch(("https://api.github.com/repos/%s/tags"):format(repo), { - custom_fetcher = { - cmd = "gh", - args = { "api", ("repos/%s/tags"):format(repo) }, - }, - }, function(err, response) - if err then - log.fmt_error("Failed to fetch tags for repo=%s", err) - return callback("Failed to fetch tags.", nil) - end - callback(nil, vim.json.decode(response)) - end) -end - ----@param repo string The GitHub repo ("username/repo"). ----@param callback fun(err: string|nil, latest_tag: GitHubTag|nil) -function M.fetch_latest_tag(repo, callback) - M.fetch_tags(repo, function(err, tags) - if err then - return callback(err, nil) - end - if vim.tbl_count(tags) == 0 then - return callback("No tags found.", nil) - end - callback(nil, tags[1]) - end) -end - -return M diff --git a/lua/nvim-lsp-installer/core/fetch.lua b/lua/nvim-lsp-installer/core/fetch.lua index bd1a582d..d97da59b 100644 --- a/lua/nvim-lsp-installer/core/fetch.lua +++ b/lua/nvim-lsp-installer/core/fetch.lua @@ -1,6 +1,8 @@ local log = require "nvim-lsp-installer.log" -local process = require "nvim-lsp-installer.process" local platform = require "nvim-lsp-installer.platform" +local Result = require "nvim-lsp-installer.core.result" +local spawn = require "nvim-lsp-installer.core.spawn" +local powershell = require "nvim-lsp-installer.core.managers.powershell" local USER_AGENT = "nvim-lsp-installer (+https://github.com/williamboman/nvim-lsp-installer)" @@ -10,89 +12,29 @@ local HEADERS = { iwr = ("-Headers @{'User-Agent' = '%s'}"):format(USER_AGENT), } -local function with_headers(headers, args) - local result = {} - vim.list_extend(result, headers) - vim.list_extend(result, args) - return result -end - ----@alias FetchCallback fun(err: string|nil, raw_data: string) - ----@param url string The url to fetch. ----@param callback_or_opts FetchCallback|{custom_fetcher: { cmd: string, args: string[] }} ----@param callback FetchCallback -local function fetch(url, callback_or_opts, callback) - local opts = type(callback_or_opts) == "table" and callback_or_opts or {} - callback = type(callback_or_opts) == "function" and callback_or_opts or callback - local stdio = process.in_memory_sink() +---@async +---@param url string @The url to fetch. +local function fetch(url) log.fmt_debug("Fetching URL %s", url) - local on_exit = function(success) - if success then - log.fmt_debug("Successfully fetched URL %s", url) - callback(nil, table.concat(stdio.buffers.stdout, "")) - else - local stderr = table.concat(stdio.buffers.stderr, "") - log.fmt_warn("Failed to fetch URL %s. stderr=%s", url, stderr) - callback(("Failed to fetch url %q.\n%s"):format(url, stderr), nil) - end - end - local job_variants = { - process.lazy_spawn("wget", { - args = with_headers(HEADERS.wget, { "-nv", "-O", "-", url }), - stdio_sink = stdio.sink, - }), - process.lazy_spawn("curl", { - args = with_headers(HEADERS.curl, { "-fsSL", url }), - stdio_sink = stdio.sink, - }), - } + local platform_specific = Result.failure() if platform.is_win then - local ps_script = { - "$ProgressPreference = 'SilentlyContinue';", - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", - ("Write-Output (iwr %s -UseBasicParsing -Uri %q).Content"):format(HEADERS.iwr, url), - } - table.insert( - job_variants, - 1, - process.lazy_spawn("powershell.exe", { - args = { "-NoProfile", "-Command", table.concat(ps_script, ";") }, - stdio_sink = stdio.sink, - env = process.graft_env({}, { "PSMODULEPATH" }), - }) - ) - end - - if opts.custom_fetcher then - table.insert( - job_variants, - 1, - process.lazy_spawn(opts.custom_fetcher.cmd, { - args = opts.custom_fetcher.args, - stdio_sink = stdio.sink, - }) + platform_specific = powershell.command( + ([[Write-Output (iwr %s -UseBasicParsing -Uri %q).Content;]]):format(HEADERS.iwr, url) ) end - process.attempt { - jobs = job_variants, - on_iterate = function() - log.debug "Flushing stdout/stderr buffers." - stdio.buffers.stdout = {} - stdio.buffers.stderr = {} - end, - on_finish = on_exit, - } + return platform_specific + :recover_catching(function() + return spawn.wget({ HEADERS.wget, "-nv", "-O", "-", url }):get_or_throw() + end) + :recover_catching(function() + return spawn.curl({ HEADERS.curl, "-fsSL", url }):get_or_throw() + end) + :map(function(result) + return result.stdout + end) end -return setmetatable({ - with_headers = with_headers, - HEADERS = HEADERS, -}, { - __call = function(_, ...) - return fetch(...) - end, -}) +return fetch diff --git a/lua/nvim-lsp-installer/core/installer/context.lua b/lua/nvim-lsp-installer/core/installer/context.lua index 0b13859b..5705fa41 100644 --- a/lua/nvim-lsp-installer/core/installer/context.lua +++ b/lua/nvim-lsp-installer/core/installer/context.lua @@ -76,7 +76,8 @@ function CwdManager.new(boundary_path, cwd) end function CwdManager:get() - return assert(self.cwd, "Tried to access cwd before it was set.") + assert(self.cwd ~= nil, "Tried to access cwd before it was set.") + return self.cwd end ---@param new_cwd string diff --git a/lua/nvim-lsp-installer/core/managers/cargo/client.lua b/lua/nvim-lsp-installer/core/managers/cargo/client.lua new file mode 100644 index 00000000..7e69cdf4 --- /dev/null +++ b/lua/nvim-lsp-installer/core/managers/cargo/client.lua @@ -0,0 +1,14 @@ +local fetch = require "nvim-lsp-installer.core.fetch" + +local M = {} + +---@alias CrateResponse {crate: {id: string, max_stable_version: string, max_version: string, newest_version: string}} + +---@async +---@param crate string +---@return Result @of [Crate] +function M.fetch_crate(crate) + return fetch(("https://crates.io/api/v1/crates/%s"):format(crate)):map_catching(vim.json.decode) +end + +return M diff --git a/lua/nvim-lsp-installer/core/managers/cargo/init.lua b/lua/nvim-lsp-installer/core/managers/cargo/init.lua index 5d74e010..84beaf1e 100644 --- a/lua/nvim-lsp-installer/core/managers/cargo/init.lua +++ b/lua/nvim-lsp-installer/core/managers/cargo/init.lua @@ -3,11 +3,8 @@ local path = require "nvim-lsp-installer.path" local spawn = require "nvim-lsp-installer.core.spawn" local a = require "nvim-lsp-installer.core.async" local Optional = require "nvim-lsp-installer.core.optional" -local crates = require "nvim-lsp-installer.core.clients.crates" -local Result = require "nvim-lsp-installer.core.result" local installer = require "nvim-lsp-installer.core.installer" - -local fetch_crate = a.promisify(crates.fetch_crate, true) +local client = require "nvim-lsp-installer.core.managers.cargo.client" ---@param crate string local function with_receipt(crate) @@ -58,7 +55,7 @@ function M.install(crate, opts) end ---@param output string @The `cargo install --list` output. ----@return Record<string, string> @Key is the crate name, value is its version. +---@return table<string, string> @Key is the crate name, value is its version. function M.parse_installed_crates(output) local installed_crates = {} for _, line in ipairs(vim.split(output, "\n")) do @@ -74,18 +71,19 @@ end ---@param receipt InstallReceipt ---@param install_dir string function M.check_outdated_primary_package(receipt, install_dir) - local installed_version = M.get_installed_primary_package_version(receipt, install_dir):get_or_throw() - - local response = fetch_crate(receipt.primary_source.package) - if installed_version ~= response.crate.max_stable_version then - return Result.success { - name = receipt.primary_source.package, - current_version = installed_version, - latest_version = response.crate.max_stable_version, - } - else - return Result.failure "Primary package is not outdated." - end + return M.get_installed_primary_package_version(receipt, install_dir):map_catching(function(installed_version) + ---@type CrateResponse + local crate_response = client.fetch_crate(receipt.primary_source.package):get_or_throw() + if installed_version ~= crate_response.crate.max_stable_version then + return { + name = receipt.primary_source.package, + current_version = installed_version, + latest_version = crate_response.crate.max_stable_version, + } + else + error "Primary package is not outdated." + end + end) end ---@async diff --git a/lua/nvim-lsp-installer/core/managers/gem/init.lua b/lua/nvim-lsp-installer/core/managers/gem/init.lua index f40cb736..4191d587 100644 --- a/lua/nvim-lsp-installer/core/managers/gem/init.lua +++ b/lua/nvim-lsp-installer/core/managers/gem/init.lua @@ -75,10 +75,10 @@ function M.parse_outdated_gem(outdated_gem) return outdated_package end ----Parses the stdout of the `gem list` command into a Record<package_name, version> +---Parses the stdout of the `gem list` command into a table<package_name, version> ---@param output string function M.parse_gem_list_output(output) - ---@type Record<string, string> + ---@type table<string, string> local gem_versions = {} for _, line in ipairs(vim.split(output, "\n")) do local gem_package, version = line:match "^(%S+) %((%S+)%)$" diff --git a/lua/nvim-lsp-installer/core/managers/github/client.lua b/lua/nvim-lsp-installer/core/managers/github/client.lua new file mode 100644 index 00000000..a8766748 --- /dev/null +++ b/lua/nvim-lsp-installer/core/managers/github/client.lua @@ -0,0 +1,88 @@ +local Data = require "nvim-lsp-installer.data" +local log = require "nvim-lsp-installer.log" +local fetch = require "nvim-lsp-installer.core.fetch" +local spawn = require "nvim-lsp-installer.core.spawn" + +local list_find_first = Data.list_find_first + +local M = {} + +---@alias GitHubRelease {tag_name:string, prerelease: boolean, draft: boolean} +---@alias GitHubTag {name: string} + +---@async +---@param repo string The GitHub repo ("username/repo"). +function M.fetch_releases(repo) + log.fmt_trace("Fetching GitHub releases for repo=%s", repo) + local path = ("repos/%s/releases"):format(repo) + return spawn.gh({ "api", path }) + :map(function(result) + return result.stdout + end) + :recover_catching(function() + return fetch(("https://api.github.com/%s"):format(path)):get_or_throw() + end) + :map_catching(vim.json.decode) +end + +---@alias FetchLatestGithubReleaseOpts {tag_name_pattern:string} + +---@async +---@param repo string The GitHub repo ("username/repo"). +---@param opts FetchLatestGithubReleaseOpts|nil +---@return Result @of GitHubRelease +function M.fetch_latest_release(repo, opts) + opts = opts or {} + return M.fetch_releases(repo):map_catching( + ---@param releases GitHubRelease[] + function(releases) + ---@type GitHubRelease|nil + local latest_release = list_find_first( + releases, + ---@param release GitHubRelease + function(release) + local is_stable_release = not release.prerelease and not release.draft + if opts.tag_name_pattern then + return is_stable_release and release.tag_name:match(opts.tag_name_pattern) + end + return is_stable_release + end + ) + + if not latest_release then + log.fmt_info("Failed to find latest release. repo=%s, opts=%s", repo, opts) + error "Failed to find latest release." + end + + log.fmt_debug("Resolved latest version repo=%s, tag_name=%s", repo, latest_release.tag_name) + return latest_release + end + ) +end + +---@async +---@param repo string The GitHub repo ("username/repo"). +function M.fetch_tags(repo) + local path = ("repos/%s/tags"):format(repo) + return spawn.gh({ "api", path }) + :map(function(result) + return result.stdout + end) + :recover_catching(function() + return fetch(("https://api.github.com/%s"):format(path)):get_or_throw() + end) + :map_catching(vim.json.decode) +end + +---@async +---@param repo string The GitHub repo ("username/repo"). +function M.fetch_latest_tag(repo) + return M.fetch_tags(repo):map_catching(function(tags) + if vim.tbl_count(tags) == 0 then + error "No tags found." + end + return tags[1] + end) +end + +return M diff --git a/lua/nvim-lsp-installer/core/managers/npm/init.lua b/lua/nvim-lsp-installer/core/managers/npm/init.lua index a4631e42..94d8b2ee 100644 --- a/lua/nvim-lsp-installer/core/managers/npm/init.lua +++ b/lua/nvim-lsp-installer/core/managers/npm/init.lua @@ -69,7 +69,7 @@ end ---@param exec_args string[] @The arguments to pass to npm exec. function M.exec(exec_args) local ctx = installer.context() - ctx.spawn.npm { "exec", "--yes", exec_args } + ctx.spawn.npm { "exec", "--yes", "--", exec_args } end ---@async diff --git a/lua/nvim-lsp-installer/core/managers/powershell/init.lua b/lua/nvim-lsp-installer/core/managers/powershell/init.lua new file mode 100644 index 00000000..a246e24c --- /dev/null +++ b/lua/nvim-lsp-installer/core/managers/powershell/init.lua @@ -0,0 +1,46 @@ +local spawn = require "nvim-lsp-installer.core.spawn" +local process = require "nvim-lsp-installer.process" + +local M = {} + +local PWSHOPT = { + progress_preference = [[ $ProgressPreference = 'SilentlyContinue'; ]], -- https://stackoverflow.com/a/63301751 + security_protocol = [[ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ]], +} + +---@param script string +---@param opts JobSpawnOpts +---@param custom_spawn JobSpawn +function M.script(script, opts, custom_spawn) + opts = opts or {} + ---@type JobSpawn + local spawner = custom_spawn or spawn + return spawner.powershell(vim.tbl_extend("keep", { + "-NoProfile", + on_spawn = function(_, stdio) + local stdin = stdio[1] + stdin:write(PWSHOPT.progress_preference) + stdin:write(PWSHOPT.security_protocol) + stdin:write(script) + stdin:close() + end, + env = process.graft_env(opts.env or {}, { "PSMODULEPATH" }), + }, opts)) +end + +---@param command string +---@param opts JobSpawnOpts +---@param custom_spawn JobSpawn +function M.command(command, opts, custom_spawn) + opts = opts or {} + ---@type JobSpawn + local spawner = custom_spawn or spawn + return spawner.powershell(vim.tbl_extend("keep", { + "-NoProfile", + "-Command", + PWSHOPT.progress_preference .. PWSHOPT.security_protocol .. command, + env = process.graft_env(opts.env or {}, { "PSMODULEPATH" }), + }, opts)) +end + +return M diff --git a/lua/nvim-lsp-installer/core/managers/std/init.lua b/lua/nvim-lsp-installer/core/managers/std/init.lua new file mode 100644 index 00000000..49a7f53d --- /dev/null +++ b/lua/nvim-lsp-installer/core/managers/std/init.lua @@ -0,0 +1,44 @@ +local a = require "nvim-lsp-installer.core.async" +local installer = require "nvim-lsp-installer.core.installer" + +local M = {} + +local function with_system_executable_receipt(executable) + return function() + local ctx = installer.context() + ctx.receipt:with_primary_source(ctx.receipt.system(executable)) + end +end + +---@async +---@param executable string +---@param opts {help_url:string|nil} +function M.system_executable(executable, opts) + return function() + M.ensure_executable(executable, opts).with_receipt() + end +end + +---@async +---@param executable string +---@param opts {help_url:string|nil} +function M.ensure_executable(executable, opts) + local ctx = installer.context() + opts = opts or {} + if vim.in_fast_event() then + a.scheduler() + end + if vim.fn.executable(executable) ~= 1 then + ctx.stdio_sink.stderr(("%s was not found in path.\n"):format(executable)) + if opts.help_url then + ctx.stdio_sink.stderr(("See %s for installation instructions.\n"):format(opts.help_url)) + end + error("Installation failed: system executable was not found.", 0) + end + + return { + with_receipt = with_system_executable_receipt(executable), + } +end + +return M |
