1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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
|