aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/core
diff options
context:
space:
mode:
Diffstat (limited to 'lua/nvim-lsp-installer/core')
-rw-r--r--lua/nvim-lsp-installer/core/async/uv.lua2
-rw-r--r--lua/nvim-lsp-installer/core/clients/crates.lua23
-rw-r--r--lua/nvim-lsp-installer/core/clients/eclipse.lua12
-rw-r--r--lua/nvim-lsp-installer/core/clients/github.lua92
-rw-r--r--lua/nvim-lsp-installer/core/fetch.lua98
-rw-r--r--lua/nvim-lsp-installer/core/installer/context.lua3
-rw-r--r--lua/nvim-lsp-installer/core/managers/cargo/client.lua14
-rw-r--r--lua/nvim-lsp-installer/core/managers/cargo/init.lua32
-rw-r--r--lua/nvim-lsp-installer/core/managers/gem/init.lua4
-rw-r--r--lua/nvim-lsp-installer/core/managers/github/client.lua88
-rw-r--r--lua/nvim-lsp-installer/core/managers/npm/init.lua2
-rw-r--r--lua/nvim-lsp-installer/core/managers/powershell/init.lua46
-rw-r--r--lua/nvim-lsp-installer/core/managers/std/init.lua44
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