diff options
| author | William Boman <william@redwill.se> | 2022-04-21 12:09:59 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-04-21 12:09:59 +0200 |
| commit | b68fcc6bb2c770495ff8e2508c06dfdd49abcc80 (patch) | |
| tree | df7c71efb59958deb21a18eeccf3e3c43c4cd704 /lua/nvim-lsp-installer/core | |
| parent | run autogen_metadata.lua (diff) | |
| download | mason-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.lua | 2 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/fs.lua | 15 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/installer/context.lua | 42 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/gem/init.lua | 38 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/git/init.lua | 11 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/github/client.lua | 51 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/github/init.lua | 89 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/go/init.lua | 2 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/pip3/init.lua | 6 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/powershell/init.lua | 6 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/managers/std/init.lua | 124 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/optional.lua | 12 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/result.lua | 9 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/spawn.lua | 20 |
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, } |
