diff options
| author | William Boman <william@redwill.se> | 2023-03-14 04:48:03 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-14 04:48:03 +0100 |
| commit | bf087883b05082a07ec2abe22e904b227b21f0d3 (patch) | |
| tree | 2a024f842fb0be4ce91f325af08ecc0028299a6f | |
| parent | fix(sources): also set .desc property when updating spec (#1095) (diff) | |
| download | mason-bf087883b05082a07ec2abe22e904b227b21f0d3.tar mason-bf087883b05082a07ec2abe22e904b227b21f0d3.tar.gz mason-bf087883b05082a07ec2abe22e904b227b21f0d3.tar.bz2 mason-bf087883b05082a07ec2abe22e904b227b21f0d3.tar.lz mason-bf087883b05082a07ec2abe22e904b227b21f0d3.tar.xz mason-bf087883b05082a07ec2abe22e904b227b21f0d3.tar.zst mason-bf087883b05082a07ec2abe22e904b227b21f0d3.zip | |
feat: add registry.refresh() method (#1096)
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | doc/mason.txt | 64 | ||||
| -rw-r--r-- | lua/mason-core/async/init.lua | 3 | ||||
| -rw-r--r-- | lua/mason-core/installer/registry/init.lua | 2 | ||||
| -rw-r--r-- | lua/mason-registry/init.lua | 82 | ||||
| -rw-r--r-- | lua/mason-registry/sources/init.lua | 23 | ||||
| -rw-r--r-- | lua/mason/api/command.lua | 2 | ||||
| -rw-r--r-- | lua/mason/ui/init.lua | 1 | ||||
| -rw-r--r-- | tests/mason/api/command_spec.lua | 41 |
9 files changed, 204 insertions, 17 deletions
@@ -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 diff --git a/doc/mason.txt b/doc/mason.txt index 1c2ba506..19eba5cd 100644 --- a/doc/mason.txt +++ b/doc/mason.txt @@ -124,6 +124,26 @@ 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* You may install packages either via the command interface or via Mason's Lua @@ -170,6 +190,13 @@ 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 :MasonInstall <package> ... @@ -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 diff --git a/lua/mason-core/async/init.lua b/lua/mason-core/async/init.lua index 3c88eef0..0b327218 100644 --- a/lua/mason-core/async/init.lua +++ b/lua/mason-core/async/init.lua @@ -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 diff --git a/lua/mason-core/installer/registry/init.lua b/lua/mason-core/installer/registry/init.lua index 5968987b..f03f3e31 100644 --- a/lua/mason-core/installer/registry/init.lua +++ b/lua/mason-core/installer/registry/init.lua @@ -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 = {} @@ -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 diff --git a/lua/mason-registry/init.lua b/lua/mason-registry/init.lua index 4568c54d..19a24d60 100644 --- a/lua/mason-registry/init.lua +++ b/lua/mason-registry/init.lua @@ -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 @@ -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 "cache", "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 @@ -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 diff --git a/lua/mason-registry/sources/init.lua b/lua/mason-registry/sources/init.lua index 0e5e0d29..0739f40b 100644 --- a/lua/mason-registry/sources/init.lua +++ b/lua/mason-registry/sources/init.lua @@ -1,3 +1,5 @@ +local _ = require "mason-core.functional" + local M = {} ---@param registry_id string @@ -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 diff --git a/lua/mason/api/command.lua b/lua/mason/api/command.lua index fadb9ac4..4be13d86 100644 --- a/lua/mason/api/command.lua +++ b/lua/mason/api/command.lua @@ -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") diff --git a/lua/mason/ui/init.lua b/lua/mason/ui/init.lua index bbe534ad..340434eb 100644 --- a/lua/mason/ui/init.lua +++ b/lua/mason/ui/init.lua @@ -7,6 +7,7 @@ end function M.open() local api = require "mason.ui.instance" + require("mason-registry").refresh(function() end) api.window.open() end diff --git a/tests/mason/api/command_spec.lua b/tests/mason/api/command_spec.lua index aee08f6f..7b77bae3 100644 --- a/tests/mason/api/command_spec.lua +++ b/tests/mason/api/command_spec.lua @@ -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" @@ -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) |
