aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2023-10-13 01:53:41 +0200
committerWilliam Boman <william@redwill.se>2025-02-19 09:23:19 +0100
commitf1e58d3ce7ab3bdb3036b791811896a0220703ad (patch)
treece44529583dcf72844b206fe8578f0ada5ef153f
parentrefactor(installer): move initializations to InstallContext constructor (diff)
downloadmason-f1e58d3ce7ab3bdb3036b791811896a0220703ad.tar
mason-f1e58d3ce7ab3bdb3036b791811896a0220703ad.tar.gz
mason-f1e58d3ce7ab3bdb3036b791811896a0220703ad.tar.bz2
mason-f1e58d3ce7ab3bdb3036b791811896a0220703ad.tar.lz
mason-f1e58d3ce7ab3bdb3036b791811896a0220703ad.tar.xz
mason-f1e58d3ce7ab3bdb3036b791811896a0220703ad.tar.zst
mason-f1e58d3ce7ab3bdb3036b791811896a0220703ad.zip
refactor(path): use InstallLocation to produce paths, remove static path methods
-rw-r--r--lua/mason-core/installer/linker.lua45
-rw-r--r--lua/mason-core/installer/location.lua27
-rw-r--r--lua/mason-core/installer/runner.lua2
-rw-r--r--lua/mason-core/package.lua7
-rw-r--r--lua/mason-core/path.lua40
-rw-r--r--lua/mason-registry/init.lua11
-rw-r--r--lua/mason-registry/sources/github.lua3
-rw-r--r--lua/mason/init.lua10
-rw-r--r--tests/mason-core/installer/linker_spec.lua60
-rw-r--r--tests/mason/setup_spec.lua14
10 files changed, 115 insertions, 104 deletions
diff --git a/lua/mason-core/installer/linker.lua b/lua/mason-core/installer/linker.lua
index 83b1f4a5..5ab6044a 100644
--- a/lua/mason-core/installer/linker.lua
+++ b/lua/mason-core/installer/linker.lua
@@ -8,18 +8,40 @@ local platform = require "mason-core.platform"
local M = {}
----@alias LinkContext { type: '"bin"' | '"opt"' | '"share"', prefix: fun(path: string): string }
+---@alias LinkContext { type: '"bin"' | '"opt"' | '"share"', prefix: fun(path: string, location: InstallLocation): string }
---@type table<'"BIN"' | '"OPT"' | '"SHARE"', LinkContext>
local LinkContext = {
- BIN = { type = "bin", prefix = path.bin_prefix },
- OPT = { type = "opt", prefix = path.opt_prefix },
- SHARE = { type = "share", prefix = path.share_prefix },
+ BIN = {
+ type = "bin",
+ ---@param path string
+ ---@param location InstallLocation
+ prefix = function(path, location)
+ return location:bin(path)
+ end,
+ },
+ OPT = {
+ type = "opt",
+ ---@param path string
+ ---@param location InstallLocation
+ prefix = function(path, location)
+ return location:opt(path)
+ end,
+ },
+ SHARE = {
+ type = "share",
+ ---@param path string
+ ---@param location InstallLocation
+ prefix = function(path, location)
+ return location:share(path)
+ end,
+ },
}
---@param receipt InstallReceipt
---@param link_context LinkContext
-local function unlink(receipt, link_context)
+---@param location InstallLocation
+local function unlink(receipt, link_context, location)
return Result.pcall(function()
local links = receipt:get_links()[link_context.type]
if not links then
@@ -29,7 +51,7 @@ local function unlink(receipt, link_context)
if receipt:get_schema_version() == "1.0" and link_context == LinkContext.BIN and platform.is.win then
linked_file = linked_file .. ".cmd"
end
- local share_path = link_context.prefix(linked_file)
+ local share_path = link_context.prefix(linked_file, location)
fs.sync.unlink(share_path)
end
end)
@@ -37,13 +59,14 @@ end
---@param pkg Package
---@param receipt InstallReceipt
+---@param location InstallLocation
---@nodiscard
-function M.unlink(pkg, receipt)
+function M.unlink(pkg, receipt, location)
log.fmt_debug("Unlinking %s", pkg, receipt:get_links())
return Result.try(function(try)
- try(unlink(receipt, LinkContext.BIN))
- try(unlink(receipt, LinkContext.SHARE))
- try(unlink(receipt, LinkContext.OPT))
+ try(unlink(receipt, LinkContext.BIN, location))
+ try(unlink(receipt, LinkContext.SHARE, location))
+ try(unlink(receipt, LinkContext.OPT, location))
end)
end
@@ -58,7 +81,7 @@ local function link(context, link_context, link_fn)
if platform.is.win and link_context == LinkContext.BIN then
name = ("%s.cmd"):format(name)
end
- local new_abs_path = link_context.prefix(name)
+ local new_abs_path = link_context.prefix(name, context.location)
local target_abs_path = path.concat { context.package:get_install_path(), rel_path }
do
diff --git a/lua/mason-core/installer/location.lua b/lua/mason-core/installer/location.lua
index 694ae0f4..a77e2915 100644
--- a/lua/mason-core/installer/location.lua
+++ b/lua/mason-core/installer/location.lua
@@ -1,6 +1,5 @@
local Path = require "mason-core.path"
-local Result = require "mason-core.result"
-local fs = require "mason-core.fs"
+local platform = require "mason-core.platform"
local settings = require "mason.settings"
---@class InstallLocation
@@ -25,6 +24,9 @@ end
---@async
function InstallLocation:initialize()
+ local Result = require "mason-core.result"
+ local fs = require "mason-core.fs"
+
return Result.try(function(try)
for _, p in ipairs {
self.dir,
@@ -51,6 +53,11 @@ function InstallLocation:share(path)
end
---@param path string?
+function InstallLocation:opt(path)
+ return Path.concat { self.dir, "opt", path }
+end
+
+---@param path string?
function InstallLocation:package(path)
return Path.concat { self.dir, "packages", path }
end
@@ -65,4 +72,20 @@ function InstallLocation:lockfile(name)
return self:staging(("%s.lock"):format(name))
end
+---@param path string
+function InstallLocation:registry(path)
+ return Path.concat { self.dir, "registry", path }
+end
+
+---@param opts { PATH: '"append"' | '"prepend"' | '"skip"' }
+function InstallLocation:set_env(opts)
+ vim.env.MASON = self.dir
+
+ if opts.PATH == "prepend" then
+ vim.env.PATH = self:bin() .. platform.path_sep .. vim.env.PATH
+ elseif opts.PATH == "append" then
+ vim.env.PATH = vim.env.PATH .. platform.path_sep .. self:bin()
+ end
+end
+
return InstallLocation
diff --git a/lua/mason-core/installer/runner.lua b/lua/mason-core/installer/runner.lua
index 8a03b724..3e3580e5 100644
--- a/lua/mason-core/installer/runner.lua
+++ b/lua/mason-core/installer/runner.lua
@@ -130,7 +130,7 @@ function InstallRunner:execute(opts, callback)
context:build_receipt():on_success(
---@param receipt InstallReceipt
function(receipt)
- linker.unlink(handle.package, receipt):on_failure(function(err)
+ linker.unlink(handle.package, receipt, self.location):on_failure(function(err)
log.error("Failed to unlink failed installation.", err)
end)
end
diff --git a/lua/mason-core/package.lua b/lua/mason-core/package.lua
index 1c9eee96..58ad9b46 100644
--- a/lua/mason-core/package.lua
+++ b/lua/mason-core/package.lua
@@ -184,7 +184,7 @@ function Package:unlink(receipt)
-- 1. Unlink
local linker = require "mason-core.installer.linker"
- linker.unlink(self, receipt):get_or_throw()
+ linker.unlink(self, receipt, InstallLocation.global()):get_or_throw()
-- 2. Remove installation artifacts
fs.sync.rmrf(install_path)
@@ -199,7 +199,7 @@ function Package:get_handle()
end
function Package:get_install_path()
- return path.package_prefix(self.name)
+ return InstallLocation.global():package(self.name)
end
---@return Optional # Optional<InstallReceipt>
@@ -254,7 +254,8 @@ function Package:get_all_versions()
end
function Package:get_lsp_settings_schema()
- local schema_file = path.share_prefix(path.concat { "mason-schemas", "lsp", ("%s.json"):format(self.name) })
+ local schema_file = InstallLocation.global()
+ :share(path.concat { "mason-schemas", "lsp", ("%s.json"):format(self.name) })
if fs.sync.file_exists(schema_file) then
return Result.pcall(vim.json.decode, fs.sync.read_file(schema_file), {
luanil = { object = true, array = true },
diff --git a/lua/mason-core/path.lua b/lua/mason-core/path.lua
index 3d7c8668..9eeed3ba 100644
--- a/lua/mason-core/path.lua
+++ b/lua/mason-core/path.lua
@@ -27,44 +27,4 @@ function M.is_subdirectory(root_path, path)
return root_path == path or path:sub(1, #root_path + 1) == root_path .. sep
end
----@param dir string?
-function M.install_prefix(dir)
- local settings = require "mason.settings"
- return M.concat { settings.current.install_root_dir, dir }
-end
-
----@param executable string?
-function M.bin_prefix(executable)
- return M.concat { M.install_prefix "bin", executable }
-end
-
----@param file string?
-function M.share_prefix(file)
- return M.concat { M.install_prefix "share", file }
-end
-
----@param file string?
-function M.opt_prefix(file)
- return M.concat { M.install_prefix "opt", file }
-end
-
----@param name string?
-function M.package_prefix(name)
- return M.concat { M.install_prefix "packages", name }
-end
-
----@param name string?
-function M.package_build_prefix(name)
- return M.concat { M.install_prefix "staging", name }
-end
-
----@param name string
-function M.package_lock(name)
- return M.package_build_prefix(("%s.lock"):format(name))
-end
-
-function M.registry_prefix()
- return M.install_prefix "registries"
-end
-
return M
diff --git a/lua/mason-registry/init.lua b/lua/mason-registry/init.lua
index a407ad4f..9842748b 100644
--- a/lua/mason-registry/init.lua
+++ b/lua/mason-registry/init.lua
@@ -1,4 +1,5 @@
local EventEmitter = require "mason-core.EventEmitter"
+local InstallLocation = require "mason-core.installer.location"
local Optional = require "mason-core.optional"
local _ = require "mason-core.functional"
local fs = require "mason-core.fs"
@@ -20,6 +21,7 @@ local sources = require "mason-registry.sources"
local M = setmetatable({}, { __index = EventEmitter })
EventEmitter.init(M)
+---@type fun(location: InstallLocation): table<string, true>
local scan_install_root
do
@@ -37,13 +39,14 @@ do
end)
)
+ ---@param location InstallLocation
---@return table<string, true>
- scan_install_root = function()
+ scan_install_root = function(location)
if cached_dirs then
return cached_dirs
end
log.trace "Scanning installation root dir"
- local ok, entries = pcall(fs.sync.readdir, path.package_prefix())
+ local ok, entries = pcall(fs.sync.readdir, location:package())
if not ok then
log.debug("Failed to scan installation root dir", entries)
-- presume installation root dir has not been created yet (i.e., no packages installed)
@@ -63,7 +66,7 @@ end
---modules required to load.
---@param package_name string
function M.is_installed(package_name)
- return scan_install_root()[package_name] == true
+ return scan_install_root(InstallLocation.global())[package_name] == true
end
---Returns an instance of the Package class if the provided package name exists. This function errors if a package cannot be found.
@@ -93,7 +96,7 @@ local get_packages = _.map(M.get_package)
---Returns all installed package names. This is a fast function that doesn't load any extra modules.
---@return string[]
function M.get_installed_package_names()
- return _.keys(scan_install_root())
+ return _.keys(scan_install_root(InstallLocation.global()))
end
---Returns all installed package instances. This is a slower function that loads more modules.
diff --git a/lua/mason-registry/sources/github.lua b/lua/mason-registry/sources/github.lua
index fce5077f..81ccaf56 100644
--- a/lua/mason-registry/sources/github.lua
+++ b/lua/mason-registry/sources/github.lua
@@ -1,3 +1,4 @@
+local InstallLocation = require "mason-core.installer.location"
local Optional = require "mason-core.optional"
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
@@ -31,7 +32,7 @@ GitHubRegistrySource.__index = GitHubRegistrySource
---@param spec GitHubRegistrySourceSpec
function GitHubRegistrySource.new(spec)
- local root_dir = path.concat { path.registry_prefix(), "github", spec.namespace, spec.name }
+ local root_dir = InstallLocation.global():registry(path.concat { "github", spec.namespace, spec.name })
return setmetatable({
id = spec.id,
spec = spec,
diff --git a/lua/mason/init.lua b/lua/mason/init.lua
index 269eda3c..0be007a4 100644
--- a/lua/mason/init.lua
+++ b/lua/mason/init.lua
@@ -1,5 +1,4 @@
-local path = require "mason-core.path"
-local platform = require "mason-core.platform"
+local InstallLocation = require "mason-core.installer.location"
local settings = require "mason.settings"
local M = {}
@@ -20,13 +19,8 @@ function M.setup(config)
if config then
settings.set(config)
end
- vim.env.MASON = settings.current.install_root_dir
- if settings.current.PATH == "prepend" then
- vim.env.PATH = path.bin_prefix() .. platform.path_sep .. vim.env.PATH
- elseif settings.current.PATH == "append" then
- vim.env.PATH = vim.env.PATH .. platform.path_sep .. path.bin_prefix()
- end
+ InstallLocation.global():set_env { PATH = settings.current.PATH }
require "mason.api.command"
setup_autocmds()
diff --git a/tests/mason-core/installer/linker_spec.lua b/tests/mason-core/installer/linker_spec.lua
index 9684f57d..eb5ce394 100644
--- a/tests/mason-core/installer/linker_spec.lua
+++ b/tests/mason-core/installer/linker_spec.lua
@@ -41,18 +41,19 @@ describe("linker", function()
it("should symlink executable on Unix", function()
local dummy = registry.get_package "dummy"
+ local ctx = test_helpers.create_context()
+
stub(fs.async, "file_exists")
stub(fs.async, "symlink")
stub(fs.async, "write_file")
- fs.async.file_exists.on_call_with(path.bin_prefix "my-executable").returns(false)
- fs.async.file_exists.on_call_with(path.bin_prefix "another-executable").returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:bin "my-executable").returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:bin "another-executable").returns(false)
fs.async.file_exists
.on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "my-executable" })
.returns(true)
fs.async.file_exists.on_call_with(path.concat { dummy:get_install_path(), "another-executable" }).returns(true)
- local ctx = test_helpers.create_context()
ctx:link_bin("my-executable", path.concat { "nested", "path", "my-executable" })
ctx:link_bin("another-executable", "another-executable")
local result = a.run_blocking(linker.link, ctx)
@@ -62,35 +63,36 @@ describe("linker", function()
assert.spy(fs.async.symlink).was_called(2)
assert
.spy(fs.async.symlink)
- .was_called_with(path.concat { dummy:get_install_path(), "another-executable" }, path.bin_prefix "another-executable")
+ .was_called_with(path.concat { dummy:get_install_path(), "another-executable" }, ctx.location:bin "another-executable")
assert
.spy(fs.async.symlink)
.was_called_with(
path.concat { dummy:get_install_path(), "nested", "path", "my-executable" },
- path.bin_prefix "my-executable"
+ ctx.location:bin "my-executable"
)
end)
it("should write executable wrapper on Windows", function()
+ local dummy = registry.get_package "dummy"
+ local ctx = test_helpers.create_context()
+
platform.is.darwin = false
platform.is.mac = false
platform.is.linux = false
platform.is.unix = false
platform.is.win = true
- local dummy = registry.get_package "dummy"
stub(fs.async, "file_exists")
stub(fs.async, "symlink")
stub(fs.async, "write_file")
- fs.async.file_exists.on_call_with(path.bin_prefix "my-executable").returns(false)
- fs.async.file_exists.on_call_with(path.bin_prefix "another-executable").returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:bin "my-executable").returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:bin "another-executable").returns(false)
fs.async.file_exists
.on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "my-executable" })
.returns(true)
fs.async.file_exists.on_call_with(path.concat { dummy:get_install_path(), "another-executable" }).returns(true)
- local ctx = test_helpers.create_context()
ctx:link_bin("my-executable", path.concat { "nested", "path", "my-executable" })
ctx:link_bin("another-executable", "another-executable")
@@ -100,17 +102,19 @@ describe("linker", function()
assert.spy(fs.async.symlink).was_called(0)
assert.spy(fs.async.write_file).was_called(2)
assert.spy(fs.async.write_file).was_called_with(
- path.bin_prefix "another-executable.cmd",
+ ctx.location:bin "another-executable.cmd",
WIN_CMD_SCRIPT:format(path.concat { dummy:get_install_path(), "another-executable" })
)
assert.spy(fs.async.write_file).was_called_with(
- path.bin_prefix "my-executable.cmd",
+ ctx.location:bin "my-executable.cmd",
WIN_CMD_SCRIPT:format(path.concat { dummy:get_install_path(), "nested", "path", "my-executable" })
)
end)
it("should symlink share files", function()
local dummy = registry.get_package "dummy"
+ local ctx = test_helpers.create_context()
+
stub(fs.async, "mkdirp")
stub(fs.async, "dir_exists")
stub(fs.async, "file_exists")
@@ -118,11 +122,10 @@ describe("linker", function()
stub(fs.async, "write_file")
-- mock non-existent dest files
- fs.async.file_exists.on_call_with(path.share_prefix "share-file").returns(false)
- fs.async.file_exists.on_call_with(path.share_prefix(path.concat { "nested", "share-file" })).returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:share "share-file").returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:share(path.concat { "nested", "share-file" })).returns(false)
- fs.async.dir_exists.on_call_with(path.share_prefix()).returns(false)
- fs.async.dir_exists.on_call_with(path.share_prefix "nested/path").returns(false)
+ fs.async.dir_exists.on_call_with(ctx.location:share "nested/path").returns(false)
-- mock existent source files
fs.async.file_exists.on_call_with(path.concat { dummy:get_install_path(), "share-file" }).returns(true)
@@ -130,7 +133,6 @@ describe("linker", function()
.on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" })
.returns(true)
- local ctx = test_helpers.create_context()
ctx.links.share["nested/path/share-file"] = path.concat { "nested", "path", "to", "share-file" }
ctx.links.share["share-file"] = "share-file"
@@ -142,36 +144,36 @@ describe("linker", function()
assert.spy(fs.async.symlink).was_called(2)
assert
.spy(fs.async.symlink)
- .was_called_with(path.concat { dummy:get_install_path(), "share-file" }, path.share_prefix "share-file")
+ .was_called_with(path.concat { dummy:get_install_path(), "share-file" }, ctx.location:share "share-file")
assert.spy(fs.async.symlink).was_called_with(
path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" },
- path.share_prefix "nested/path/share-file"
+ ctx.location:share "nested/path/share-file"
)
assert.spy(fs.async.mkdirp).was_called(2)
- assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix())
- assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix "nested/path")
+ assert.spy(fs.async.mkdirp).was_called_with(ctx.location:share "nested/path")
end)
it("should copy share files on Windows", function()
+ local dummy = registry.get_package "dummy"
+ local ctx = test_helpers.create_context()
+
platform.is.darwin = false
platform.is.mac = false
platform.is.linux = false
platform.is.unix = false
platform.is.win = true
- local dummy = registry.get_package "dummy"
stub(fs.async, "mkdirp")
stub(fs.async, "dir_exists")
stub(fs.async, "file_exists")
stub(fs.async, "copy_file")
-- mock non-existent dest files
- fs.async.file_exists.on_call_with(path.share_prefix "share-file").returns(false)
- fs.async.file_exists.on_call_with(path.share_prefix(path.concat { "nested", "share-file" })).returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:share "share-file").returns(false)
+ fs.async.file_exists.on_call_with(ctx.location:share(path.concat { "nested", "share-file" })).returns(false)
- fs.async.dir_exists.on_call_with(path.share_prefix()).returns(false)
- fs.async.dir_exists.on_call_with(path.share_prefix "nested/path").returns(false)
+ fs.async.dir_exists.on_call_with(ctx.location:share "nested/path").returns(false)
-- mock existent source files
fs.async.file_exists.on_call_with(path.concat { dummy:get_install_path(), "share-file" }).returns(true)
@@ -179,7 +181,6 @@ describe("linker", function()
.on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" })
.returns(true)
- local ctx = test_helpers.create_context()
ctx.links.share["nested/path/share-file"] = path.concat { "nested", "path", "to", "share-file" }
ctx.links.share["share-file"] = "share-file"
@@ -190,15 +191,14 @@ describe("linker", function()
assert.spy(fs.async.copy_file).was_called(2)
assert
.spy(fs.async.copy_file)
- .was_called_with(path.concat { dummy:get_install_path(), "share-file" }, path.share_prefix "share-file", { excl = true })
+ .was_called_with(path.concat { dummy:get_install_path(), "share-file" }, ctx.location:share "share-file", { excl = true })
assert.spy(fs.async.copy_file).was_called_with(
path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" },
- path.share_prefix "nested/path/share-file",
+ ctx.location:share "nested/path/share-file",
{ excl = true }
)
assert.spy(fs.async.mkdirp).was_called(2)
- assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix())
- assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix "nested/path")
+ assert.spy(fs.async.mkdirp).was_called_with(ctx.location:share "nested/path")
end)
end)
diff --git a/tests/mason/setup_spec.lua b/tests/mason/setup_spec.lua
index b733edd0..68871119 100644
--- a/tests/mason/setup_spec.lua
+++ b/tests/mason/setup_spec.lua
@@ -1,6 +1,6 @@
+local InstallLocation = require "mason-core.installer.location"
local mason = require "mason"
local match = require "luassert.match"
-local path = require "mason-core.path"
local settings = require "mason.settings"
describe("mason setup", function()
@@ -12,32 +12,38 @@ describe("mason setup", function()
it("should enhance the PATH environment", function()
mason.setup()
- assert.equals(("%s:/usr/local/bin:/usr/bin"):format(path.bin_prefix()), vim.env.PATH)
+ local global_location = InstallLocation.global()
+ assert.equals(("%s:/usr/local/bin:/usr/bin"):format(global_location:bin()), vim.env.PATH)
end)
it("should prepend the PATH environment", function()
mason.setup { PATH = "prepend" }
- assert.equals(("%s:/usr/local/bin:/usr/bin"):format(path.bin_prefix()), vim.env.PATH)
+ local global_location = InstallLocation.global()
+ assert.equals(("%s:/usr/local/bin:/usr/bin"):format(global_location:bin()), vim.env.PATH)
end)
it("should append PATH", function()
mason.setup { PATH = "append" }
- assert.equals(("/usr/local/bin:/usr/bin:%s"):format(path.bin_prefix()), vim.env.PATH)
+ local global_location = InstallLocation.global()
+ assert.equals(("/usr/local/bin:/usr/bin:%s"):format(global_location:bin()), vim.env.PATH)
end)
it("shouldn't modify PATH", function()
local PATH = vim.env.PATH
+ local global_location = InstallLocation.global()
mason.setup { PATH = "skip" }
assert.equals(PATH, vim.env.PATH)
end)
it("should set MASON env", function()
assert.is_nil(vim.env.MASON)
+ local global_location = InstallLocation.global()
mason.setup()
assert.equals(vim.fn.expand "~/.local/share/nvim/mason", vim.env.MASON)
end)
it("should set up user commands", function()
+ local global_location = InstallLocation.global()
mason.setup()
local user_commands = vim.api.nvim_get_commands {}