aboutsummaryrefslogtreecommitdiffstats
path: root/lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua')
-rw-r--r--lua/mason-core/installer/InstallLocation.lua11
-rw-r--r--lua/mason-core/installer/InstallRunner.lua2
-rw-r--r--lua/mason-core/installer/UninstallRunner.lua2
-rw-r--r--lua/mason-core/installer/context/init.lua2
-rw-r--r--lua/mason-core/installer/linker.lua69
-rw-r--r--lua/mason-core/package/AbstractPackage.lua13
-rw-r--r--lua/mason-core/package/init.lua13
-rw-r--r--lua/mason-registry/init.lua125
-rw-r--r--lua/mason-registry/installer.lua29
-rw-r--r--lua/mason-registry/sources/file.lua4
-rw-r--r--lua/mason-registry/sources/github.lua4
-rw-r--r--lua/mason-registry/sources/init.lua70
-rw-r--r--lua/mason-registry/sources/lua.lua4
-rw-r--r--lua/mason-registry/sources/synthesized.lua23
-rw-r--r--lua/mason-registry/sources/util.lua4
-rw-r--r--lua/mason/init.lua3
-rw-r--r--lua/mason/settings.lua8
17 files changed, 262 insertions, 124 deletions
diff --git a/lua/mason-core/installer/InstallLocation.lua b/lua/mason-core/installer/InstallLocation.lua
index 77252761..157adc8c 100644
--- a/lua/mason-core/installer/InstallLocation.lua
+++ b/lua/mason-core/installer/InstallLocation.lua
@@ -34,6 +34,7 @@ function InstallLocation:initialize()
self:bin(),
self:share(),
self:package(),
+ self:system_package(),
self:staging(),
} do
if not fs.sync.dir_exists(p) then
@@ -63,6 +64,11 @@ function InstallLocation:package(pkg)
return Path.concat { self.dir, "packages", pkg }
end
+---@param pkg string?
+function InstallLocation:system_package(pkg)
+ return Path.concat { self.dir, "system_packages", pkg }
+end
+
---@param path string?
function InstallLocation:staging(path)
return Path.concat { self.dir, "staging", path }
@@ -78,11 +84,6 @@ function InstallLocation:registry(path)
return Path.concat { self.dir, "registries", path }
end
----@param pkg string
-function InstallLocation:receipt(pkg)
- return Path.concat { self:package(pkg), "mason-receipt.json" }
-end
-
---@param opts { PATH: '"append"' | '"prepend"' | '"skip"' }
function InstallLocation:set_env(opts)
vim.env.MASON = self.dir
diff --git a/lua/mason-core/installer/InstallRunner.lua b/lua/mason-core/installer/InstallRunner.lua
index 93225e11..3eba879a 100644
--- a/lua/mason-core/installer/InstallRunner.lua
+++ b/lua/mason-core/installer/InstallRunner.lua
@@ -135,7 +135,7 @@ function InstallRunner:execute(opts, callback)
end))
---@type InstallReceipt
local receipt = try(context:build_receipt())
- try(Result.pcall(fs.sync.write_file, handle.location:receipt(handle.package.name), receipt:to_json()))
+ try(Result.pcall(fs.sync.write_file, handle.package:get_receipt_path(handle.location), receipt:to_json()))
return {
receipt = receipt,
}
diff --git a/lua/mason-core/installer/UninstallRunner.lua b/lua/mason-core/installer/UninstallRunner.lua
index 760ad88b..3429fc93 100644
--- a/lua/mason-core/installer/UninstallRunner.lua
+++ b/lua/mason-core/installer/UninstallRunner.lua
@@ -47,7 +47,7 @@ function UninstallRunner:execute(opts, callback)
else
try(pkg:unlink(location))
end
- fs.sync.rmrf(location:package(pkg.name))
+ fs.sync.rmrf(pkg:get_install_path(location))
return receipt
end):get_or_throw()
end, function(success, result)
diff --git a/lua/mason-core/installer/context/init.lua b/lua/mason-core/installer/context/init.lua
index ae96f986..9225315f 100644
--- a/lua/mason-core/installer/context/init.lua
+++ b/lua/mason-core/installer/context/init.lua
@@ -302,7 +302,7 @@ function InstallContext:build_receipt()
end
function InstallContext:get_install_path()
- return self.location:package(self.package.name)
+ return self.package:get_install_path(self.location)
end
return InstallContext
diff --git a/lua/mason-core/installer/linker.lua b/lua/mason-core/installer/linker.lua
index a26d2592..415f61eb 100644
--- a/lua/mason-core/installer/linker.lua
+++ b/lua/mason-core/installer/linker.lua
@@ -1,17 +1,17 @@
+local Path = require "mason-core.path"
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local a = require "mason-core.async"
local fs = require "mason-core.fs"
local log = require "mason-core.log"
-local path = require "mason-core.path"
local platform = require "mason-core.platform"
local M = {}
---@alias LinkContext { type: '"bin"' | '"opt"' | '"share"', prefix: fun(path: string, location: InstallLocation): string }
----@type table<'"BIN"' | '"OPT"' | '"SHARE"', LinkContext>
local LinkContext = {
+ ---@type LinkContext
BIN = {
type = "bin",
---@param path string
@@ -20,6 +20,7 @@ local LinkContext = {
return location:bin(path)
end,
},
+ ---@type LinkContext
OPT = {
type = "opt",
---@param path string
@@ -28,6 +29,7 @@ local LinkContext = {
return location:opt(path)
end,
},
+ ---@type LinkContext
SHARE = {
type = "share",
---@param path string
@@ -38,6 +40,36 @@ local LinkContext = {
},
}
+local SystemLinkContext = {
+ ---@type LinkContext
+ BIN = {
+ type = "bin",
+ ---@param path string
+ ---@param location InstallLocation
+ prefix = function(path, location)
+ return location:opt(Path.concat { "mason", "system", "bin", path })
+ end,
+ },
+ ---@type LinkContext
+ OPT = {
+ type = "opt",
+ ---@param path string
+ ---@param location InstallLocation
+ prefix = function(path, location)
+ return location:opt(Path.concat { "mason", "system", "opt", path })
+ end,
+ },
+ ---@type LinkContext
+ SHARE = {
+ type = "share",
+ ---@param path string
+ ---@param location InstallLocation
+ prefix = function(path, location)
+ return location:opt(Path.concat { "mason", "system", "share", path })
+ end,
+ },
+}
+
---@param receipt InstallReceipt
---@param link_context LinkContext
---@param location InstallLocation
@@ -48,7 +80,7 @@ local function unlink(receipt, link_context, location)
return
end
for linked_file in pairs(links) do
- if receipt:get_schema_version() == "1.0" and link_context == LinkContext.BIN and platform.is.win then
+ if receipt:get_schema_version() == "1.0" and link_context.type == "bin" and platform.is.win then
linked_file = linked_file .. ".cmd"
end
local share_path = link_context.prefix(linked_file, location)
@@ -63,10 +95,11 @@ end
---@nodiscard
function M.unlink(pkg, receipt, location)
log.fmt_debug("Unlinking %s", pkg, receipt:get_links())
+ local link_context = pkg.spec.system and SystemLinkContext or LinkContext
return Result.try(function(try)
- try(unlink(receipt, LinkContext.BIN, location))
- try(unlink(receipt, LinkContext.SHARE, location))
- try(unlink(receipt, LinkContext.OPT, location))
+ try(unlink(receipt, link_context.BIN, location))
+ try(unlink(receipt, link_context.SHARE, location))
+ try(unlink(receipt, link_context.OPT, location))
end)
end
@@ -78,12 +111,12 @@ local function link(context, link_context, link_fn)
log.trace("Linking", context.package, link_context.type, context.links[link_context.type])
return Result.try(function(try)
for name, rel_path in pairs(context.links[link_context.type]) do
- if platform.is.win and link_context == LinkContext.BIN then
+ if platform.is.win and link_context.type == "bin" then
name = ("%s.cmd"):format(name)
end
local new_abs_path = link_context.prefix(name, context.location)
- local target_abs_path = path.concat { context:get_install_path(), rel_path }
- local target_rel_path = path.relative(new_abs_path, target_abs_path)
+ local target_abs_path = Path.concat { context:get_install_path(), rel_path }
+ local target_rel_path = Path.relative(new_abs_path, target_abs_path)
-- 1. Ensure destination directory exists
a.scheduler()
@@ -129,8 +162,9 @@ local function copyfile(context, link_context)
end
---@param context InstallContext
-local function win_bin_wrapper(context)
- return link(context, LinkContext.BIN, function(new_abs_path, __, target_rel_path)
+---@param link_context LinkContext
+local function win_bin_wrapper(context, link_context)
+ return link(context, link_context, function(new_abs_path, __, target_rel_path)
local windows_target_rel_path = target_rel_path:gsub("/", "\\")
return Result.pcall(
fs.async.write_file,
@@ -156,15 +190,16 @@ end
---@nodiscard
function M.link(context)
log.fmt_debug("Linking %s", context.package)
+ local link_context = context.package.spec.system and SystemLinkContext or LinkContext
return Result.try(function(try)
if platform.is.win then
- try(win_bin_wrapper(context))
- try(copyfile(context, LinkContext.SHARE))
- try(copyfile(context, LinkContext.OPT))
+ try(win_bin_wrapper(context, link_context.BIN))
+ try(copyfile(context, link_context.SHARE))
+ try(copyfile(context, link_context.OPT))
else
- try(symlink(context, LinkContext.BIN))
- try(symlink(context, LinkContext.SHARE))
- try(symlink(context, LinkContext.OPT))
+ try(symlink(context, link_context.BIN))
+ try(symlink(context, link_context.SHARE))
+ try(symlink(context, link_context.OPT))
end
end)
end
diff --git a/lua/mason-core/package/AbstractPackage.lua b/lua/mason-core/package/AbstractPackage.lua
index 5678f4dd..a852d350 100644
--- a/lua/mason-core/package/AbstractPackage.lua
+++ b/lua/mason-core/package/AbstractPackage.lua
@@ -6,6 +6,7 @@ local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local fs = require "mason-core.fs"
local log = require "mason-core.log"
+local path = require "mason-core.path"
local settings = require "mason.settings"
local Semaphore = require("mason-core.async.control").Semaphore
@@ -128,7 +129,7 @@ end
---@return Optional # Optional<InstallReceipt>
function AbstractPackage:get_receipt(location)
location = location or InstallLocation.global()
- local receipt_path = location:receipt(self.name)
+ local receipt_path = self:get_receipt_path(location)
if fs.sync.file_exists(receipt_path) then
local receipt = require "mason-core.receipt"
return Optional.of(receipt.InstallReceipt.from_json(vim.json.decode(fs.sync.read_file(receipt_path))))
@@ -137,6 +138,11 @@ function AbstractPackage:get_receipt(location)
end
---@param location? InstallLocation
+function AbstractPackage:get_receipt_path(location)
+ return path.concat { self:get_install_path(location), "mason-receipt.json" }
+end
+
+---@param location? InstallLocation
---@return boolean
function AbstractPackage:is_installed(location)
error "Unimplemented."
@@ -174,6 +180,11 @@ function AbstractPackage:get_installed_version(location)
:or_else(nil)
end
+---@param location? InstallLocation
+function AbstractPackage:get_install_path(location)
+ error "Unimplemented."
+end
+
---@param opts? PackageInstallOpts
---@param callback? InstallRunnerCallback
---@return InstallHandle
diff --git a/lua/mason-core/package/init.lua b/lua/mason-core/package/init.lua
index cb4ef99e..07257d8d 100644
--- a/lua/mason-core/package/init.lua
+++ b/lua/mason-core/package/init.lua
@@ -76,6 +76,7 @@ Package.License = setmetatable({}, {
---@class RegistryPackageSpec
---@field schema RegistryPackageSpecSchema
---@field name string
+---@field system boolean?
---@field description string
---@field homepage string
---@field licenses string[]
@@ -147,9 +148,19 @@ function Package:uninstall(opts, callback)
end
---@param location? InstallLocation
+function Package:get_install_path(location)
+ location = location or InstallLocation.global()
+ if self.spec.system then
+ return location:system_package(self.name)
+ else
+ return location:package(self.name)
+ end
+end
+
+---@param location? InstallLocation
function Package:is_installed(location)
location = location or InstallLocation.global()
- local ok, stat = pcall(vim.loop.fs_stat, location:package(self.name))
+ local ok, stat = pcall(vim.loop.fs_stat, self:get_install_path(location))
if not ok or not stat then
return false
end
diff --git a/lua/mason-registry/init.lua b/lua/mason-registry/init.lua
index 5806c30a..1e59175c 100644
--- a/lua/mason-registry/init.lua
+++ b/lua/mason-registry/init.lua
@@ -1,13 +1,17 @@
local EventEmitter = require "mason-core.EventEmitter"
local InstallLocation = require "mason-core.installer.InstallLocation"
local log = require "mason-core.log"
+local path = require "mason-core.path"
local uv = vim.loop
local LazySourceCollection = require "mason-registry.sources"
-- singleton
local Registry = EventEmitter:new()
-Registry.sources = LazySourceCollection:new()
+Registry.sources = LazySourceCollection:new(path.concat { vim.fn.stdpath "cache", "mason-registry-update" })
+Registry.system_sources =
+ LazySourceCollection:new(path.concat { vim.fn.stdpath "cache", "mason-system-registry-update" }, true)
+
---@type table<string, string[]>
Registry.aliases = {}
@@ -37,6 +41,21 @@ function Registry.has_package(pkg_name)
return ok
end
+---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 Registry.get_system_package(pkg_name)
+ for source in Registry.system_sources:iterate() do
+ local pkg = source:get_package(pkg_name)
+ if pkg then
+ return pkg
+ end
+ end
+ log.fmt_error("Cannot find system package %q.", pkg_name)
+ error(("Cannot find system package %q."):format(pkg_name))
+end
+
function Registry.get_installed_package_names()
local fs = require "mason-core.fs"
if not fs.sync.dir_exists(InstallLocation.global():package()) then
@@ -103,49 +122,61 @@ function Registry.get_package_aliases(name)
return Registry.aliases[name] or {}
end
----@param callback? fun(success: boolean, updated_registries: RegistrySource[])
-function Registry.update(callback)
- local a = require "mason-core.async"
+---@async
+---@param sources LazySourceCollection
+local function update(sources)
local installer = require "mason-registry.installer"
+
+ if sources.install_channel then
+ log.debug "Registry update already in progress."
+ return sources.install_channel:receive():get_or_throw()
+ else
+ log.debug "Updating the registry."
+ Registry:emit("update:start", sources)
+ return installer
+ .install(sources, function(finished, all)
+ Registry:emit("update:progress", finished, all)
+ end)
+ :on_success(function(updated_registries)
+ log.fmt_debug("Successfully updated %d registries.", #updated_registries)
+ Registry:emit("update:success", updated_registries)
+ end)
+ :on_failure(function(errors)
+ log.error("Failed to update registries.", errors)
+ Registry:emit("update:failed", errors)
+ end)
+ :get_or_throw()
+ end
+end
+
+---@alias RegistryUpdateCallback fun(success: boolean, updated_registries: RegistrySource[])
+
+---@param callback? RegistryUpdateCallback
+function Registry.update_system(callback)
+ local a = require "mason-core.async"
local noop = function() end
+ a.run(update, callback or noop, Registry.system_sources)
+end
- a.run(function()
- if installer.channel then
- log.debug "Registry update already in progress."
- return installer.channel:receive():get_or_throw()
- else
- log.debug "Updating the registry."
- Registry:emit("update:start", Registry.sources)
- return installer
- .install(Registry.sources, function(finished, all)
- Registry:emit("update:progress", finished, all)
- end)
- :on_success(function(updated_registries)
- log.fmt_debug("Successfully updated %d registries.", #updated_registries)
- Registry:emit("update:success", updated_registries)
- end)
- :on_failure(function(errors)
- log.error("Failed to update registries.", errors)
- Registry:emit("update:failed", errors)
- end)
- :get_or_throw()
- end
- end, callback or noop)
+---@param callback? RegistryUpdateCallback
+function Registry.update(callback)
+ local a = require "mason-core.async"
+ local noop = function() end
+ a.run(update, callback or noop, Registry.sources)
end
local REGISTRY_STORE_TTL = 86400 -- 24 hrs
----@param callback? fun(success: boolean, updated_registries: RegistrySource[])
-function Registry.refresh(callback)
- log.debug "Refreshing the registry."
+---@param sources LazySourceCollection
+---@param callback? RegistryUpdateCallback
+local function refresh(sources, callback)
local a = require "mason-core.async"
- local installer = require "mason-registry.installer"
- local state = installer.get_registry_state()
- if state and Registry.sources:is_all_installed() then
+ local state = sources:get_install_state()
+ if state and sources:is_all_installed() then
local registry_age = os.time() - state.timestamp
- if registry_age <= REGISTRY_STORE_TTL and state.checksum == Registry.sources:checksum() then
+ if registry_age <= REGISTRY_STORE_TTL and state.checksum == sources:checksum() then
log.fmt_debug(
"Registry refresh is not necessary yet. Registry age=%d, checksum=%s",
registry_age,
@@ -158,25 +189,25 @@ function Registry.refresh(callback)
end
end
- local function async_update()
- return a.wait(function(resolve, reject)
- Registry.update(function(success, result)
- if success then
- resolve(result)
- else
- reject(result)
- end
- end)
- end)
- end
-
if not callback then
-- We don't want to error in the synchronous version because of how this function is recommended to be used in
-- 3rd party code. If accessing the update result is required, users are recommended to pass a callback.
- pcall(a.run_blocking, async_update)
+ pcall(a.run_blocking, update, sources)
else
- a.run(async_update, callback)
+ a.run(update, callback, sources)
end
end
+---@param callback? RegistryUpdateCallback
+function Registry.refresh(callback)
+ log.debug "Refreshing the registry."
+ refresh(Registry.sources, callback)
+end
+
+---@param callback? RegistryUpdateCallback
+function Registry.refresh_system(callback)
+ log.debug "Refreshing the system registry."
+ refresh(Registry.system_sources, callback)
+end
+
return Registry
diff --git a/lua/mason-registry/installer.lua b/lua/mason-registry/installer.lua
index 05592227..b832805b 100644
--- a/lua/mason-registry/installer.lua
+++ b/lua/mason-registry/installer.lua
@@ -4,30 +4,19 @@ local OneShotChannel = require("mason-core.async.control").OneShotChannel
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local fs = require "mason-core.fs"
-local path = require "mason-core.path"
local M = {}
-local STATE_FILE = path.concat { vim.fn.stdpath "cache", "mason-registry-update" }
-
---@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")
+ local state_file = sources:get_state_file()
+ 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
+ fs.sync.write_file(state_file, _.join("\n", { sources:checksum(), tostring(time) }))
end
---@async
@@ -36,8 +25,8 @@ end
---@return Result # Result<RegistrySource[]>
function M.install(sources, on_progress)
log.debug("Installing registries.", sources)
- assert(not M.channel, "Cannot install when channel is active.")
- M.channel = OneShotChannel:new()
+ assert(not sources.install_channel, "Cannot install when channel is active.")
+ sources.install_channel = OneShotChannel:new()
local finished_registries = {}
local registries = sources:to_list { include_uninstalled = true, include_synthesized = false }
@@ -69,15 +58,15 @@ function M.install(sources, on_progress)
if any_failed then
local unwrap_failures = _.compose(_.map(Result.err_or_nil), _.filter(Result.is_failure))
local result = Result.failure(unwrap_failures(results))
- M.channel:send(result)
- M.channel = nil
+ sources.install_channel:send(result)
+ sources.install_channel = nil
return result
else
local result = Result.success(_.map(Result.get_or_nil, results))
a.scheduler()
update_registry_state(sources, os.time())
- M.channel:send(result)
- M.channel = nil
+ sources.install_channel:send(result)
+ sources.install_channel = nil
return result
end
end
diff --git a/lua/mason-registry/sources/file.lua b/lua/mason-registry/sources/file.lua
index 663efaaa..f29cf2dc 100644
--- a/lua/mason-registry/sources/file.lua
+++ b/lua/mason-registry/sources/file.lua
@@ -21,12 +21,14 @@ local FileRegistrySource = {}
FileRegistrySource.__index = FileRegistrySource
---@param spec FileRegistrySourceSpec
-function FileRegistrySource:new(spec)
+---@param system boolean
+function FileRegistrySource:new(spec, system)
---@type FileRegistrySource
local instance = {}
setmetatable(instance, self)
instance.id = spec.id
instance.spec = spec
+ instance.system = system
return instance
end
diff --git a/lua/mason-registry/sources/github.lua b/lua/mason-registry/sources/github.lua
index 2b177bdd..7b650ae8 100644
--- a/lua/mason-registry/sources/github.lua
+++ b/lua/mason-registry/sources/github.lua
@@ -29,7 +29,8 @@ local GitHubRegistrySource = {}
GitHubRegistrySource.__index = GitHubRegistrySource
---@param spec GitHubRegistrySourceSpec
-function GitHubRegistrySource:new(spec)
+---@param system boolean
+function GitHubRegistrySource:new(spec, system)
---@type GitHubRegistrySource
local instance = {}
setmetatable(instance, GitHubRegistrySource)
@@ -38,6 +39,7 @@ function GitHubRegistrySource:new(spec)
instance.spec = spec
instance.repo = ("%s/%s"):format(spec.namespace, spec.name)
instance.root_dir = root_dir
+ instance.system = system
instance.data_file = path.concat { root_dir, "registry.json" }
instance.info_file = path.concat { root_dir, "info.json" }
return instance
diff --git a/lua/mason-registry/sources/init.lua b/lua/mason-registry/sources/init.lua
index 36e62ec5..b69fa8b1 100644
--- a/lua/mason-registry/sources/init.lua
+++ b/lua/mason-registry/sources/init.lua
@@ -1,7 +1,9 @@
+local _ = require "mason-core.functional"
local log = require "mason-core.log"
---@class RegistrySource
---@field id string
+---@field system boolean
---@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[]
@@ -16,42 +18,46 @@ local log = require "mason-core.log"
---@class LazySource
---@field type RegistrySourceType
---@field id string
----@field init fun(id: string): RegistrySource
+---@field init fun(id: string, system: boolean): RegistrySource
+---@field system boolean
local LazySource = {}
LazySource.__index = LazySource
---@param id string
-function LazySource.GitHub(id)
+---@param system boolean
+function LazySource.GitHub(id, system)
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 {
+ return GitHubRegistrySource:new({
id = id,
namespace = namespace,
name = name,
version = version,
- }
+ }, system)
end
---@param id string
-function LazySource.Lua(id)
+---@param system boolean
+function LazySource.Lua(id, system)
local LuaRegistrySource = require "mason-registry.sources.lua"
- return LuaRegistrySource:new {
+ return LuaRegistrySource:new({
id = id,
mod = id,
- }
+ }, system)
end
---@param id string
-function LazySource.File(id)
+---@param system boolean
+function LazySource.File(id, system)
local FileRegistrySource = require "mason-registry.sources.file"
- return FileRegistrySource:new {
+ return FileRegistrySource:new({
id = id,
path = id,
- }
+ }, system)
end
function LazySource.Synthesized()
@@ -62,17 +68,20 @@ end
---@param type RegistrySourceType
---@param id string
---@param init fun(id: string): RegistrySource
-function LazySource:new(type, id, init)
+---@param system boolean
+function LazySource:new(type, id, init, system)
+ ---@type LazySource
local instance = setmetatable({}, self)
instance.type = type
instance.id = id
instance.init = init
+ instance.system = system
return instance
end
function LazySource:get()
if not self.instance then
- self.instance = self.init(self.id)
+ self.instance = self.init(self.id, self.system)
end
return self.instance
end
@@ -105,43 +114,66 @@ local function split_once_left(str, char)
end
---@param registry_id string
-local function parse(registry_id)
+---@param system boolean
+local function parse(registry_id, system)
local type, id = split_once_left(registry_id, ":")
assert(id, ("Malformed registry %q"):format(registry_id))
if type == "github" then
- return LazySource:new(type, id, LazySource.GitHub)
+ return LazySource:new(type, id, LazySource.GitHub, system)
elseif type == "lua" then
- return LazySource:new(type, id, LazySource.Lua)
+ return LazySource:new(type, id, LazySource.Lua, system)
elseif type == "file" then
- return LazySource:new(type, id, LazySource.File)
+ return LazySource:new(type, id, LazySource.File, system)
end
error(("Unknown registry type: %s"):format(type))
end
---@class LazySourceCollection
+---@field state_file string
+---@field system boolean?
---@field list LazySource[]
---@field synthesized LazySource
+---@field install_channel OneShotChannel?
local LazySourceCollection = {}
LazySourceCollection.__index = LazySourceCollection
---@return LazySourceCollection
-function LazySourceCollection:new()
+---@param state_file string
+---@param system boolean?
+function LazySourceCollection:new(state_file, system)
+ ---@type LazySourceCollection
local instance = {}
setmetatable(instance, self)
+ instance.state_file = state_file
+ instance.system = system
instance.list = {}
instance.synthesized = LazySource:new("synthesized", "synthesized", LazySource.Synthesized)
return instance
end
+---@return { checksum: string, timestamp: integer }?
+function LazySourceCollection:get_install_state()
+ local fs = require "mason-core.fs"
+ if fs.sync.file_exists(self.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(self.state_file))
+ end
+end
+
+function LazySourceCollection:get_state_file()
+ return self.state_file
+end
+
---@param registry string
function LazySourceCollection:append(registry)
- self:unique_insert(parse(registry))
+ self:unique_insert(parse(registry, not not self.system))
return self
end
---@param registry string
function LazySourceCollection:prepend(registry)
- self:unique_insert(parse(registry), 1)
+ self:unique_insert(parse(registry, not not self.system), 1)
return self
end
diff --git a/lua/mason-registry/sources/lua.lua b/lua/mason-registry/sources/lua.lua
index 40e728b6..273655c0 100644
--- a/lua/mason-registry/sources/lua.lua
+++ b/lua/mason-registry/sources/lua.lua
@@ -13,12 +13,14 @@ local LuaRegistrySource = {}
LuaRegistrySource.__index = LuaRegistrySource
---@param spec LuaRegistrySourceSpec
-function LuaRegistrySource:new(spec)
+---@param system boolean
+function LuaRegistrySource:new(spec, system)
---@type LuaRegistrySource
local instance = {}
setmetatable(instance, LuaRegistrySource)
instance.id = spec.id
instance.spec = spec
+ instance.system = system
return instance
end
diff --git a/lua/mason-registry/sources/synthesized.lua b/lua/mason-registry/sources/synthesized.lua
index 8cad635e..2a6cd281 100644
--- a/lua/mason-registry/sources/synthesized.lua
+++ b/lua/mason-registry/sources/synthesized.lua
@@ -5,6 +5,7 @@ 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"
+local path = require "mason-core.path"
---@class SynthesizedRegistrySource : RegistrySource
---@field buffer table<string, Package>
@@ -68,14 +69,20 @@ 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)
+ local location = InstallLocation.global()
+ local receipt_paths = {
+ path.concat { location:package(pkg_name), "mason-receipt.json" },
+ path.concat { location:system_package(pkg_name), "mason-receipt.json" },
+ }
+ for _, receipt_path in ipairs(receipt_paths) do
+ 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
end
diff --git a/lua/mason-registry/sources/util.lua b/lua/mason-registry/sources/util.lua
index 9efa1420..e37dabfe 100644
--- a/lua/mason-registry/sources/util.lua
+++ b/lua/mason-registry/sources/util.lua
@@ -30,6 +30,10 @@ M.hydrate_package = _.curryN(function(registry, buffer, spec)
local _ = Package.License[lang]
end, spec.licenses)
+ if registry.system then
+ spec = _.assoc("system", true, spec)
+ end
+
local existing_instance = buffer[spec.name]
if existing_instance then
-- Apply spec to the existing Package instances. This is important as to not have lingering package instances.
diff --git a/lua/mason/init.lua b/lua/mason/init.lua
index 9c507f5c..043fb651 100644
--- a/lua/mason/init.lua
+++ b/lua/mason/init.lua
@@ -26,6 +26,9 @@ function M.setup(config)
for _, registry in ipairs(settings.current.registries) do
Registry.sources:append(registry)
end
+ for _, registry in ipairs(settings.current.system_registries) do
+ Registry.system_sources:append(registry)
+ end
require "mason.api.command"
setup_autocmds()
diff --git a/lua/mason/settings.lua b/lua/mason/settings.lua
index e70d90a4..1788c035 100644
--- a/lua/mason/settings.lua
+++ b/lua/mason/settings.lua
@@ -34,6 +34,14 @@ local DEFAULT_SETTINGS = {
"github:mason-org/mason-registry",
},
+ ---@since 2.3.0
+ -- [Advanced setting]
+ -- The registries to source system packages from. Accepts multiple entries. Should a package with the same name exist in
+ -- multiple registries, the registry listed first will be used.
+ system_registries = {
+ "github:mason-org/mason-system-registry",
+ },
+
---@since 1.0.0
-- The provider implementations to use for resolving supplementary package metadata (e.g., all available versions).
-- Accepts multiple entries, where later entries will be used as fallback should prior providers fail.