Skip to content

Commit

Permalink
feat: add registry.refresh() method
Browse files Browse the repository at this point in the history
  • Loading branch information
williamboman committed Mar 14, 2023
1 parent 63988d8 commit b58b1ea
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ Refer to the [Wiki](https://github.com/williamboman/mason.nvim/wiki/Extensions)
> `:h mason-commands`
- `:Mason` - opens a graphical status window
- `:MasonInstall <package> ...` - installs/reinstalls the provided packages
- `:MasonUpdate` - updates all managed registries
- `:MasonInstall <package> ...` - installs/re-installs the provided packages
- `:MasonUninstall <package> ...` - uninstalls the provided packages
- `:MasonUninstallAll` - uninstalls all packages
- `:MasonLog` - opens the `mason.nvim` log file in a new tab window
Expand Down
64 changes: 64 additions & 0 deletions doc/mason.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,26 @@ packages, expand package information, and more. The UI comes with a set of
keybinds which you may find in the help view by pressing `g?` when the Mason
window is open.

==============================================================================
REGISTRIES *mason-registries*

`mason.nvim` sources package definitions from the registries it has been
configured with (see |mason-settings|). `mason.nvim` uses the core registry,
governed by `mason.nvim`, by default. This may be extended, or even entirely
overridden, through additional registries, like so:

>lua
require("mason").setup {
registries = {
"lua:my-registry",
"github:mason-org/mason-registry",
},
}
<

Packages are loaded from registries in the order they've been configured,
with registries appearing first in the list having precedence.

==============================================================================
HOW TO INSTALL PACKAGES *mason-how-to-install-packages*

Expand Down Expand Up @@ -169,6 +189,13 @@ packages, expand package information, and more. The UI comes with a set of
keybinds which you may find in the help view by pressing `g?` when the Mason
window is open.

------------------------------------------------------------------------------
UPDATE REGISTRIES *:MasonUpdate*
>vim
:MasonUpdate
<
Updates all managed registries.

------------------------------------------------------------------------------
INSTALLING PACKAGES *:MasonInstall*
>vim
Expand Down Expand Up @@ -471,5 +498,42 @@ get_all_package_names()
Returns:
string[]

*mason-registry.update()*
update({callback})
Updates all managed registries.

Parameters:
{callback} - Callback of the signature `fun(success: boolean, updated_registries: RegistrySource[])`

*mason-registry.update()*
refresh({callback?})
Refreshes all registries if needed. This is a convenience wrapper around
|mason-registry.update()| that only updates registries if:
1) registries haven't been updated in a while
2) or, one or more registries are not installed

Runs in a blocking fashion if no {callback} is provided. Note that when
running in blocking fashion the entire editor is frozen, so prefer the
asynchronous variant unless absolutely needed.

Parameters:
{callback?} (optional) - Invoked when the registry has been refreshed.

Example:
>lua
local registry = require("mason-registry")

-- 1. synchronous
registry.refresh()
local packages = registry.get_all_packages()
...

-- 2. asynchronous
registry.refresh(function ()
local packages = registry.get_all_packages()
...
end)
<


vim:tw=78:ft=help:norl:expandtab:sw=4
3 changes: 2 additions & 1 deletion lua/mason-core/async/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ exports.run_blocking = function(suspend_fn, ...)
end, ...)

if
vim.wait(60 * 60 * 1000, function() -- the wait time is completely arbitrary
resolved
or vim.wait(60 * 60 * 1000, function() -- the wait time is completely arbitrary
return resolved == true
end, 50)
then
Expand Down
2 changes: 1 addition & 1 deletion lua/mason-core/installer/registry/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ local _ = require "mason-core.functional"
local a = require "mason-core.async"
local link = require "mason-core.installer.registry.link"
local log = require "mason-core.log"
local semver = require "mason-core.semver"

local M = {}

Expand Down Expand Up @@ -71,6 +70,7 @@ local function coalesce_source(source, version)
local version_override = source.version_overrides[i]
local version_type, constraint = unpack(_.split(":", version_override.constraint))
if version_type == "semver" then
local semver = require "mason-core.semver"
local version_match = Result.try(function(try)
local requested_version = try(semver.parse(version))
if _.starts_with("<=", constraint) then
Expand Down
82 changes: 68 additions & 14 deletions lua/mason-registry/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local path = require "mason-core.path"
local sources = require "mason-registry.sources"

---@class RegistrySource
---@field id string
---@field get_package fun(self: RegistrySource, pkg_name: string): Package?
---@field get_all_package_names fun(self: RegistrySource): string[]
---@field get_display_name fun(self: RegistrySource): string
Expand Down Expand Up @@ -118,12 +119,33 @@ function M.get_all_packages()
return get_packages(M.get_all_package_names())
end

---@param cb fun(success: boolean, err: any?)
function M.update(cb)
local STATE_FILE = path.concat { vim.fn.stdpath "state", "mason-registry-update" }

---@param time integer
local function get_store_age(time)
local checksum = sources.checksum()
if fs.sync.file_exists(STATE_FILE) then
local parse_state_file =
_.compose(_.evolve { timestamp = tonumber }, _.zip_table { "checksum", "timestamp" }, _.split "\n")
local state = parse_state_file(fs.sync.read_file(STATE_FILE))
if checksum == state.checksum then
return math.abs(time - state.timestamp)
end
end
return time
end

---@async
---@param time integer
local function update_store_timestamp(time)
fs.async.write_file(STATE_FILE, _.join("\n", { sources.checksum(), tostring(time) }))
end

---@param callback? fun(success: boolean, updated_registries: RegistrySource[])
function M.update(callback)
local a = require "mason-core.async"
local Result = require "mason-core.result"

a.run(function()
return a.run(function()
return Result.try(function(try)
local updated_sources = {}
for source in sources.iter { include_uninstalled = true } do
Expand All @@ -135,23 +157,55 @@ function M.update(cb)
end)
end
return updated_sources
end):on_success(function(updated_sources)
if #updated_sources > 0 then
M:emit("update", updated_sources)
end
end)
end, function(success, sources_or_err)
if not success then
cb(success, sources_or_err)
end, function(success, result)
if not callback then
return
end
sources_or_err
:on_success(function(updated_sources)
if #updated_sources > 0 then
M:emit("update", updated_sources)
end
cb(true, updated_sources)
if not success then
return callback(false, result)
end
result
:on_success(function(value)
callback(true, value)
end)
:on_failure(function(err)
cb(false, err)
callback(false, err)
end)
end)
end

local REGISTRY_STORE_TTL = 86400 -- 24 hrs

---@param cb? fun()
function M.refresh(cb)
local a = require "mason-core.async"

---@async
local function refresh()
if vim.in_fast_event() then
a.scheduler()
end
local is_outdated = get_store_age(os.time()) > REGISTRY_STORE_TTL
if is_outdated or not sources.is_installed() then
if a.wait(M.update) then
if vim.in_fast_event() then
a.scheduler()
end
update_store_timestamp(os.time())
end
end
end

if not cb then
a.run_blocking(refresh)
else
a.run(refresh, cb)
end
end

return M
23 changes: 23 additions & 0 deletions lua/mason-registry/sources/init.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local _ = require "mason-core.functional"

local M = {}

---@param registry_id string
Expand Down Expand Up @@ -72,4 +74,25 @@ function M.iter(opts)
end
end

---@return boolean #Returns true if all sources are installed.
function M.is_installed()
for source in M.iter { include_uninstalled = true } do
if not source:is_installed() then
return false
end
end
return true
end

---@return string # The sha256 checksum of the currently registered sources.
function M.checksum()
---@type string[]
local registry_ids = {}
for source in M.iter { include_uninstalled = true } do
table.insert(registry_ids, source.id)
end
local checksum = _.compose(vim.fn.sha256, _.join "", _.sort_by(_.identity))
return checksum(registry_ids)
end

return M
2 changes: 2 additions & 0 deletions lua/mason/api/command.lua
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,14 @@ vim.api.nvim_create_user_command("MasonLog", MasonLog, {
_G.mason_completion = {
available_package_completion = function()
local registry = require "mason-registry"
registry.refresh()
local package_names = registry.get_all_package_names()
table.sort(package_names)
return table.concat(package_names, "\n")
end,
installed_package_completion = function()
local registry = require "mason-registry"
registry.refresh()
local package_names = registry.get_installed_package_names()
table.sort(package_names)
return table.concat(package_names, "\n")
Expand Down
1 change: 1 addition & 0 deletions lua/mason/ui/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ end

function M.open()
local api = require "mason.ui.instance"
require("mason-registry").refresh(function() end)
api.window.open()
end

Expand Down
41 changes: 41 additions & 0 deletions tests/mason/api/command_spec.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local log = require "mason-core.log"
local match = require "luassert.match"
local spy = require "luassert.spy"
local stub = require "luassert.stub"

local Pkg = require "mason-core.package"
local a = require "mason-core.async"
Expand Down Expand Up @@ -88,3 +89,43 @@ describe(":MasonLog", function()
end)
end)
end)

describe(":MasonUpdate", function()
it(
"should update registries",
async_test(function()
stub(registry, "update", function(cb)
cb(true, { {} })
end)
spy.on(vim, "notify")
api.MasonUpdate()
assert.spy(vim.notify).was_called(1)
assert.spy(vim.notify).was_called_with("Updating registries…", vim.log.levels.INFO, {
title = "mason.nvim",
})
a.scheduler()
assert.spy(vim.notify).was_called_with("Successfully updated 1 registry.", vim.log.levels.INFO, {
title = "mason.nvim",
})
end)
)

it(
"should notify errors",
async_test(function()
stub(registry, "update", function(cb)
cb(false, "Some error.")
end)
spy.on(vim, "notify")
api.MasonUpdate()
assert.spy(vim.notify).was_called(1)
assert.spy(vim.notify).was_called_with("Updating registries…", vim.log.levels.INFO, {
title = "mason.nvim",
})
a.scheduler()
assert.spy(vim.notify).was_called_with("Failed to update registries: Some error.", vim.log.levels.ERROR, {
title = "mason.nvim",
})
end)
)
end)

0 comments on commit b58b1ea

Please sign in to comment.