aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/package/init.lua
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2023-11-07 00:29:18 +0100
committerWilliam Boman <william@redwill.se>2025-02-19 12:15:48 +0100
commit6a7662760c515c74f2c37fc825776ead65d307f9 (patch)
tree0f4496d0678c7029b10236cbf48cc0f5ff63c1dc /lua/mason-core/package/init.lua
parentfix(pypi): remove -U flag and fix log message (diff)
downloadmason-6a7662760c515c74f2c37fc825776ead65d307f9.tar
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.gz
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.bz2
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.lz
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.xz
mason-6a7662760c515c74f2c37fc825776ead65d307f9.tar.zst
mason-6a7662760c515c74f2c37fc825776ead65d307f9.zip
refactor!: change Package API
This changes the following public APIs: **(_breaking_) Events on the `Package` class** The `uninstall:success` event on the `Package` class now receives an `InstallReceipt` as argument, instead of an `InstallHandle`. This receipt is an in-memory representation of what was uninstalled. There's also a new `uninstall:failed` event for situations where uninstallation for some reason fails. Note: this also applies to the registry events (i.e. `package:uninstall:success` and `package:uninstall:failed`). --- **(_breaking_) `Package:uninstall()` is now asynchronous and receives two new arguments, similarly to `Package:install()`** While package uninstallations remain synchronous under the hood, the public API has been changed from synchronous -> asynchronous. Users of this method are recommended to provide a callback in situations where code needs to execute after uninstallation fully completes. --- **(_breaking_) `Package:get_install_path()` has been removed. --- **`Package:install()` now takes an optional callback** This callback allows consumers to be informed whether installation was successful or not without having to go through a different, low-level, API. See below for a comparison between the old and new APIs: ```lua -- before local handle = pkg:install() handle:once("closed", function () -- ... end) -- after pkg:install({}, function (success, result) -- ... end) ```
Diffstat (limited to 'lua/mason-core/package/init.lua')
-rw-r--r--lua/mason-core/package/init.lua182
1 files changed, 182 insertions, 0 deletions
diff --git a/lua/mason-core/package/init.lua b/lua/mason-core/package/init.lua
new file mode 100644
index 00000000..09b0ebbf
--- /dev/null
+++ b/lua/mason-core/package/init.lua
@@ -0,0 +1,182 @@
+local AbstractPackage = require "mason-core.package.AbstractPackage"
+local InstallLocation = require "mason-core.installer.InstallLocation"
+local InstallRunner = require "mason-core.installer.InstallRunner"
+local Optional = require "mason-core.optional"
+local Result = require "mason-core.result"
+local UninstallRunner = require "mason-core.installer.UninstallRunner"
+local _ = require "mason-core.functional"
+local fs = require "mason-core.fs"
+local path = require "mason-core.path"
+local registry = require "mason-registry"
+local platform = require "mason-core.platform"
+local Semaphore = require("mason-core.async.control").Semaphore
+
+---@class Package : AbstractPackage
+---@field spec RegistryPackageSpec
+---@field local_semaphore Semaphore
+local Package = {}
+Package.__index = Package
+setmetatable(Package, { __index = AbstractPackage })
+
+---@param package_identifier string
+---@return string, string?
+Package.Parse = function(package_identifier)
+ local name, version = unpack(vim.split(package_identifier, "@"))
+ return name, version
+end
+
+---@alias PackageLanguage string
+
+---@type table<PackageLanguage, PackageLanguage>
+Package.Lang = setmetatable({}, {
+ __index = function(s, lang)
+ s[lang] = lang
+ return s[lang]
+ end,
+})
+
+---@enum PackageCategory
+Package.Cat = {
+ Compiler = "Compiler",
+ Runtime = "Runtime",
+ DAP = "DAP",
+ LSP = "LSP",
+ Linter = "Linter",
+ Formatter = "Formatter",
+}
+
+---@alias PackageLicense string
+
+---@type table<PackageLicense, PackageLicense>
+Package.License = setmetatable({}, {
+ __index = function(s, license)
+ s[license] = license
+ return s[license]
+ end,
+})
+
+---@class RegistryPackageSourceVersionOverride : RegistryPackageSource
+---@field constraint string
+
+---@class RegistryPackageSource
+---@field id string PURL-compliant identifier.
+---@field version_overrides? RegistryPackageSourceVersionOverride[]
+
+---@class RegistryPackageSchemas
+---@field lsp string?
+
+---@class RegistryPackageDeprecation
+---@field since string
+---@field message string
+
+---@alias RegistryPackageSpecSchema
+--- | '"registry+v1"'
+
+---@class RegistryPackageSpec
+---@field schema RegistryPackageSpecSchema
+---@field name string
+---@field description string
+---@field homepage string
+---@field licenses string[]
+---@field languages string[]
+---@field categories string[]
+---@field deprecation RegistryPackageDeprecation?
+---@field source RegistryPackageSource
+---@field schemas RegistryPackageSchemas?
+---@field bin table<string, string>?
+---@field share table<string, string>?
+---@field opt table<string, string>?
+
+---@param spec RegistryPackageSpec
+local function validate_spec(spec)
+ if platform.cached_features["nvim-0.11"] ~= 1 then
+ return
+ end
+ vim.validate("schema", spec.schema, _.equals "registry+v1", "registry+v1")
+ vim.validate("name", spec.name, "string")
+ vim.validate("description", spec.description, "string")
+ vim.validate("homepage", spec.homepage, "string")
+ vim.validate("licenses", spec.licenses, "table")
+ vim.validate("categories", spec.categories, "table")
+ vim.validate("languages", spec.languages, "table")
+ vim.validate("source", spec.source, "table")
+ vim.validate("bin", spec.bin, { "table", "nil" })
+ vim.validate("share", spec.share, { "table", "nil" })
+end
+
+---@param spec RegistryPackageSpec
+function Package:new(spec)
+ validate_spec(spec)
+ ---@type Package
+ local instance = AbstractPackage.new(self, spec)
+ instance.local_semaphore = Semaphore:new(1)
+ return instance
+end
+
+---@param opts? PackageInstallOpts
+---@param callback? InstallRunnerCallback
+---@return InstallHandle
+function Package:install(opts, callback)
+ opts = opts or {}
+ assert(not self:is_installing(), "Package is already installing.")
+ assert(not self:is_uninstalling(), "Package is uninstalling.")
+ opts = vim.tbl_extend("force", self.DEFAULT_INSTALL_OPTS, opts or {})
+
+ local handle = self:new_install_handle(opts.location)
+ registry:emit("package:install:handle", handle)
+ local runner = InstallRunner:new(handle, AbstractPackage.SEMAPHORE)
+
+ runner:execute(opts, callback)
+
+ return handle
+end
+
+---@param opts? PackageUninstallOpts
+---@param callback? fun(success: boolean, error: any)
+function Package:uninstall(opts, callback)
+ opts = opts or {}
+ assert(self:is_installed(opts.location), "Package is not installed.")
+ assert(not self:is_uninstalling(), "Package is already uninstalling.")
+ local handle = self:new_uninstall_handle(opts.location)
+ registry:emit("package:uninstall:handle", handle)
+ local runner = UninstallRunner:new(handle, AbstractPackage.SEMAPHORE)
+ runner:execute(opts, callback)
+ return handle
+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))
+ if not ok or not stat then
+ return false
+ end
+ return stat.type == "directory"
+end
+
+function Package:get_lsp_settings_schema()
+ local schema_file = InstallLocation.global()
+ :share(path.concat { "mason-schemas", "lsp", ("%s.json"):format(self.name) })
+ if fs.sync.file_exists(schema_file) then
+ return Result.pcall(vim.json.decode, fs.sync.read_file(schema_file), {
+ luanil = { object = true, array = true },
+ }):ok()
+ end
+ return Optional.empty()
+end
+
+function Package:get_aliases()
+ return require("mason-registry").get_package_aliases(self.name)
+end
+
+---@async
+---@private
+function Package:acquire_permit()
+ return self.local_semaphore:acquire()
+end
+
+function Package:__tostring()
+ return ("Package(name=%s)"):format(self.name)
+end
+
+return Package