aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2025-03-02 02:34:16 +0100
committerWilliam Boman <william@redwill.se>2025-03-03 02:17:02 +0100
commit11b8792af0462f5255c86b2d51d7430a223f4136 (patch)
tree11e2bbb8225d43c1914dc5d53034dd2915a43350
parentfix(spawn): expand executable paths on Windows before passing to uv_spawn (#1... (diff)
downloadmason-11b8792af0462f5255c86b2d51d7430a223f4136.tar
mason-11b8792af0462f5255c86b2d51d7430a223f4136.tar.gz
mason-11b8792af0462f5255c86b2d51d7430a223f4136.tar.bz2
mason-11b8792af0462f5255c86b2d51d7430a223f4136.tar.lz
mason-11b8792af0462f5255c86b2d51d7430a223f4136.tar.xz
mason-11b8792af0462f5255c86b2d51d7430a223f4136.tar.zst
mason-11b8792af0462f5255c86b2d51d7430a223f4136.zip
refactor(registry): refactor registry initialization
-rw-r--r--lua/mason-core/installer/InstallLocation.lua5
-rw-r--r--lua/mason-registry/init.lua272
-rw-r--r--lua/mason-registry/installer.lua72
-rw-r--r--lua/mason-registry/sources/file.lua6
-rw-r--r--lua/mason-registry/sources/github.lua20
-rw-r--r--lua/mason-registry/sources/init.lua208
-rw-r--r--lua/mason-registry/sources/lua.lua10
-rw-r--r--lua/mason/health.lua3
-rw-r--r--lua/mason/init.lua8
-rw-r--r--lua/mason/ui/instance.lua2
-rw-r--r--tests/mason/setup_spec.lua2
11 files changed, 301 insertions, 307 deletions
diff --git a/lua/mason-core/installer/InstallLocation.lua b/lua/mason-core/installer/InstallLocation.lua
index 00b517b9..77252761 100644
--- a/lua/mason-core/installer/InstallLocation.lua
+++ b/lua/mason-core/installer/InstallLocation.lua
@@ -24,7 +24,6 @@ function InstallLocation:get_dir()
return self.dir
end
----@async
function InstallLocation:initialize()
local Result = require "mason-core.result"
local fs = require "mason-core.fs"
@@ -37,8 +36,8 @@ function InstallLocation:initialize()
self:package(),
self:staging(),
} do
- if not fs.async.dir_exists(p) then
- try(Result.pcall(fs.async.mkdirp, p))
+ if not fs.sync.dir_exists(p) then
+ try(Result.pcall(fs.sync.mkdirp, p))
end
end
end)
diff --git a/lua/mason-registry/init.lua b/lua/mason-registry/init.lua
index 746e487b..5e4d124f 100644
--- a/lua/mason-registry/init.lua
+++ b/lua/mason-registry/init.lua
@@ -1,230 +1,158 @@
local EventEmitter = require "mason-core.EventEmitter"
local InstallLocation = require "mason-core.installer.InstallLocation"
-local Optional = require "mason-core.optional"
-local _ = require "mason-core.functional"
-local fs = require "mason-core.fs"
local log = require "mason-core.log"
-local path = require "mason-core.path"
-local sources = require "mason-registry.sources"
+local uv = vim.loop
+local LazySourceCollection = require "mason-registry.sources"
----@class RegistrySource
----@field id string
----@field get_package fun(self: RegistrySource, pkg_name: string): Package?
----@field get_all_package_names fun(self: RegistrySource): string[]
----@field get_all_package_specs fun(self: RegistrySource): PackageSpec[] | RegistryPackageSpec[]
----@field get_display_name fun(self: RegistrySource): string
----@field is_installed fun(self: RegistrySource): boolean
----@field get_installer fun(self: RegistrySource): Optional # Optional<async fun (): Result>
+-- singleton
+local Registry = EventEmitter:new()
----@class MasonRegistry : EventEmitter
----@diagnostic disable-next-line: assign-type-mismatch
-local M = setmetatable({}, { __index = EventEmitter })
-EventEmitter.init(M)
-
----@type fun(location: InstallLocation): table<string, true>
-local scan_install_root
-
-do
- ---@type table<string, true>?
- local cached_dirs
-
- local get_directories = _.compose(
- _.set_of,
- _.filter_map(function(entry)
- if entry.type == "directory" then
- return Optional.of(entry.name)
- else
- return Optional.empty()
- end
- end)
- )
-
- ---@param location InstallLocation
- ---@return table<string, true>
- 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, 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)
- return {}
- end
- cached_dirs = get_directories(entries)
- vim.schedule(function()
- cached_dirs = nil
- end)
- log.trace("Resolved installation root dirs", cached_dirs)
- return cached_dirs
- end
-end
+Registry.sources = LazySourceCollection:new()
+---@type table<string, string[]>
+Registry.aliases = {}
----Checks whether the provided package name is installed.
----In many situations, this is a more efficient option than the Package:is_installed() method due to a smaller amount of
----modules required to load.
----@param package_name string
-function M.is_installed(package_name)
- return scan_install_root(InstallLocation.global())[package_name] == true
+---@param pkg_name string
+function Registry.is_installed(pkg_name)
+ local ok, stat = pcall(uv.fs_stat, InstallLocation.global():package(pkg_name), "r", 438)
+ return ok and stat.type == "directory"
end
----Returns an instance of the Package class if the provided package name exists. This function errors if a package cannot be found.
----@param package_name string
+---Returns an instance of the Package class if the provided package name exists. This function errors if a package
+---cannot be found.
+---@param pkg_name string
---@return Package
-function M.get_package(package_name)
- for source in sources.iter() do
- local pkg = source:get_package(package_name)
+function Registry.get_package(pkg_name)
+ for source in Registry.sources:iterate() do
+ local pkg = source:get_package(pkg_name)
if pkg then
return pkg
end
end
- log.fmt_error("Cannot find package %q.", package_name)
- error(("Cannot find package %q."):format(package_name))
+ log.fmt_error("Cannot find package %q.", pkg_name)
+ error(("Cannot find package %q."):format(pkg_name))
end
----Returns true if the provided package_name can be found in the registry.
----@param package_name string
----@return boolean
-function M.has_package(package_name)
- local ok = pcall(M.get_package, package_name)
+function Registry.has_package(pkg_name)
+ local ok = pcall(Registry.get_package, pkg_name)
return ok
end
-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(InstallLocation.global()))
+function Registry.get_installed_package_names()
+ local fs = require "mason-core.fs"
+ if not fs.sync.dir_exists(InstallLocation.global():package()) then
+ return {}
+ end
+ local entries = fs.sync.readdir(InstallLocation:global():package())
+ local directories = {}
+ for _, entry in ipairs(entries) do
+ if entry.type == "directory" then
+ directories[#directories + 1] = entry.name
+ end
+ end
+ -- TODO: validate that entry is a mason package
+ return directories
end
----Returns all installed package instances. This is a slower function that loads more modules.
----@return Package[]
-function M.get_installed_packages()
- return get_packages(M.get_installed_package_names())
+function Registry.get_installed_packages()
+ return vim.tbl_map(Registry.get_package, Registry.get_installed_package_names())
end
----Returns all package names. This is a fast function that doesn't load any extra modules.
----@return string[]
-function M.get_all_package_names()
+function Registry.get_all_package_names()
local pkgs = {}
- for source in sources.iter() do
+ for source in Registry.sources:iterate() do
for _, name in ipairs(source:get_all_package_names()) do
pkgs[name] = true
end
end
- return _.keys(pkgs)
+ return vim.tbl_keys(pkgs)
end
----Returns all package instances. This is a slower function that loads more modules.
----@return Package[]
-function M.get_all_packages()
- return get_packages(M.get_all_package_names())
+function Registry.get_all_packages()
+ return vim.tbl_map(Registry.get_package, Registry.get_all_package_names())
end
----@return RegistryPackageSpec[]
-function M.get_all_package_specs()
+function Registry.get_all_package_specs()
local specs = {}
- for source in sources.iter() do
- vim.list_extend(specs, source:get_all_package_specs())
+ for source in Registry.sources:iterate() do
+ for _, spec in ipairs(source:get_all_package_specs()) do
+ if not specs[spec.name] then
+ specs[spec.name] = spec
+ end
+ end
end
- return _.uniq_by(_.prop "name", specs)
+ return vim.tbl_values(specs)
end
-local STATE_FILE = path.concat {
- vim.fn.stdpath((vim.fn.has "nvim-0.8.0" == 1) and "state" or "cache"),
- "mason-registry-update",
-}
-
----@param time integer
-local function get_store_age(time)
- local checksum = sources.checksum()
- if fs.sync.file_exists(STATE_FILE) then
- local parse_state_file =
- _.compose(_.evolve { timestamp = tonumber }, _.zip_table { "checksum", "timestamp" }, _.split "\n")
- local state = parse_state_file(fs.sync.read_file(STATE_FILE))
- if checksum == state.checksum then
- return math.abs(time - state.timestamp)
+---Register aliases for the specified packages
+---@param new_aliases table<string, string[]>
+function Registry.register_package_aliases(new_aliases)
+ for pkg_name, pkg_aliases in pairs(new_aliases) do
+ Registry.aliases[pkg_name] = Registry.aliases[pkg_name] or {}
+ for _, alias in pairs(pkg_aliases) do
+ if alias ~= pkg_name then
+ table.insert(Registry.aliases[pkg_name], alias)
+ end
end
end
- return time
end
----@param time integer
-local function update_store_timestamp(time)
- local dir = vim.fn.fnamemodify(STATE_FILE, ":h")
- if not fs.sync.dir_exists(dir) then
- fs.sync.mkdirp(dir)
- end
- fs.sync.write_file(STATE_FILE, _.join("\n", { sources.checksum(), tostring(time) }))
+---@param name string
+function Registry.get_package_aliases(name)
+ return Registry.aliases[name] or {}
end
---@param callback? fun(success: boolean, updated_registries: RegistrySource[])
-function M.update(callback)
+function Registry.update(callback)
+ log.debug "Updating the registry."
local a = require "mason-core.async"
+ local installer = require "mason-registry.installer"
+ local noop = function() end
- return a.run(require("mason-registry.installer").run, function(success, result)
- if not callback then
- return
+ a.run(function()
+ if installer.channel then
+ log.trace "Registry update already in progress."
+ return installer.channel:receive():get_or_throw()
+ else
+ return installer
+ .install(Registry.sources)
+ :on_success(function(updated_registries)
+ Registry:emit("update", updated_registries)
+ end)
+ :get_or_throw()
end
- if not success then
- return callback(false, result)
- end
- result
- :on_success(function(value)
- callback(true, value)
- end)
- :on_failure(function(err)
- callback(false, err)
- end)
- end)
+ end, callback or noop)
end
local REGISTRY_STORE_TTL = 86400 -- 24 hrs
----@param cb? fun()
-function M.refresh(cb)
+---@param callback? fun(success: boolean, updated_registries: RegistrySource[])
+function Registry.refresh(callback)
+ log.debug "Refreshing the registry."
local a = require "mason-core.async"
+ local installer = require "mason-registry.installer"
- ---@async
- local function refresh()
- a.scheduler()
- local is_outdated = get_store_age(os.time()) > REGISTRY_STORE_TTL
- if is_outdated or not sources.is_installed() then
- if a.wait(M.update) then
- a.scheduler()
- update_store_timestamp(os.time())
- end
+ local state = installer.get_registry_state()
+ local is_state_ok = state
+ and (os.time() - state.timestamp) <= REGISTRY_STORE_TTL
+ and state.checksum == Registry.sources:checksum()
+
+ if is_state_ok and Registry.sources:is_all_installed() then
+ log.debug "Registry refresh not necessary."
+ if callback then
+ callback(true, {})
end
+ return
end
- if not cb then
- a.run_blocking(refresh)
+ if not callback then
+ return a.run_blocking(function()
+ return a.wait(Registry.update)
+ end)
else
- a.run(refresh, cb)
+ a.run(function()
+ return a.wait(Registry.update)
+ end, callback)
end
end
----@type table<string, string[]>
-local aliases = {}
-
----Register aliases for the specified packages
----@param new_aliases table<string, string[]>
-function M.register_package_aliases(new_aliases)
- for pkg_name, pkg_aliases in pairs(new_aliases) do
- aliases[pkg_name] = aliases[pkg_name] or {}
- for _, alias in pairs(pkg_aliases) do
- if alias ~= pkg_name then
- table.insert(aliases[pkg_name], alias)
- end
- end
- end
-end
-
----@param name string
-function M.get_package_aliases(name)
- return aliases[name] or {}
-end
-
-return M
+return Registry
diff --git a/lua/mason-registry/installer.lua b/lua/mason-registry/installer.lua
index 3fc4fb31..a1984921 100644
--- a/lua/mason-registry/installer.lua
+++ b/lua/mason-registry/installer.lua
@@ -1,38 +1,58 @@
local a = require "mason-core.async"
+local log = require "mason-core.log"
local OneShotChannel = require("mason-core.async.control").OneShotChannel
local Result = require "mason-core.result"
-local sources = require "mason-registry.sources"
+local _ = require "mason-core.functional"
+local fs = require "mason-core.fs"
+local path = require "mason-core.path"
local M = {}
----@type OneShotChannel?
-local update_channel
+local STATE_FILE = path.concat { vim.fn.stdpath "cache", "mason-registry-update" }
----@async
-function M.run()
- if not update_channel or update_channel:is_closed() then
- update_channel = OneShotChannel:new()
- a.run(function()
- update_channel:send(Result.try(function(try)
- local updated_sources = {}
- for source in sources.iter { include_uninstalled = true } do
- source:get_installer():if_present(function(installer)
- try(installer():map_err(function(err)
- return ("%s failed to install: %s"):format(source, err)
- end))
- table.insert(updated_sources, source)
- end)
- end
- return updated_sources
- end):on_success(function(updated_sources)
- if #updated_sources > 0 then
- require("mason-registry"):emit("update", updated_sources)
- end
- end))
- end, function() end)
+---@param sources LazySourceCollection
+---@param time integer
+local function update_registry_state(sources, time)
+ log.trace("Updating registry state", sources, time)
+ local dir = vim.fn.fnamemodify(STATE_FILE, ":h")
+ if not fs.sync.dir_exists(dir) then
+ fs.sync.mkdirp(dir)
+ end
+ fs.sync.write_file(STATE_FILE, _.join("\n", { sources:checksum(), tostring(time) }))
+end
+
+---@return { checksum: string, timestamp: integer }?
+function M.get_registry_state()
+ if fs.sync.file_exists(STATE_FILE) then
+ local parse_state_file =
+ _.compose(_.evolve { timestamp = tonumber }, _.zip_table { "checksum", "timestamp" }, _.split "\n")
+ return parse_state_file(fs.sync.read_file(STATE_FILE))
end
+end
- return update_channel:receive()
+---@async
+---@param sources LazySourceCollection
+---@return Result # Result<RegistrySource[]>
+function M.install(sources)
+ log.debug("Installing registries.", sources)
+ assert(not M.channel, "Cannot install when channel is active.")
+ M.channel = OneShotChannel:new()
+ local result = Result.try(function(try)
+ local updated_sources = {}
+ for source in sources:iterate { include_uninstalled = true } do
+ log.trace("Installing registry.", source)
+ try(source:install():map_err(function(err)
+ return ("%s failed to install: %s"):format(source, err)
+ end))
+ table.insert(updated_sources, source)
+ end
+ a.scheduler()
+ update_registry_state(sources, os.time())
+ return updated_sources
+ end)
+ M.channel:send(result)
+ M.channel = nil
+ return result
end
return M
diff --git a/lua/mason-registry/sources/file.lua b/lua/mason-registry/sources/file.lua
index 62e7d7a4..84566479 100644
--- a/lua/mason-registry/sources/file.lua
+++ b/lua/mason-registry/sources/file.lua
@@ -14,6 +14,7 @@ local util = require "mason-registry.sources.util"
local Channel = async_control.Channel
---@class FileRegistrySourceSpec
+---@field id string
---@field path string
---@class FileRegistrySource : RegistrySource
@@ -28,6 +29,7 @@ function FileRegistrySource:new(spec)
---@type FileRegistrySource
local instance = {}
setmetatable(instance, self)
+ instance.id = spec.id
instance.spec = spec
return instance
end
@@ -68,10 +70,6 @@ function FileRegistrySource:get_all_package_names()
return _.map(_.prop "name", self:get_all_package_specs())
end
-function FileRegistrySource:get_installer()
- return Optional.of(_.partial(self.install, self))
-end
-
---@async
function FileRegistrySource:install()
return Result.try(function(try)
diff --git a/lua/mason-registry/sources/github.lua b/lua/mason-registry/sources/github.lua
index b314d690..ceb503dc 100644
--- a/lua/mason-registry/sources/github.lua
+++ b/lua/mason-registry/sources/github.lua
@@ -15,7 +15,6 @@ local parse_checksums = _.compose(_.from_pairs, _.map(_.compose(_.reverse, _.spl
---@class GitHubRegistrySourceSpec
---@field id string
----@field repo string
---@field namespace string
---@field name string
---@field version string?
@@ -38,6 +37,7 @@ function GitHubRegistrySource:new(spec)
local root_dir = InstallLocation.global():registry(path.concat { "github", spec.namespace, spec.name })
instance.id = spec.id
instance.spec = spec
+ instance.repo = ("%s/%s"):format(spec.namespace, spec.name)
instance.root_dir = root_dir
instance.data_file = path.concat { root_dir, "registry.json" }
instance.info_file = path.concat { root_dir, "info.json" }
@@ -81,10 +81,6 @@ function GitHubRegistrySource:get_all_package_names()
return _.map(_.prop "name", self:get_all_package_specs())
end
-function GitHubRegistrySource:get_installer()
- return Optional.of(_.partial(self.install, self))
-end
-
---@async
function GitHubRegistrySource:install()
local zzlib = require "mason-vendor.zzlib"
@@ -106,7 +102,7 @@ function GitHubRegistrySource:install()
---@type GitHubRelease
local release = try(
providers.github
- .get_latest_release(self.spec.repo)
+ .get_latest_release(self.repo)
:map_err(_.always "Failed to fetch latest registry version from GitHub API.")
)
version = release.tag_name
@@ -114,7 +110,7 @@ function GitHubRegistrySource:install()
end
local zip_file = path.concat { self.root_dir, "registry.json.zip" }
- try(fetch(settings.current.github.download_url_template:format(self.spec.repo, version, "registry.json.zip"), {
+ try(fetch(settings.current.github.download_url_template:format(self.repo, version, "registry.json.zip"), {
out_file = zip_file,
}):map_err(_.always "Failed to download registry archive."))
local zip_buffer = fs.async.read_file(zip_file)
@@ -126,7 +122,7 @@ function GitHubRegistrySource:install()
pcall(fs.async.unlink, zip_file)
local checksums = try(
- fetch(settings.current.github.download_url_template:format(self.spec.repo, version, "checksums.txt")):map_err(
+ fetch(settings.current.github.download_url_template:format(self.repo, version, "checksums.txt")):map_err(
_.always "Failed to download checksums.txt."
)
)
@@ -158,17 +154,17 @@ end
function GitHubRegistrySource:get_display_name()
if self:is_installed() then
local info = self:get_info()
- return ("github.com/%s version: %s"):format(self.spec.repo, info.version)
+ return ("github.com/%s version: %s"):format(self.repo, info.version)
else
- return ("github.com/%s [uninstalled]"):format(self.spec.repo)
+ return ("github.com/%s [uninstalled]"):format(self.repo)
end
end
function GitHubRegistrySource:__tostring()
if self.spec.version then
- return ("GitHubRegistrySource(repo=%s, version=%s)"):format(self.spec.repo, self.spec.version)
+ return ("GitHubRegistrySource(repo=%s, version=%s)"):format(self.repo, self.spec.version)
else
- return ("GitHubRegistrySource(repo=%s)"):format(self.spec.repo)
+ return ("GitHubRegistrySource(repo=%s)"):format(self.repo)
end
end
diff --git a/lua/mason-registry/sources/init.lua b/lua/mason-registry/sources/init.lua
index af1b9f6a..fd9cc573 100644
--- a/lua/mason-registry/sources/init.lua
+++ b/lua/mason-registry/sources/init.lua
@@ -1,6 +1,76 @@
-local _ = require "mason-core.functional"
+---@class RegistrySource
+---@field id string
+---@field get_package fun(self: RegistrySource, pkg_name: string): Package?
+---@field get_all_package_names fun(self: RegistrySource): string[]
+---@field get_all_package_specs fun(self: RegistrySource): RegistryPackageSpec[]
+---@field get_display_name fun(self: RegistrySource): string
+---@field is_installed fun(self: RegistrySource): boolean
+---@field install fun(self: RegistrySource): Result
-local M = {}
+---@alias RegistrySourceType '"github"' | '"lua"' | '"file"'
+
+---@class LazySource
+---@field type RegistrySourceType
+---@field id string
+---@field init fun(id: string): RegistrySource
+local LazySource = {}
+LazySource.__index = LazySource
+
+---@param id string
+function LazySource.GitHub(id)
+ local namespace, name = id:match "^(.+)/(.+)$"
+ if not namespace or not name then
+ error(("Failed to parse repository from GitHub registry: %q"):format(id), 0)
+ end
+ local name, version = unpack(vim.split(name, "@"))
+ local GitHubRegistrySource = require "mason-registry.sources.github"
+ return GitHubRegistrySource:new {
+ id = id,
+ namespace = namespace,
+ name = name,
+ version = version,
+ }
+end
+
+---@param id string
+function LazySource.Lua(id)
+ local LuaRegistrySource = require "mason-registry.sources.lua"
+ return LuaRegistrySource:new {
+ id = id,
+ mod = id,
+ }
+end
+
+---@param id string
+function LazySource.File(id)
+ local FileRegistrySource = require "mason-registry.sources.file"
+ return FileRegistrySource:new {
+ id = id,
+ path = id,
+ }
+end
+
+---@param type RegistrySourceType
+---@param id string
+---@param init fun(id: string): RegistrySource
+function LazySource:new(type, id, init)
+ local instance = setmetatable({}, self)
+ instance.type = type
+ instance.id = id
+ instance.init = init
+ return instance
+end
+
+function LazySource:get()
+ if not self.instance then
+ self.instance = self.init(self.id)
+ end
+ return self.instance
+end
+
+function LazySource:__tostring()
+ return ("LazySource(type=%s, id=%s)"):format(self.type, self.id)
+end
---@param str string
local function split_once_left(str, char)
@@ -14,88 +84,46 @@ local function split_once_left(str, char)
end
---@param registry_id string
----@return fun(): RegistrySource # Thunk to instantiate provider.
local function parse(registry_id)
local type, id = split_once_left(registry_id, ":")
+ assert(id, ("Malformed registry %q"):format(registry_id))
if type == "github" then
- local namespace, name = id:match "^(.+)/(.+)$"
- if not namespace or not name then
- error(("Failed to parse repository from GitHub registry: %q."):format(registry_id), 0)
- end
- local name, version = unpack(vim.split(name, "@"))
- return function()
- local GitHubRegistrySource = require "mason-registry.sources.github"
- return GitHubRegistrySource:new {
- id = registry_id,
- repo = ("%s/%s"):format(namespace, name),
- namespace = namespace,
- name = name,
- version = version,
- }
- end
+ return LazySource:new(type, id, LazySource.GitHub)
elseif type == "lua" then
- return function()
- local LuaRegistrySource = require "mason-registry.sources.lua"
- return LuaRegistrySource:new {
- id = registry_id,
- mod = id,
- }
- end
+ return LazySource:new(type, id, LazySource.Lua)
elseif type == "file" then
- return function()
- local FileRegistrySource = require "mason-registry.sources.file"
- return FileRegistrySource:new {
- path = id,
- }
- end
- elseif type ~= nil then
- error(("Unknown registry type %q: %q."):format(type, registry_id), 0)
+ return LazySource:new(type, id, LazySource.File)
end
- error(("Malformed registry id: %q."):format(registry_id), 0)
+ error(("Unknown registry type: %s"):format(type))
end
----@type ((fun(): RegistrySource) | RegistrySource)[]
-local registries = {}
+---@class LazySourceCollection
+---@field list LazySource[]
+local LazySourceCollection = {}
+LazySourceCollection.__index = LazySourceCollection
----@param registry_ids string[]
-function M.set_registries(registry_ids)
- registries = {}
- for _, registry in ipairs(registry_ids) do
- local ok, err = pcall(function()
- table.insert(registries, parse(registry))
- end)
- if not ok then
- local log = require "mason-core.log"
- local notify = require "mason-core.notify"
- log.fmt_error("Failed to parse registry %q: %s", registry, err)
- notify(err, vim.log.levels.ERROR)
- end
- end
+---@return LazySourceCollection
+function LazySourceCollection:new()
+ local instance = {}
+ setmetatable(instance, self)
+ instance.list = {}
+ return instance
end
----@param opts? { include_uninstalled?: boolean }
-function M.iter(opts)
- opts = opts or {}
- local i = 1
- return function()
- while i <= #registries do
- local registry = registries[i]
- if type(registry) == "function" then
- -- unwrap thunk
- registry = registry()
- registries[i] = registry
- end
- i = i + 1
- if opts.include_uninstalled or registry:is_installed() then
- return registry
- end
- end
- end
+---@param registry string
+function LazySourceCollection:append(registry)
+ table.insert(self.list, parse(registry))
+ return self
+end
+
+---@param registry string
+function LazySourceCollection:prepend(registry)
+ table.insert(self.list, 1, parse(registry))
+ return self
end
----@return boolean #Returns true if all sources are installed.
-function M.is_installed()
- for source in M.iter { include_uninstalled = true } do
+function LazySourceCollection:is_all_installed()
+ for source in self:iterate { include_uninstalled = true } do
if not source:is_installed() then
return false
end
@@ -103,15 +131,37 @@ function M.is_installed()
return true
end
----@return string # The sha256 checksum of the currently registered sources.
-function M.checksum()
+function LazySourceCollection:checksum()
---@type string[]
- local registry_ids = {}
- for source in M.iter { include_uninstalled = true } do
- table.insert(registry_ids, source.id)
+ local registry_ids = vim.tbl_map(
+ ---@param source LazySource
+ function(source)
+ return source.id
+ end,
+ self.list
+ )
+ table.sort(registry_ids)
+ return vim.fn.sha256(table.concat(registry_ids, ""))
+end
+
+---@param opts? { include_uninstalled?: boolean }
+function LazySourceCollection:iterate(opts)
+ opts = opts or {}
+
+ local idx = 1
+ return function()
+ while idx <= #self.list do
+ local source = self.list[idx]:get()
+ idx = idx + 1
+ if opts.include_uninstalled or source:is_installed() then
+ return source
+ end
+ end
end
- local checksum = _.compose(vim.fn.sha256, _.join "", _.sort_by(_.identity))
- return checksum(registry_ids)
end
-return M
+function LazySourceCollection:__tostring()
+ return ("LazySourceCollection(list={%s})"):format(table.concat(vim.tbl_map(tostring, self.list), ", "))
+end
+
+return LazySourceCollection
diff --git a/lua/mason-registry/sources/lua.lua b/lua/mason-registry/sources/lua.lua
index fd88a49d..7fae54fe 100644
--- a/lua/mason-registry/sources/lua.lua
+++ b/lua/mason-registry/sources/lua.lua
@@ -1,4 +1,5 @@
local Optional = require "mason-core.optional"
+local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local log = require "mason-core.log"
@@ -35,6 +36,10 @@ function LuaRegistrySource:get_package(pkg_name)
end
end
+function LuaRegistrySource:install()
+ return Result.pcall(require, self.spec.mod)
+end
+
---@return string[]
function LuaRegistrySource:get_all_package_names()
local index = require(self.spec.mod)
@@ -53,11 +58,6 @@ function LuaRegistrySource:is_installed()
return ok
end
-function LuaRegistrySource:get_installer()
- local Optional = require "mason-core.optional"
- return Optional.empty()
-end
-
function LuaRegistrySource:get_display_name()
if self:is_installed() then
return ("require(%q)"):format(self.spec.mod)
diff --git a/lua/mason/health.lua b/lua/mason/health.lua
index 5c54b1b3..5ac2cd1b 100644
--- a/lua/mason/health.lua
+++ b/lua/mason/health.lua
@@ -6,7 +6,6 @@ local control = require "mason-core.async.control"
local platform = require "mason-core.platform"
local providers = require "mason-core.providers"
local registry = require "mason-registry"
-local registry_sources = require "mason-registry.sources"
local settings = require "mason.settings"
local spawn = require "mason-core.spawn"
local version = require "mason.version"
@@ -68,7 +67,7 @@ end
local function check_registries()
report_start "mason.nvim [Registries]"
a.wait(registry.refresh)
- for source in registry_sources.iter { include_uninstalled = true } do
+ for source in registry.sources:iterate { include_uninstalled = true } do
if source:is_installed() then
report_ok(("Registry `%s` is installed."):format(source:get_display_name()))
else
diff --git a/lua/mason/init.lua b/lua/mason/init.lua
index ff26cc8d..9c507f5c 100644
--- a/lua/mason/init.lua
+++ b/lua/mason/init.lua
@@ -1,4 +1,5 @@
local InstallLocation = require "mason-core.installer.InstallLocation"
+local Registry = require "mason-registry"
local settings = require "mason.settings"
local M = {}
@@ -20,11 +21,14 @@ function M.setup(config)
settings.set(config)
end
- InstallLocation.global():set_env { PATH = settings.current.PATH }
+ local global_location = InstallLocation.global()
+ global_location:set_env { PATH = settings.current.PATH }
+ for _, registry in ipairs(settings.current.registries) do
+ Registry.sources:append(registry)
+ end
require "mason.api.command"
setup_autocmds()
- require("mason-registry.sources").set_registries(settings.current.registries)
M.has_setup = true
end
diff --git a/lua/mason/ui/instance.lua b/lua/mason/ui/instance.lua
index 8a82bc64..70c85858 100644
--- a/lua/mason/ui/instance.lua
+++ b/lua/mason/ui/instance.lua
@@ -684,7 +684,7 @@ end
local function update_registry_info()
local registries = {}
- for source in require("mason-registry.sources").iter { include_uninstalled = true } do
+ for source in registry.sources:iterate { include_uninstalled = true } do
table.insert(registries, {
name = source:get_display_name(),
is_installed = source:is_installed(),
diff --git a/tests/mason/setup_spec.lua b/tests/mason/setup_spec.lua
index f3193a27..a4320bb3 100644
--- a/tests/mason/setup_spec.lua
+++ b/tests/mason/setup_spec.lua
@@ -10,7 +10,7 @@ describe("mason setup", function()
settings.set(settings._DEFAULT_SETTINGS)
end)
- it("should enhance the PATH environment", function()
+ it("should modify the PATH environment", function()
mason.setup()
local global_location = InstallLocation.global()
assert.equals(("%s:/usr/local/bin:/usr/bin"):format(global_location:bin()), vim.env.PATH)