diff options
| author | William Boman <william@redwill.se> | 2025-03-02 02:34:16 +0100 |
|---|---|---|
| committer | William Boman <william@redwill.se> | 2025-03-03 02:17:02 +0100 |
| commit | 11b8792af0462f5255c86b2d51d7430a223f4136 (patch) | |
| tree | 11e2bbb8225d43c1914dc5d53034dd2915a43350 /lua | |
| parent | fix(spawn): expand executable paths on Windows before passing to uv_spawn (#1... (diff) | |
| download | mason-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
Diffstat (limited to 'lua')
| -rw-r--r-- | lua/mason-core/installer/InstallLocation.lua | 5 | ||||
| -rw-r--r-- | lua/mason-registry/init.lua | 272 | ||||
| -rw-r--r-- | lua/mason-registry/installer.lua | 72 | ||||
| -rw-r--r-- | lua/mason-registry/sources/file.lua | 6 | ||||
| -rw-r--r-- | lua/mason-registry/sources/github.lua | 20 | ||||
| -rw-r--r-- | lua/mason-registry/sources/init.lua | 208 | ||||
| -rw-r--r-- | lua/mason-registry/sources/lua.lua | 10 | ||||
| -rw-r--r-- | lua/mason/health.lua | 3 | ||||
| -rw-r--r-- | lua/mason/init.lua | 8 | ||||
| -rw-r--r-- | lua/mason/ui/instance.lua | 2 |
10 files changed, 300 insertions, 306 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(), |
