diff options
| author | William Boman <william@redwill.se> | 2022-04-06 00:25:50 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-04-06 00:25:50 +0200 |
| commit | 999476c324b26223aaf409a84497215e18d74499 (patch) | |
| tree | 323cc5b072471e5e78ee94b273157779bbcb9611 /lua/nvim-lsp-installer/core | |
| parent | fix(taplo): use crate distribution (#576) (diff) | |
| download | mason-999476c324b26223aaf409a84497215e18d74499.tar mason-999476c324b26223aaf409a84497215e18d74499.tar.gz mason-999476c324b26223aaf409a84497215e18d74499.tar.bz2 mason-999476c324b26223aaf409a84497215e18d74499.tar.lz mason-999476c324b26223aaf409a84497215e18d74499.tar.xz mason-999476c324b26223aaf409a84497215e18d74499.tar.zst mason-999476c324b26223aaf409a84497215e18d74499.zip | |
switch majority of installers to async implementation (#574)
Diffstat (limited to 'lua/nvim-lsp-installer/core')
| -rw-r--r-- | lua/nvim-lsp-installer/core/fs.lua | 23 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/installer/context.lua (renamed from lua/nvim-lsp-installer/core/context.lua) | 121 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/installer/init.lua | 65 | ||||
| -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 | 4 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/optional.lua | 4 | ||||
| -rw-r--r-- | lua/nvim-lsp-installer/core/result.lua | 16 |
7 files changed, 153 insertions, 82 deletions
diff --git a/lua/nvim-lsp-installer/core/fs.lua b/lua/nvim-lsp-installer/core/fs.lua index f3db813a..3819bd51 100644 --- a/lua/nvim-lsp-installer/core/fs.lua +++ b/lua/nvim-lsp-installer/core/fs.lua @@ -59,10 +59,33 @@ end ---@async ---@param path string +function M.mkdirp(path) + log.debug("fs: mkdirp", path) + if vim.in_fast_event() then + a.scheduler() + end + if vim.fn.mkdir(path, "p") ~= 1 then + log.debug "fs: mkdirp failed" + error(("mkdirp: Could not create directory %q."):format(path)) + end +end + +---@async +---@param path string ---@param new_path string function M.rename(path, new_path) log.debug("fs: rename", path, new_path) uv.fs_rename(path, new_path) end +---@async +---@param path string +---@param contents string +function M.write_file(path, contents) + log.fmt_debug("fs: write_file %s", path) + local fd = assert(uv.fs_open(path, "w", 438)) + uv.fs_write(fd, contents, -1) + assert(uv.fs_close(fd)) +end + return M diff --git a/lua/nvim-lsp-installer/core/context.lua b/lua/nvim-lsp-installer/core/installer/context.lua index 128ad417..ad956178 100644 --- a/lua/nvim-lsp-installer/core/context.lua +++ b/lua/nvim-lsp-installer/core/installer/context.lua @@ -1,11 +1,9 @@ local spawn = require "nvim-lsp-installer.core.spawn" local log = require "nvim-lsp-installer.log" -local Optional = require "nvim-lsp-installer.core.optional" local fs = require "nvim-lsp-installer.core.fs" -local settings = require "nvim-lsp-installer.settings" -local Result = require "nvim-lsp-installer.core.result" local path = require "nvim-lsp-installer.path" local platform = require "nvim-lsp-installer.platform" +local receipt = require "nvim-lsp-installer.core.receipt" ---@class ContextualSpawn ---@field cwd CwdManager @@ -57,111 +55,80 @@ function ContextualFs:dir_exists(rel_path) end ---@class CwdManager +---@field private boundary_path string @Defines the upper boundary for which paths are allowed as cwd. +---@field private cwd string local CwdManager = {} CwdManager.__index = CwdManager -function CwdManager.new(cwd) - return setmetatable({ cwd = cwd }, CwdManager) + +function CwdManager.new(boundary_path, cwd) + assert(type(boundary_path) == "string") + return setmetatable({ + boundary_path = boundary_path, + cwd = cwd, + }, CwdManager) end + function CwdManager:get() - return self.cwd + return assert(self.cwd, "Tried to access cwd before it was set.") end + +---@param new_cwd string function CwdManager:set(new_cwd) + assert(type(new_cwd) == "string") + assert( + path.is_subdirectory(self.boundary_path, new_cwd), + ("%q is not a subdirectory of %q"):format(new_cwd, self.boundary_path) + ) self.cwd = new_cwd end ---@class InstallContext +---@field public name string ---@field public receipt InstallReceiptBuilder ---@field public requested_version Optional ---@field public fs ContextualFs ---@field public spawn JobSpawn ----@field private cwd_manager CwdManager ----@field private destination_dir string +---@field public cwd CwdManager +---@field public destination_dir string +---@field public stdio_sink StdioSink local InstallContext = {} InstallContext.__index = InstallContext function InstallContext.new(opts) - local cwd_manager = CwdManager.new(opts.cwd) + local cwd_manager = CwdManager.new(opts.boundary_path) return setmetatable({ - cwd_manager = cwd_manager, + name = opts.name, + cwd = cwd_manager, spawn = ContextualSpawn.new(cwd_manager, opts.stdio_sink), fs = ContextualFs.new(cwd_manager), - receipt = opts.receipt, - requested_version = opts.requested_version, + receipt = receipt.InstallReceiptBuilder.new(), destination_dir = opts.destination_dir, + requested_version = opts.requested_version, + stdio_sink = opts.stdio_sink, }, InstallContext) end ----@deprecated ----@param ctx ServerInstallContext ----@param destination_dir string -function InstallContext.from_server_context(ctx, destination_dir) - return InstallContext.new { - cwd = ctx.install_dir, - receipt = ctx.receipt, - stdio_sink = ctx.stdio_sink, - requested_version = Optional.of_nilable(ctx.requested_server_version), - destination_dir = destination_dir, - } -end - -function InstallContext:cwd() - return self.cwd_manager:get() -end - ----@param new_cwd string @The new cwd (absolute path). -function InstallContext:set_cwd(new_cwd) - self - :ensure_path_ownership(new_cwd) - :map(function(p) - self.cwd_manager:set(p) - return p - end) - :get_or_throw() -end - ----@param abs_path string -function InstallContext:ensure_path_ownership(abs_path) - if path.is_subdirectory(self:cwd_manager(), abs_path) or self.destination_dir == abs_path then - return Result.success(abs_path) - else - return Result.failure( - ("Path %q is outside of current path ownership (%q)."):format(abs_path, settings.current.install_root_dir) - ) - end -end - ---@async function InstallContext:promote_cwd() - local cwd = self:cwd() + local cwd = self.cwd:get() if self.destination_dir == cwd then log.fmt_debug("cwd %s is already promoted (at %s)", cwd, self.destination_dir) - return Result.success "Current working dir is already in destination." + return end log.fmt_debug("Promoting cwd %s to %s", cwd, self.destination_dir) - return Result.run_catching(function() - -- 1. Remove destination dir, if it exists - if fs.dir_exists(self.destination_dir) then - fs.rmrf(self.destination_dir) - end - return self.destination_dir - end) - :map_catching(function(destination_dir) - -- 2. Prepare for renaming cwd to destination - if platform.is_unix then - -- Some Unix systems will raise an error when renaming a directory to a destination that does not already exist. - fs.mkdir(destination_dir) - end - return destination_dir - end) - :map_catching(function(destination_dir) - -- 3. Move the cwd to the final installation directory - fs.rename(cwd, destination_dir) - return destination_dir - end) - :map_catching(function(destination_dir) - -- 4. Update cwd - self:set_cwd(destination_dir) - end) + -- 1. Remove destination dir, if it exists + if fs.dir_exists(self.destination_dir) then + fs.rmrf(self.destination_dir) + end + -- 2. Prepare for renaming cwd to destination + if platform.is_unix then + -- Some Unix systems will raise an error when renaming a directory to a destination that does not already exist. + fs.mkdir(self.destination_dir) + end + -- 3. Move the cwd to the final installation directory + fs.rename(cwd, self.destination_dir) + -- 4. Update cwd + self.cwd:set(self.destination_dir) end return InstallContext diff --git a/lua/nvim-lsp-installer/core/installer/init.lua b/lua/nvim-lsp-installer/core/installer/init.lua new file mode 100644 index 00000000..03b2de01 --- /dev/null +++ b/lua/nvim-lsp-installer/core/installer/init.lua @@ -0,0 +1,65 @@ +local log = require "nvim-lsp-installer.log" +local path = require "nvim-lsp-installer.path" +local fs = require "nvim-lsp-installer.core.fs" +local Result = require "nvim-lsp-installer.core.result" + +local M = {} + +---@async +---@param context InstallContext +local function write_receipt(context) + if context.receipt.is_marked_invalid then + return log.fmt_debug("Skipping writing receipt for %s because it is marked as invalid.", context.name) + end + context.receipt:with_name(context.name):with_schema_version("1.0a"):with_completion_time(vim.loop.gettimeofday()) + local receipt_success, install_receipt = pcall(context.receipt.build, context.receipt) + if receipt_success then + local receipt_path = path.concat { context.cwd:get(), "nvim-lsp-installer-receipt.json" } + pcall(fs.write_file, receipt_path, vim.json.encode(install_receipt)) + else + log.fmt_error("Failed to build receipt for installation=%s, error=%s", context.name, install_receipt) + end +end + +---@async +---@param context InstallContext +---@param installer async fun(ctx: InstallContext) +function M.execute(context, installer) + log.fmt_debug("Executing installer for name=%s", context.name) + local tmp_installation_dir = ("%s.tmp"):format(context.destination_dir) + return Result.run_catching(function() + -- 1. prepare installation dir + context.receipt:with_start_time(vim.loop.gettimeofday()) + if fs.dir_exists(tmp_installation_dir) then + fs.rmrf(tmp_installation_dir) + end + fs.mkdirp(tmp_installation_dir) + context.cwd:set(tmp_installation_dir) + + -- 2. run installer + installer(context) + + -- 3. finalize + write_receipt(context) + context:promote_cwd() + end):on_failure(function(failure) + context.stdio_sink.stderr(tostring(failure)) + context.stdio_sink.stderr "\n" + log.fmt_error("Installation failed, name=%s, error=%s", context.name, failure) + pcall(fs.rmrf, tmp_installation_dir) + pcall(fs.rmrf, context.cwd:get()) + end) +end + +---@param installers async fun(ctx: InstallContext)[] +function M.serial(installers) + ---@async + ---@param ctx InstallContext + return function(ctx) + for _, installer_step in pairs(installers) do + installer_step(ctx) + end + end +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 96706368..d73a250c 100644 --- a/lua/nvim-lsp-installer/core/managers/go/init.lua +++ b/lua/nvim-lsp-installer/core/managers/go/init.lua @@ -12,7 +12,7 @@ function M.packages(packages) ---@param ctx InstallContext return function(ctx) local env = process.graft_env { - GOBIN = ctx.cwd(), + GOBIN = ctx.cwd:get(), } -- Install the head package do diff --git a/lua/nvim-lsp-installer/core/managers/pip3/init.lua b/lua/nvim-lsp-installer/core/managers/pip3/init.lua index 61130a96..06408f3b 100644 --- a/lua/nvim-lsp-installer/core/managers/pip3/init.lua +++ b/lua/nvim-lsp-installer/core/managers/pip3/init.lua @@ -32,7 +32,7 @@ function M.packages(packages) or list_not_nil(vim.g.python3_host_prog, "python3", "python") -- pip3 will hardcode the full path to venv executables, so we need to promote cwd to make sure pip uses the final destination path. - ctx:promote_cwd():get_or_throw() + ctx:promote_cwd() -- Find first executable that manages to create venv local executable = list_find_first(executables, function(executable) @@ -42,7 +42,7 @@ function M.packages(packages) Optional.of_nilable(executable) :if_present(function(python3) ctx.spawn[python3] { - env = process.graft_env(M.env(ctx:cwd())), -- use venv env + env = process.graft_env(M.env(ctx.cwd:get())), -- use venv env "-m", "pip", "install", diff --git a/lua/nvim-lsp-installer/core/optional.lua b/lua/nvim-lsp-installer/core/optional.lua index d37a7e69..8e68648c 100644 --- a/lua/nvim-lsp-installer/core/optional.lua +++ b/lua/nvim-lsp-installer/core/optional.lua @@ -54,9 +54,9 @@ function Optional:or_else(value) end ---@param supplier fun(): Optional -function Optional:when_empty(supplier) +function Optional:or_(supplier) if self:is_present() then - return self._value + return self else return supplier() end diff --git a/lua/nvim-lsp-installer/core/result.lua b/lua/nvim-lsp-installer/core/result.lua index f5c2d747..74fb9bc8 100644 --- a/lua/nvim-lsp-installer/core/result.lua +++ b/lua/nvim-lsp-installer/core/result.lua @@ -113,6 +113,22 @@ function Result:recover_catching(recover_fn) end end +---@param fn fun(value: any): any +function Result:on_failure(fn) + if self:is_failure() then + fn(self.value.error) + end + return self +end + +---@param fn fun(value: any): any +function Result:on_success(fn) + if self:is_success() then + fn(self.value) + end + return self +end + ---@param fn fun(): any ---@return Result function Result.run_catching(fn) |
