Skip to content

Commit

Permalink
feat(update): :Rocks update {rock} alias for individual rocks
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb committed Jul 9, 2024
1 parent 027fe81 commit 7bf5331
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 44 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ Arguments:

- `rock`: The luarocks package.
- `version`: Optional. Used to pin a rock to a specific version.
If omitted, rocks.nvim will install (or update to) the latest version.
- `args[]`: Optional arguments, e.g. `opt=true`, to prevent rocks.nvim
from automatically sourcing a rock at startup.

Expand Down Expand Up @@ -345,8 +346,9 @@ Examples:
- Running the `:Rocks update` command will update every available rock
that is not pinned.

- `:Rocks install {rock}` (without a version) will update `{rock}`
to the latest version.
- `:Rocks update {rock}` will update `{rock}` to the latest version.
The command provides completions for outdated luarocks packages
and `scm`/`dev` rocks.

### Syncing rocks

Expand Down
10 changes: 7 additions & 3 deletions doc/rocks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,24 @@ rocks.nvim commands *rocks-commands*

install {rock} {version?} {args[]?} Install {rock} with optional {version} and optional {args[]}.
Example: ':Rocks install neorg 8.0.0 opt=true'
Will install or update to the latest version if called
without {version}.
args (optional):
- opt={true|false}
Rocks that have been installed with 'opt=true'
can be sourced with |packadd|.
- pin={true|false}
Rocks that have been installed with 'pim=true'
will be ignored by ':Rocks update'.
Use 'Rocks! install ...' to skip prompts
Use 'Rocks! install ...' to skip prompts.
prune {rock} Uninstall {rock} and its stale dependencies,
and remove it from rocks.toml.
sync Synchronize installed rocks with rocks.toml.
It may take more than one sync to prune all rocks that can be pruned.
update Search for updated rocks and install them.
Use 'Rocks! update` to skip prompts
update {rock?} Search for updated rocks and install them.
If called with the optional {rock} argument, only {rock}
will be updated.
Use 'Rocks! update` to skip prompts.
with breaking changes.
edit Edit the rocks.toml file.
pin {rock} Pin {rock} to the installed version.
Expand Down
68 changes: 50 additions & 18 deletions lua/rocks/cache.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,37 @@ local state = require("rocks.state")
local constants = require("rocks.constants")
local nio = require("nio")

---@type { [rock_name]: Rock[] } | nil
---@type table<rock_name, Rock[]> | nil
local _cached_rocks = nil

---@type { [rock_name]: Rock[] } | nil
---@type table<rock_name, Rock[]> | nil
local _cached_dev_binaries = nil

---Used for completions only
---@type string[] | nil
local _removable_rock_cache = nil

---@type table<rock_name, OutdatedRock[]> | nil
local _outdated_rock_cache = nil

---Tries to get the cached value
---Returns an empty list if the cache is not ready,
---and triggers an async task to populate the cache.
---WARNING: `cache_ref` MUST be populated by `populate`
---
---@generic T
---@param cache_ref T
---@param populate async fun():T Stateul
---@return table<string, T> result indexed by name
local function try_get_unsafe(cache_ref, populate)
if not cache_ref then
nio.run(populate)
local result = vim.empty_dict()
return result
end
return cache_ref
end

---Query luarocks packages and populate the cache.
---@type async fun()
cache.populate_cached_rocks = nio.create(function()
Expand All @@ -50,15 +71,9 @@ end)
---Tries to get the cached rocks.
---Returns an empty list if the cache is not ready,
---and triggers an async task to populate the cache.
---@return { [string]: Rock[] } rocks indexed by name
---@return table<string, Rock[]> rocks indexed by name
function cache.try_get_rocks()
if not _cached_rocks then
nio.run(cache.populate_cached_rocks)
local rocks = vim.empty_dict()
---@cast rocks { [string]: Rock[] }
return rocks
end
return _cached_rocks
return try_get_unsafe(_cached_rocks, cache.populate_cached_rocks)
end

---Query the state for rocks that can be removed
Expand All @@ -74,22 +89,39 @@ end)
---Tries to get the cached removable rocks.
---Returns an empty list if the cache is not ready,
---and triggers an async task to populate the cache.
---@return { [string]: Rock[] } rocks indexed by name
---@return table<string, Rock[]> rocks indexed by name
function cache.try_get_removable_rocks()
if not _removable_rock_cache then
nio.run(cache.populate_removable_rock_cache)
local rocks = vim.empty_dict()
---@cast rocks { [string]: Rock[] }
return rocks
end
return _removable_rock_cache
return try_get_unsafe(_removable_rock_cache, cache.populate_removable_rock_cache)
end

---Invalidate the removable rocks cache
cache.invalidate_removable_rocks = function()
_removable_rock_cache = nil
end

---Query the state for rocks that can be removed
---and populate the cache.
---@type async fun()
cache.populate_outdated_rock_cache = nio.create(function()
if _outdated_rock_cache then
return
end
_outdated_rock_cache = state.outdated_rocks()
end)

---Populate all rocks state caches
cache.populate_all_rocks_state_caches = nio.create(function()
cache.populate_removable_rock_cache()
cache.populate_outdated_rock_cache()
end)
---Tries to get the cached removable rocks.
---Returns an empty list if the cache is not ready,
---and triggers an async task to populate the cache.
---@return table<string, Rock[]> rocks indexed by name
function cache.try_get_outdated_rocks()
return try_get_unsafe(_outdated_rock_cache, cache.populate_outdated_rock_cache)
end

---Search the cache for rocks-binaries-dev rocks.
---Repopulates the cache and runs a second search if not found
---@type async fun(rock_name: string, version: string?)
Expand Down
46 changes: 39 additions & 7 deletions lua/rocks/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@
---
--- install {rock} {version?} {args[]?} Install {rock} with optional {version} and optional {args[]}.
--- Example: ':Rocks install neorg 8.0.0 opt=true'
--- Will install or update to the latest version if called
--- without {version}.
--- args (optional):
--- - opt={true|false}
--- Rocks that have been installed with 'opt=true'
--- can be sourced with |packadd|.
--- - pin={true|false}
--- Rocks that have been installed with 'pim=true'
--- will be ignored by ':Rocks update'.
--- Use 'Rocks! install ...' to skip prompts
--- Use 'Rocks! install ...' to skip prompts.
--- prune {rock} Uninstall {rock} and its stale dependencies,
--- and remove it from rocks.toml.
--- sync Synchronize installed rocks with rocks.toml.
--- It may take more than one sync to prune all rocks that can be pruned.
--- update Search for updated rocks and install them.
--- Use 'Rocks! update` to skip prompts
--- update {rock?} Search for updated rocks and install them.
--- If called with the optional {rock} argument, only {rock}
--- will be updated.
--- Use 'Rocks! update` to skip prompts.
--- with breaking changes.
--- edit Edit the rocks.toml file.
--- pin {rock} Pin {rock} to the installed version.
Expand Down Expand Up @@ -122,10 +126,36 @@ end
---@type { [string]: RocksCmd }
local rocks_command_tbl = {
update = {
impl = function(_, opts)
require("rocks.operations").update(nil, {
skip_prompts = opts.bang,
})
impl = function(args, opts)
if #args == 0 then
require("rocks.operations").update(nil, {
skip_prompts = opts.bang,
})
elseif #args == 1 then
local rock_name = args[1]
local user_rocks = config.get_user_rocks()
local rock = user_rocks[rock_name]
if not rock then
vim.notify(("Rocks update: %s is not installed"):format(rock_name), vim.log.levels.ERROR)
return
elseif rock.version == "dev" or rock.version == "scm" then
-- Skip "rock not found" prompt
table.insert(args, rock.version)
end
require("rocks.operations").add(args, nil, {
skip_prompts = opts.bang,
cmd = "update",
})
else
vim.notify("Rocks update: Too many arguments: " .. vim.inspect(args), vim.log.levels.ERROR)
end
end,
complete = function(query)
local outdated_rocks = cache.try_get_outdated_rocks()
---@param spec RockSpec
return fuzzy_filter_user_rocks(function(spec)
return outdated_rocks[spec.name] ~= nil or spec.version == "dev" or spec.version == "scm"
end, query)
end,
},
sync = {
Expand Down Expand Up @@ -193,6 +223,7 @@ local rocks_command_tbl = {
require("rocks.operations").pin(rock_name)
end,
complete = function(query)
---@param spec RockSpec
return fuzzy_filter_user_rocks(function(spec)
---@cast spec RockSpec
return not spec.pin
Expand All @@ -209,6 +240,7 @@ local rocks_command_tbl = {
require("rocks.operations").unpin(rock_name)
end,
complete = function(query)
---@param spec RockSpec
return fuzzy_filter_user_rocks(function(spec)
---@cast spec RockSpec
return spec.pin
Expand Down
34 changes: 22 additions & 12 deletions lua/rocks/operations/add.lua
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,18 @@ end

---@class rocks.AddOpts
---@field skip_prompts? boolean Whether to skip any "search 'dev' manifest prompts
---@field cmd? 'install' | 'update' Command used to invoke this function. Default: `'install'`

--- Adds a new rock and updates the `rocks.toml` file
---@param arg_list string[] #Argument list, potentially used by external handlers. The first argument is the package, e.g. the rock name
---@param callback? fun(rock: Rock)
---@param opts? rocks.AddOpts
add.add = function(arg_list, callback, opts)
opts = opts or {}
opts.cmd = opts.cmd or "install"
local is_install = opts.cmd == "install"
local progress_handle = progress.handle.create({
title = "Installing",
title = is_install and "Installing" or "Updating",
lsp_client = { name = constants.ROCKS_NVIM },
})
local function report_error(message)
Expand Down Expand Up @@ -102,10 +105,14 @@ add.add = function(arg_list, callback, opts)
---@type rock_name
local rock_name = arg_list[1]:lower()
if #(vim.split(rock_name, "/")) ~= 1 then
local message = [[
'Rocks install' does not support {owner/repo} for luarocks packages.
Use 'Rocks install {rock_name}' or install rocks-git.nvim.
]]
local message = string.format(
[[
'Rocks %s' does not support {owner/repo} for luarocks packages.
Use 'Rocks %s {rock_name}' or install rocks-git.nvim.
]],
opts.cmd,
opts.cmd
)
report_error(message)
return
end
Expand All @@ -114,19 +121,19 @@ Use 'Rocks install {rock_name}' or install rocks-git.nvim.
local args = #arg_list == 1 and {} or { unpack(arg_list, 2, #arg_list) }
local parse_result = parser.parse_install_args(args)
if not vim.tbl_isempty(parse_result.invalid_args) then
report_error(("invalid install args: %s"):format(vim.inspect(parse_result.invalid_args)))
report_error(("invalid %s args: %s"):format(opts.cmd, vim.inspect(parse_result.invalid_args)))
return
end
if not vim.tbl_isempty(parse_result.conflicting_args) then
report_error(("conflicting install args: %s"):format(vim.inspect(parse_result.conflicting_args)))
report_error(("conflicting %s args: %s"):format(opts.cmd, vim.inspect(parse_result.conflicting_args)))
return
end
local install_spec = parse_result.spec
local version = install_spec.version
local breaking_change = not version and helpers.get_breaking_change(rock_name)
if breaking_change and not helpers.prompt_for_breaking_intall(breaking_change) then
progress_handle:report({
title = "Installation aborted",
title = string.format("%s aborted", is_install and "Installation" or "Update"),
})
progress_handle:cancel()
end
Expand All @@ -144,13 +151,16 @@ Use 'Rocks install {rock_name}' or install rocks-git.nvim.
local stderr = installed_rock
---@cast stderr string
local not_found = stderr:match("No results matching query were found") ~= nil
local message = ("Installation of %s failed. Run ':Rocks log' for details."):format(rock_name)
local message = ("%s %s failed. Run ':Rocks log' for details."):format(
is_install and "Installing" or "Updating",
rock_name
)
if not_found then
message = ("Could not find %s %s"):format(rock_name, version or "")
end
nio.scheduler()
progress_handle:report({
title = "Installation failed",
title = ("%s failed"):format(is_install and "Installation" or "Update"),
message = message,
})
if not_found then
Expand All @@ -163,7 +173,7 @@ Use 'Rocks install {rock_name}' or install rocks-git.nvim.
---@cast installed_rock Rock
nio.scheduler()
progress_handle:report({
title = "Installation successful",
title = ("%s successful"):format(is_install and "Installation" or "Update"),
message = ("%s -> %s"):format(installed_rock.name, installed_rock.version),
percentage = 100,
})
Expand Down Expand Up @@ -203,7 +213,7 @@ Use 'Rocks install {rock_name}' or install rocks-git.nvim.
user_rocks.plugins[rock_name] = installed_rock.version
end
fs.write_file_await(config.config_path, "w", tostring(user_rocks))
cache.populate_removable_rock_cache()
cache.populate_all_rocks_state_caches()
vim.schedule(function()
-- Re-generate help tags
if config.generate_help_pages then
Expand Down
2 changes: 1 addition & 1 deletion lua/rocks/operations/prune.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ prune.prune = function(rock_name)
fs.write_file_await(config.config_path, "w", tostring(user_config))
local user_rocks = config.get_user_rocks()
handlers.prune_user_rocks(user_rocks, report_progress, report_error)
cache.populate_removable_rock_cache()
cache.populate_all_rocks_state_caches()
vim.schedule(function()
if success then
progress_handle:finish()
Expand Down
2 changes: 1 addition & 1 deletion lua/rocks/operations/update.lua
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ update.update = function(on_complete, opts)
else
progress_handle:finish()
end
cache.populate_removable_rock_cache()
cache.populate_all_rocks_state_caches()

-- Re-generate help tags
if config.generate_help_pages then
Expand Down
1 change: 1 addition & 0 deletions plugin/rocks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ if not config.lazy then
nio.gather({
cache.populate_cached_rocks,
cache.populate_removable_rock_cache,
cache.populate_outdated_rock_cache,
})
end)
end)
Expand Down

0 comments on commit 7bf5331

Please sign in to comment.