diff options
| author | William Boman <william@redwill.se> | 2023-10-13 01:53:41 +0200 |
|---|---|---|
| committer | William Boman <william@redwill.se> | 2025-02-19 09:23:19 +0100 |
| commit | f1e58d3ce7ab3bdb3036b791811896a0220703ad (patch) | |
| tree | ce44529583dcf72844b206fe8578f0ada5ef153f | |
| parent | refactor(installer): move initializations to InstallContext constructor (diff) | |
| download | mason-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.lua | 45 | ||||
| -rw-r--r-- | lua/mason-core/installer/location.lua | 27 | ||||
| -rw-r--r-- | lua/mason-core/installer/runner.lua | 2 | ||||
| -rw-r--r-- | lua/mason-core/package.lua | 7 | ||||
| -rw-r--r-- | lua/mason-core/path.lua | 40 | ||||
| -rw-r--r-- | lua/mason-registry/init.lua | 11 | ||||
| -rw-r--r-- | lua/mason-registry/sources/github.lua | 3 | ||||
| -rw-r--r-- | lua/mason/init.lua | 10 | ||||
| -rw-r--r-- | tests/mason-core/installer/linker_spec.lua | 60 | ||||
| -rw-r--r-- | tests/mason/setup_spec.lua | 14 |
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 {} |
