diff options
| author | William Boman <william@redwill.se> | 2023-11-07 00:29:18 +0100 |
|---|---|---|
| committer | William Boman <william@redwill.se> | 2025-02-19 12:15:48 +0100 |
| commit | 6a7662760c515c74f2c37fc825776ead65d307f9 (patch) | |
| tree | 0f4496d0678c7029b10236cbf48cc0f5ff63c1dc /lua/mason-core/package/init.lua | |
| parent | fix(pypi): remove -U flag and fix log message (diff) | |
| download | mason-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.lua | 182 |
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 |
