aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/core
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2022-04-06 00:25:50 +0200
committerGitHub <noreply@github.com>2022-04-06 00:25:50 +0200
commit999476c324b26223aaf409a84497215e18d74499 (patch)
tree323cc5b072471e5e78ee94b273157779bbcb9611 /lua/nvim-lsp-installer/core
parentfix(taplo): use crate distribution (#576) (diff)
downloadmason-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.lua23
-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.lua65
-rw-r--r--lua/nvim-lsp-installer/core/managers/go/init.lua2
-rw-r--r--lua/nvim-lsp-installer/core/managers/pip3/init.lua4
-rw-r--r--lua/nvim-lsp-installer/core/optional.lua4
-rw-r--r--lua/nvim-lsp-installer/core/result.lua16
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)