aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/core
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2022-04-21 12:09:59 +0200
committerGitHub <noreply@github.com>2022-04-21 12:09:59 +0200
commitb68fcc6bb2c770495ff8e2508c06dfdd49abcc80 (patch)
treedf7c71efb59958deb21a18eeccf3e3c43c4cd704 /lua/nvim-lsp-installer/core
parentrun autogen_metadata.lua (diff)
downloadmason-b68fcc6bb2c770495ff8e2508c06dfdd49abcc80.tar
mason-b68fcc6bb2c770495ff8e2508c06dfdd49abcc80.tar.gz
mason-b68fcc6bb2c770495ff8e2508c06dfdd49abcc80.tar.bz2
mason-b68fcc6bb2c770495ff8e2508c06dfdd49abcc80.tar.lz
mason-b68fcc6bb2c770495ff8e2508c06dfdd49abcc80.tar.xz
mason-b68fcc6bb2c770495ff8e2508c06dfdd49abcc80.tar.zst
mason-b68fcc6bb2c770495ff8e2508c06dfdd49abcc80.zip
chore: refactor remaining installers to async impl (#616)
Diffstat (limited to 'lua/nvim-lsp-installer/core')
-rw-r--r--lua/nvim-lsp-installer/core/fetch.lua2
-rw-r--r--lua/nvim-lsp-installer/core/fs.lua15
-rw-r--r--lua/nvim-lsp-installer/core/installer/context.lua42
-rw-r--r--lua/nvim-lsp-installer/core/managers/gem/init.lua38
-rw-r--r--lua/nvim-lsp-installer/core/managers/git/init.lua11
-rw-r--r--lua/nvim-lsp-installer/core/managers/github/client.lua51
-rw-r--r--lua/nvim-lsp-installer/core/managers/github/init.lua89
-rw-r--r--lua/nvim-lsp-installer/core/managers/go/init.lua2
-rw-r--r--lua/nvim-lsp-installer/core/managers/pip3/init.lua6
-rw-r--r--lua/nvim-lsp-installer/core/managers/powershell/init.lua6
-rw-r--r--lua/nvim-lsp-installer/core/managers/std/init.lua124
-rw-r--r--lua/nvim-lsp-installer/core/optional.lua12
-rw-r--r--lua/nvim-lsp-installer/core/result.lua9
-rw-r--r--lua/nvim-lsp-installer/core/spawn.lua20
14 files changed, 369 insertions, 58 deletions
diff --git a/lua/nvim-lsp-installer/core/fetch.lua b/lua/nvim-lsp-installer/core/fetch.lua
index 4c0d1f25..1c876bd1 100644
--- a/lua/nvim-lsp-installer/core/fetch.lua
+++ b/lua/nvim-lsp-installer/core/fetch.lua
@@ -26,7 +26,7 @@ local function fetch(url, opts)
if platform.is_win then
if opts.out_file then
platform_specific = powershell.command(
- ([[iwr %s -UseBasicParsing -Uri %q; -OutFile %q]]):format(HEADERS.iwr, url, opts.out_file)
+ ([[iwr %s -UseBasicParsing -Uri %q -OutFile %q;]]):format(HEADERS.iwr, url, opts.out_file)
)
else
platform_specific = powershell.command(
diff --git a/lua/nvim-lsp-installer/core/fs.lua b/lua/nvim-lsp-installer/core/fs.lua
index b915baf5..5106b40a 100644
--- a/lua/nvim-lsp-installer/core/fs.lua
+++ b/lua/nvim-lsp-installer/core/fs.lua
@@ -1,6 +1,8 @@
local uv = require "nvim-lsp-installer.core.async.uv"
local log = require "nvim-lsp-installer.log"
local a = require "nvim-lsp-installer.core.async"
+local Path = require "nvim-lsp-installer.path"
+local settings = require "nvim-lsp-installer.settings"
local M = {}
@@ -31,6 +33,12 @@ end
---@async
---@param path string
function M.rmrf(path)
+ assert(
+ Path.is_subdirectory(settings.current.install_root_dir, path),
+ (
+ "Refusing to rmrf %q which is outside of the allowed boundary %q. Please report this error at https://github.com/williamboman/nvim-lsp-installer/issues/new"
+ ):format(path, settings.current.install_root_dir)
+ )
log.debug("fs: rmrf", path)
if vim.in_fast_event() then
a.scheduler()
@@ -43,6 +51,13 @@ end
---@async
---@param path string
+function M.unlink(path)
+ log.debug("fs: unlink", path)
+ uv.fs_unlink(path)
+end
+
+---@async
+---@param path string
function M.mkdir(path)
log.debug("fs: mkdir", path)
uv.fs_mkdir(path, 493) -- 493(10) == 755(8)
diff --git a/lua/nvim-lsp-installer/core/installer/context.lua b/lua/nvim-lsp-installer/core/installer/context.lua
index f97f334f..366a3b77 100644
--- a/lua/nvim-lsp-installer/core/installer/context.lua
+++ b/lua/nvim-lsp-installer/core/installer/context.lua
@@ -63,6 +63,31 @@ function ContextualFs:dir_exists(rel_path)
return fs.dir_exists(path.concat { self.cwd:get(), rel_path })
end
+---@async
+---@param rel_path string @The relative path from the current working directory.
+function ContextualFs:rmrf(rel_path)
+ return fs.rmrf(path.concat { self.cwd:get(), rel_path })
+end
+
+---@async
+---@param rel_path string @The relative path from the current working directory.
+function ContextualFs:unlink(rel_path)
+ return fs.unlink(path.concat { self.cwd:get(), rel_path })
+end
+
+---@async
+---@param old_path string
+---@param new_path string
+function ContextualFs:rename(old_path, new_path)
+ return fs.rename(path.concat { self.cwd:get(), old_path }, path.concat { self.cwd:get(), new_path })
+end
+
+---@async
+---@param dirpath string
+function ContextualFs:mkdir(dirpath)
+ return fs.mkdir(path.concat { self.cwd:get(), dirpath })
+end
+
---@class CwdManager
---@field private boundary_path string @Defines the upper boundary for which paths are allowed as cwd.
---@field private cwd string
@@ -101,6 +126,7 @@ end
---@field public cwd CwdManager
---@field public destination_dir string
---@field public stdio_sink StdioSink
+---@field public boundary_path string
local InstallContext = {}
InstallContext.__index = InstallContext
@@ -112,6 +138,7 @@ function InstallContext.new(opts)
spawn = ContextualSpawn.new(cwd_manager, opts.stdio_sink),
fs = ContextualFs.new(cwd_manager),
receipt = receipt.InstallReceiptBuilder.new(),
+ boundary_path = opts.boundary_path,
destination_dir = opts.destination_dir,
requested_version = opts.requested_version,
stdio_sink = opts.stdio_sink,
@@ -154,4 +181,19 @@ function InstallContext:run_concurrently(suspend_fns)
end, suspend_fns))
end
+---@param rel_path string @The relative path from the current working directory to change cwd to. Will only restore to the initial cwd after execution of fn (if provided).
+---@param fn async fun() @(optional) The function to run in the context of the given path.
+function InstallContext:chdir(rel_path, fn)
+ local old_cwd = self.cwd:get()
+ self.cwd:set(path.concat { old_cwd, rel_path })
+ if fn then
+ local ok, result = pcall(fn)
+ self.cwd:set(old_cwd)
+ if not ok then
+ error(result, 0)
+ end
+ return result
+ end
+end
+
return InstallContext
diff --git a/lua/nvim-lsp-installer/core/managers/gem/init.lua b/lua/nvim-lsp-installer/core/managers/gem/init.lua
index 4191d587..c0d03731 100644
--- a/lua/nvim-lsp-installer/core/managers/gem/init.lua
+++ b/lua/nvim-lsp-installer/core/managers/gem/init.lua
@@ -100,27 +100,25 @@ function M.check_outdated_primary_package(receipt, install_dir)
if receipt.primary_source.type ~= "gem" then
return Result.failure "Receipt does not have a primary source of type gem"
end
- return spawn.gem({ "outdated", cwd = install_dir, env = process.graft_env(M.env(install_dir)) }):map_catching(
- function(result)
- ---@type string[]
- local lines = vim.split(result.stdout, "\n")
- local outdated_gems = vim.tbl_map(M.parse_outdated_gem, vim.tbl_filter(not_empty, lines))
+ return spawn.gem({ "outdated", cwd = install_dir, env = M.env(install_dir) }):map_catching(function(result)
+ ---@type string[]
+ local lines = vim.split(result.stdout, "\n")
+ local outdated_gems = vim.tbl_map(M.parse_outdated_gem, vim.tbl_filter(not_empty, lines))
- local outdated_gem = list_find_first(outdated_gems, function(gem)
- return gem.name == receipt.primary_source.package and gem.current_version ~= gem.latest_version
- end)
+ local outdated_gem = list_find_first(outdated_gems, function(gem)
+ return gem.name == receipt.primary_source.package and gem.current_version ~= gem.latest_version
+ end)
- return Optional.of_nilable(outdated_gem)
- :map(function(gem)
- return {
- name = receipt.primary_source.package,
- current_version = assert(gem.current_version),
- latest_version = assert(gem.latest_version),
- }
- end)
- :or_else_throw "Primary package is not outdated."
- end
- )
+ return Optional.of_nilable(outdated_gem)
+ :map(function(gem)
+ return {
+ name = receipt.primary_source.package,
+ current_version = assert(gem.current_version),
+ latest_version = assert(gem.latest_version),
+ }
+ end)
+ :or_else_throw "Primary package is not outdated."
+ end)
end
---@async
@@ -130,7 +128,7 @@ function M.get_installed_primary_package_version(receipt, install_dir)
return spawn.gem({
"list",
cwd = install_dir,
- env = process.graft_env(M.env(install_dir)),
+ env = M.env(install_dir),
}):map_catching(function(result)
local gems = M.parse_gem_list_output(result.stdout)
return Optional.of_nilable(gems[receipt.primary_source.package]):or_else_throw "Failed to find gem package version."
diff --git a/lua/nvim-lsp-installer/core/managers/git/init.lua b/lua/nvim-lsp-installer/core/managers/git/init.lua
index f3c6ee65..6efa10b9 100644
--- a/lua/nvim-lsp-installer/core/managers/git/init.lua
+++ b/lua/nvim-lsp-installer/core/managers/git/init.lua
@@ -13,11 +13,18 @@ local function with_receipt(repo)
end
---@async
----@param opts {[1]:string} @The first item in the table is the repository to clone.
+---@param opts {[1]: string, recursive: boolean} @The first item in the table is the repository to clone.
function M.clone(opts)
local ctx = installer.context()
local repo = assert(opts[1], "No git URL provided.")
- ctx.spawn.git { "clone", "--depth", "1", repo, "." }
+ ctx.spawn.git {
+ "clone",
+ "--depth",
+ "1",
+ opts.recursive and "--recursive" or vim.NIL,
+ repo,
+ ".",
+ }
ctx.requested_version:if_present(function(version)
ctx.spawn.git { "fetch", "--depth", "1", "origin", version }
ctx.spawn.git { "checkout", "FETCH_HEAD" }
diff --git a/lua/nvim-lsp-installer/core/managers/github/client.lua b/lua/nvim-lsp-installer/core/managers/github/client.lua
index a8766748..7cf80cf2 100644
--- a/lua/nvim-lsp-installer/core/managers/github/client.lua
+++ b/lua/nvim-lsp-installer/core/managers/github/client.lua
@@ -7,14 +7,13 @@ local list_find_first = Data.list_find_first
local M = {}
----@alias GitHubRelease {tag_name:string, prerelease: boolean, draft: boolean}
+---@alias GitHubReleaseAsset {url: string, id: integer, name: string, browser_download_url: string, created_at: string, updated_at: string, size: integer, download_count: integer}
+---@alias GitHubRelease {tag_name: string, prerelease: boolean, draft: boolean, assets:GitHubReleaseAsset[]}
---@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)
+---@param path string
+---@return Result @JSON decoded response.
+local function api_call(path)
return spawn.gh({ "api", path })
:map(function(result)
return result.stdout
@@ -25,10 +24,31 @@ function M.fetch_releases(repo)
:map_catching(vim.json.decode)
end
+---@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 api_call(path):map_err(function()
+ return ("Failed to fetch releases for GitHub repository %s."):format(repo)
+ end)
+end
+
+---@async
+---@param repo string @The GitHub repo ("username/repo").
+---@param tag_name string @The tag_name of the release to fetch.
+function M.fetch_release(repo, tag_name)
+ log.fmt_trace("Fetching GitHub release for repo=%s, tag_name=%s", repo, tag_name)
+ local path = ("repos/%s/releases/tags/%s"):format(repo, tag_name)
+ return api_call(path):map_err(function()
+ return ("Failed to fetch release %q for GitHub repository %s."):format(tag_name, repo)
+ end)
+end
+
---@alias FetchLatestGithubReleaseOpts {tag_name_pattern:string}
---@async
----@param repo string The GitHub repo ("username/repo").
+---@param repo string @The GitHub repo ("username/repo").
---@param opts FetchLatestGithubReleaseOpts|nil
---@return Result @of GitHubRelease
function M.fetch_latest_release(repo, opts)
@@ -61,21 +81,18 @@ function M.fetch_latest_release(repo, opts)
end
---@async
----@param repo string The GitHub repo ("username/repo").
+---@param repo string @The GitHub repo ("username/repo").
+---@return Result @of [GitHubTag[]]
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)
+ return api_call(path):map_err(function()
+ return ("Failed to fetch tags for GitHub repository %s."):format(repo)
+ end)
end
---@async
----@param repo string The GitHub repo ("username/repo").
+---@param repo string @The GitHub repo ("username/repo").
+---@return Result @of [GitHubTag]
function M.fetch_latest_tag(repo)
return M.fetch_tags(repo):map_catching(function(tags)
if vim.tbl_count(tags) == 0 then
diff --git a/lua/nvim-lsp-installer/core/managers/github/init.lua b/lua/nvim-lsp-installer/core/managers/github/init.lua
new file mode 100644
index 00000000..32afc03c
--- /dev/null
+++ b/lua/nvim-lsp-installer/core/managers/github/init.lua
@@ -0,0 +1,89 @@
+local installer = require "nvim-lsp-installer.core.installer"
+local std = require "nvim-lsp-installer.core.managers.std"
+local client = require "nvim-lsp-installer.core.managers.github.client"
+local platform = require "nvim-lsp-installer.platform"
+
+local M = {}
+
+---@param repo string
+---@param asset_file string
+---@param release string
+local function with_release_file_receipt(repo, asset_file, release)
+ return function()
+ local ctx = installer.context()
+ ctx.receipt:with_primary_source {
+ type = "github_release_file",
+ repo = repo,
+ file = asset_file,
+ release = release,
+ }
+ end
+end
+
+---@async
+---@param opts {repo: string, version: Optional|nil, asset_file: string|fun(release: string):string}
+function M.release_file(opts)
+ local ctx = installer.context()
+ local release = (opts.version or ctx.requested_version):or_else_get(function()
+ return client.fetch_latest_release(opts.repo)
+ :map(function(release)
+ return release.tag_name
+ end)
+ :get_or_throw "Failed to fetch latest release from GitHub API."
+ end)
+ ---@type string
+ local asset_file = type(opts.asset_file) == "function" and opts.asset_file(release) or opts.asset_file
+ if not asset_file then
+ error(
+ (
+ "Could not find which release file to download. Most likely the current operating system, architecture (%s), or libc (%s) is not supported."
+ ):format(platform.arch, platform.get_libc())
+ )
+ end
+ local download_url = ("https://github.com/%s/releases/download/%s/%s"):format(opts.repo, release, asset_file)
+ return {
+ release = release,
+ download_url = download_url,
+ asset_file = asset_file,
+ with_receipt = with_release_file_receipt(opts.repo, download_url, release),
+ }
+end
+
+---@param filename string
+---@param processor async fun()
+local function release_file_processor(filename, processor)
+ ---@async
+ ---@param opts {repo: string, asset_file: string|fun(release: string):string}
+ return function(opts)
+ local release_file_source = M.release_file(opts)
+ std.download_file(release_file_source.download_url, filename)
+ processor(release_file_source)
+ return release_file_source
+ end
+end
+
+M.unzip_release_file = release_file_processor("archive.zip", function()
+ std.unzip("archive.zip", ".")
+end)
+
+M.untarxz_release_file = release_file_processor("archive.tar.xz", function()
+ std.untarxz "archive.tar.xz"
+end)
+
+M.untargz_release_file = release_file_processor("archive.tar.gz", function()
+ std.untar "archive.tar.gz"
+end)
+
+---@async
+---@param opts {repo: string, out_file:string, asset_file: string|fun(release: string):string}
+function M.gunzip_release_file(opts)
+ local release_file_source = M.release_file(opts)
+ std.download_file(
+ release_file_source.download_url,
+ ("%s.gz"):format(assert(opts.out_file, "out_file must be specified"))
+ )
+ std.gunzip(opts.out_file)
+ return release_file_source
+end
+
+return M
diff --git a/lua/nvim-lsp-installer/core/managers/go/init.lua b/lua/nvim-lsp-installer/core/managers/go/init.lua
index 99be5618..f85987ef 100644
--- a/lua/nvim-lsp-installer/core/managers/go/init.lua
+++ b/lua/nvim-lsp-installer/core/managers/go/init.lua
@@ -32,7 +32,7 @@ end
---@param packages string[] The Go packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
function M.install(packages)
local ctx = installer.context()
- local env = process.graft_env {
+ local env = {
GOBIN = ctx.cwd:get(),
}
-- Install the head package
diff --git a/lua/nvim-lsp-installer/core/managers/pip3/init.lua b/lua/nvim-lsp-installer/core/managers/pip3/init.lua
index ab2ce221..d55c4ac0 100644
--- a/lua/nvim-lsp-installer/core/managers/pip3/init.lua
+++ b/lua/nvim-lsp-installer/core/managers/pip3/init.lua
@@ -56,13 +56,13 @@ function M.install(packages)
Optional.of_nilable(executable)
:if_present(function()
ctx.spawn.python {
- env = process.graft_env(M.env(ctx.cwd:get())), -- use venv env
"-m",
"pip",
"install",
"-U",
settings.current.pip.install_args,
pkgs,
+ env = M.env(ctx.cwd:get()), -- use venv env
}
end)
:or_else_throw "Unable to create python3 venv environment."
@@ -95,7 +95,7 @@ function M.check_outdated_primary_package(receipt, install_dir)
"--outdated",
"--format=json",
cwd = install_dir,
- env = process.graft_env(M.env(install_dir)), -- use venv
+ env = M.env(install_dir), -- use venv
}):map_catching(function(result)
---@alias PipOutdatedPackage {name: string, version: string, latest_version: string}
---@type PipOutdatedPackage[]
@@ -131,7 +131,7 @@ function M.get_installed_primary_package_version(receipt, install_dir)
"list",
"--format=json",
cwd = install_dir,
- env = process.graft_env(M.env(install_dir)), -- use venv env
+ env = M.env(install_dir), -- use venv env
}):map_catching(function(result)
local pip_packages = vim.json.decode(result.stdout)
local normalized_pip_package = M.normalize_package(receipt.primary_source.package)
diff --git a/lua/nvim-lsp-installer/core/managers/powershell/init.lua b/lua/nvim-lsp-installer/core/managers/powershell/init.lua
index 94046b05..97e51b3e 100644
--- a/lua/nvim-lsp-installer/core/managers/powershell/init.lua
+++ b/lua/nvim-lsp-installer/core/managers/powershell/init.lua
@@ -11,7 +11,6 @@ local PWSHOPT = {
---@param script string
---@param opts JobSpawnOpts
---@param custom_spawn JobSpawn
----@return Result
function M.script(script, opts, custom_spawn)
opts = opts or {}
---@type JobSpawn
@@ -25,14 +24,13 @@ function M.script(script, opts, custom_spawn)
stdin:write(script)
stdin:close()
end,
- env = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
+ env_raw = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
}, opts))
end
---@param command string
---@param opts JobSpawnOpts
---@param custom_spawn JobSpawn
----@return Result
function M.command(command, opts, custom_spawn)
opts = opts or {}
---@type JobSpawn
@@ -41,7 +39,7 @@ function M.command(command, opts, custom_spawn)
"-NoProfile",
"-Command",
PWSHOPT.progress_preference .. PWSHOPT.security_protocol .. command,
- env = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
+ env_raw = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
}, opts))
end
diff --git a/lua/nvim-lsp-installer/core/managers/std/init.lua b/lua/nvim-lsp-installer/core/managers/std/init.lua
index 49a7f53d..05495573 100644
--- a/lua/nvim-lsp-installer/core/managers/std/init.lua
+++ b/lua/nvim-lsp-installer/core/managers/std/init.lua
@@ -1,5 +1,10 @@
local a = require "nvim-lsp-installer.core.async"
local installer = require "nvim-lsp-installer.core.installer"
+local fetch = require "nvim-lsp-installer.core.fetch"
+local platform = require "nvim-lsp-installer.platform"
+local powershell = require "nvim-lsp-installer.core.managers.powershell"
+local path = require "nvim-lsp-installer.path"
+local Result = require "nvim-lsp-installer.core.result"
local M = {}
@@ -41,4 +46,123 @@ function M.ensure_executable(executable, opts)
}
end
+---@async
+---@param url string
+---@param out_file string
+function M.download_file(url, out_file)
+ local ctx = installer.context()
+ ctx.stdio_sink.stdout(("Downloading file %q...\n"):format(url))
+ fetch(url, {
+ out_file = path.concat { ctx.cwd:get(), out_file },
+ })
+ :map_err(function(err)
+ return ("Failed to download file %q.\n%s"):format(url, err)
+ end)
+ :get_or_throw()
+end
+
+---@async
+---@param file string
+---@param dest string
+function M.unzip(file, dest)
+ local ctx = installer.context()
+ platform.when {
+ unix = function()
+ ctx.spawn.unzip { "-d", dest, file }
+ end,
+ win = function()
+ powershell.command(
+ ("Microsoft.PowerShell.Archive\\Expand-Archive -Path %q -DestinationPath %q"):format(file, dest),
+ {},
+ ctx.spawn
+ )
+ end,
+ }
+ pcall(function()
+ -- make sure the .zip archive doesn't linger
+ ctx.fs:unlink(file)
+ end)
+end
+
+---@param file string
+local function win_extract(file)
+ local ctx = installer.context()
+ Result.run_catching(function()
+ ctx.spawn["7z"] { "x", "-y", "-r", file }
+ end)
+ :recover_catching(function()
+ ctx.spawn.peazip { "-ext2here", path.concat { ctx.cwd:get(), file } } -- peazip requires absolute paths
+ end)
+ :recover_catching(function()
+ ctx.spawn.wzunzip { file }
+ end)
+ :recover_catching(function()
+ ctx.spawn.winrar { "e", file }
+ end)
+ :get_or_throw(("Unable to unpack %s."):format(file))
+end
+
+---@async
+---@param file string
+---@param opts {strip_components:integer}
+function M.untar(file, opts)
+ opts = opts or {}
+ local ctx = installer.context()
+ ctx.spawn.tar {
+ opts.strip_components and { "--strip-components", opts.strip_components } or vim.NIL,
+ "-xvf",
+ file,
+ }
+ pcall(function()
+ ctx.fs:unlink(file)
+ end)
+end
+
+---@async
+---@param file string
+function M.untarxz(file)
+ local ctx = installer.context()
+ platform.when {
+ unix = function()
+ M.untar(file)
+ end,
+ win = function()
+ Result.run_catching(function()
+ win_extract(file) -- unpack .tar.xz to .tar
+ local uncompressed_tar = file:gsub(".xz$", "")
+ M.untar(uncompressed_tar)
+ end):recover(function()
+ ctx.spawn.arc { "unarchive", file }
+ pcall(function()
+ ctx.fs:unlink(file)
+ end)
+ end)
+ end,
+ }
+end
+
+---@async
+---@param file string
+function M.gunzip(file)
+ platform.when {
+ unix = function()
+ local ctx = installer.context()
+ ctx.spawn.gzip { "-d", file }
+ end,
+ win = function()
+ win_extract(file)
+ end,
+ }
+end
+
+---@async
+---@param flags string @The chmod flag to apply.
+---@param files string[] @A list of relative paths to apply the chmod on.
+function M.chmod(flags, files)
+ if platform.is_unix then
+ local ctx = installer.context()
+ ctx.spawn.chmod { flags, files }
+ end
+end
+
return M
diff --git a/lua/nvim-lsp-installer/core/optional.lua b/lua/nvim-lsp-installer/core/optional.lua
index 8e68648c..10af8ccb 100644
--- a/lua/nvim-lsp-installer/core/optional.lua
+++ b/lua/nvim-lsp-installer/core/optional.lua
@@ -1,4 +1,4 @@
----@class Optional
+---@class Optional<T>
---@field private _value unknown
local Optional = {}
Optional.__index = Optional
@@ -53,7 +53,17 @@ function Optional:or_else(value)
end
end
+---@param supplier fun(): any
+function Optional:or_else_get(supplier)
+ if self:is_present() then
+ return self._value
+ else
+ return supplier()
+ end
+end
+
---@param supplier fun(): Optional
+---@return Optional
function Optional:or_(supplier)
if self:is_present() then
return self
diff --git a/lua/nvim-lsp-installer/core/result.lua b/lua/nvim-lsp-installer/core/result.lua
index 74fb9bc8..132e2758 100644
--- a/lua/nvim-lsp-installer/core/result.lua
+++ b/lua/nvim-lsp-installer/core/result.lua
@@ -77,6 +77,15 @@ function Result:map(mapper_fn)
end
---@param mapper_fn fun(value: any): any
+function Result:map_err(mapper_fn)
+ if self:is_failure() then
+ return Result.failure(mapper_fn(self.value.error))
+ else
+ return self
+ end
+end
+
+---@param mapper_fn fun(value: any): any
function Result:map_catching(mapper_fn)
if self:is_success() then
local ok, result = pcall(mapper_fn, self.value)
diff --git a/lua/nvim-lsp-installer/core/spawn.lua b/lua/nvim-lsp-installer/core/spawn.lua
index 355df029..8b849f64 100644
--- a/lua/nvim-lsp-installer/core/spawn.lua
+++ b/lua/nvim-lsp-installer/core/spawn.lua
@@ -10,15 +10,8 @@ local spawn = {
npm = platform.is_win and "npm.cmd" or "npm",
gem = platform.is_win and "gem.cmd" or "gem",
composer = platform.is_win and "composer.bat" or "composer",
+ gradlew = platform.is_win and "gradlew.bat" or "gradlew",
},
- -- Utility function for optionally including arguments.
- ---@generic T
- ---@param condition boolean
- ---@param value T
- ---@return T
- _when = function(condition, value)
- return condition and value or vim.NIL
- end,
}
local function Failure(err, cmd)
@@ -50,11 +43,20 @@ setmetatable(spawn, {
return function(args)
local cmd_args = {}
parse_args(args, cmd_args)
+
+ ---@type table<string, string>
+ local env = args.env
+
+ if args.with_paths then
+ env = env or {}
+ env.PATH = process.extend_path(args.with_paths)
+ end
+
---@type JobSpawnOpts
local spawn_args = {
stdio_sink = args.stdio_sink,
cwd = args.cwd,
- env = args.env,
+ env = env and process.graft_env(env) or args.env_raw,
args = cmd_args,
}