diff options
| author | William Boman <william@redwill.se> | 2026-01-07 14:39:35 +0100 |
|---|---|---|
| committer | William Boman <william@redwill.se> | 2026-01-07 14:39:35 +0100 |
| commit | ad903f1c1f18fd229d67e523418b3aa6c9f1c862 (patch) | |
| tree | d52cca80e062b3df9b8daeba20435aa83c34c7a2 /lua/mason-registry/sources | |
| parent | fix(installer): update cwd after uv_fs_rename() was successful (#2033) (diff) | |
| download | mason-fix/support-removed-packages.tar mason-fix/support-removed-packages.tar.gz mason-fix/support-removed-packages.tar.bz2 mason-fix/support-removed-packages.tar.lz mason-fix/support-removed-packages.tar.xz mason-fix/support-removed-packages.tar.zst mason-fix/support-removed-packages.zip | |
feat: add support for removal of packages from a registryfix/support-removed-packages
This adds support for removal of packages from any given registry.
Currently mason.nvim doesn't support this at all and throws an error
when trying to interact with the registry in any way while having a
removed package installed locally.
This ensures that removed packages are available both in the `:Mason` UI
as well as the public Lua APIs. These "synthesized" packages only
supports uninstallation, and metadata such as licenses, categories,
homepage, etc is not available.
Diffstat (limited to 'lua/mason-registry/sources')
| -rw-r--r-- | lua/mason-registry/sources/github.lua | 1 | ||||
| -rw-r--r-- | lua/mason-registry/sources/init.lua | 17 | ||||
| -rw-r--r-- | lua/mason-registry/sources/synthesized.lua | 109 |
3 files changed, 124 insertions, 3 deletions
diff --git a/lua/mason-registry/sources/github.lua b/lua/mason-registry/sources/github.lua index 597e7d84..2b177bdd 100644 --- a/lua/mason-registry/sources/github.lua +++ b/lua/mason-registry/sources/github.lua @@ -1,5 +1,4 @@ local InstallLocation = require "mason-core.installer.InstallLocation" -local Optional = require "mason-core.optional" local Result = require "mason-core.result" local _ = require "mason-core.functional" local fetch = require "mason-core.fetch" diff --git a/lua/mason-registry/sources/init.lua b/lua/mason-registry/sources/init.lua index 765d4904..f468a8ef 100644 --- a/lua/mason-registry/sources/init.lua +++ b/lua/mason-registry/sources/init.lua @@ -11,7 +11,7 @@ local log = require "mason-core.log" ---@field serialize fun(self: RegistrySource): InstallReceiptRegistry ---@field is_same_location fun(self: RegistrySource, other: RegistrySource): boolean ----@alias RegistrySourceType '"github"' | '"lua"' | '"file"' +---@alias RegistrySourceType '"github"' | '"lua"' | '"file"' | '"synthesized"' ---@class LazySource ---@field type RegistrySourceType @@ -54,6 +54,11 @@ function LazySource.File(id) } end +function LazySource.Synthesized() + local SynthesizedSource = require "mason-registry.sources.synthesized" + return SynthesizedSource:new() +end + ---@param type RegistrySourceType ---@param id string ---@param init fun(id: string): RegistrySource @@ -115,6 +120,7 @@ end ---@class LazySourceCollection ---@field list LazySource[] +---@field synthesized LazySource local LazySourceCollection = {} LazySourceCollection.__index = LazySourceCollection @@ -123,6 +129,7 @@ function LazySourceCollection:new() local instance = {} setmetatable(instance, self) instance.list = {} + instance.synthesized = LazySource:new("synthesized", "synthesized", LazySource.Synthesized) return instance end @@ -184,7 +191,7 @@ function LazySourceCollection:checksum() return vim.fn.sha256(table.concat(registry_ids, "")) end ----@param opts? { include_uninstalled?: boolean } +---@param opts? { include_uninstalled?: boolean, include_synthesized?: boolean } function LazySourceCollection:iterate(opts) opts = opts or {} @@ -197,6 +204,12 @@ function LazySourceCollection:iterate(opts) return source end end + + -- We've exhausted the true registry sources, fall back to the synthesized registry source. + if idx == #self.list + 1 and opts.include_synthesized ~= false then + idx = idx + 1 + return self.synthesized:get() + end end end diff --git a/lua/mason-registry/sources/synthesized.lua b/lua/mason-registry/sources/synthesized.lua new file mode 100644 index 00000000..75638cd6 --- /dev/null +++ b/lua/mason-registry/sources/synthesized.lua @@ -0,0 +1,109 @@ +local Package = require "mason-core.package" +local Result = require "mason-core.result" +local _ = require "mason-core.functional" +local InstallReceipt = require("mason-core.receipt").InstallReceipt +local InstallLocation = require "mason-core.installer.InstallLocation" +local fs = require "mason-core.fs" +local log = require "mason-core.log" + +---@class SynthesizedRegistrySource : RegistrySource +---@field buffer table<string, Package> +local SynthesizedRegistrySource = {} +SynthesizedRegistrySource.__index = SynthesizedRegistrySource + +function SynthesizedRegistrySource:new() + ---@type SynthesizedRegistrySource + local instance = {} + setmetatable(instance, self) + instance.buffer = {} + return instance +end + +function SynthesizedRegistrySource:is_installed() + return true +end + +---@return RegistryPackageSpec[] +function SynthesizedRegistrySource:get_all_package_specs() + return {} +end + +---@param pkg_name string +---@param receipt InstallReceipt +---@return Package +function SynthesizedRegistrySource:load_package(pkg_name, receipt) + local installed_version = receipt:get_installed_package_version() + local source = { + id = ("pkg:mason/%s@%s"):format(pkg_name, installed_version or "N%2FA"), -- N%2FA = N/A + install = function() + error("This package can no longer be installed because it has been removed from the registry.", 0) + end, + } + ---@type RegistryPackageSpec + local spec = { + schema = "registry+v1", + name = pkg_name, + description = "", + categories = {}, + languages = {}, + homepage = "", + licenses = {}, + deprecation = { + since = receipt:get_installed_package_version() or "N/A", + message = "This package has been removed from the registry.", + }, + source = source, + } + local existing_pkg = self.buffer[pkg_name] + if existing_pkg then + existing_pkg:update(spec, self) + return existing_pkg + else + local pkg = Package:new(spec, self) + self.buffer[pkg_name] = pkg + return pkg + end +end + +---@param pkg_name string +---@return Package? +function SynthesizedRegistrySource:get_package(pkg_name) + local receipt_path = InstallLocation.global():receipt(pkg_name) + if fs.sync.file_exists(receipt_path) then + local ok, receipt_json = pcall(vim.json.decode, fs.sync.read_file(receipt_path)) + if ok then + local receipt = InstallReceipt.from_json(receipt_json) + return self:load_package(pkg_name, receipt) + else + log.error("Failed to decode package receipt", pkg_name, receipt_json) + end + end +end + +function SynthesizedRegistrySource:get_all_package_names() + return vim.tbl_keys(self.buffer) +end + +---@async +function SynthesizedRegistrySource:install() + return Result.success() +end + +function SynthesizedRegistrySource:get_display_name() + return "SynthesizedRegistrySource" +end + +function SynthesizedRegistrySource:serialize() + return {} +end + +---@param other SynthesizedRegistrySource +function SynthesizedRegistrySource:is_same_location(other) + return true +end + +function SynthesizedRegistrySource:__tostring() + return "SynthesizedRegistrySource" +end + +return SynthesizedRegistrySource |
