Skip to content

Commit

Permalink
feat(api): Add support for install options
Browse files Browse the repository at this point in the history
  • Loading branch information
simifalaye committed Nov 5, 2024
1 parent 654a01d commit 9ad8634
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/luarocks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
test_interpreters: ""
dependencies: |
luarocks >= 3.11.1, < 4.0.0
toml-edit >= 0.5.0
toml-edit >= 0.6.0
fidget.nvim >= 1.1.0
fzy
nvim-nio
Expand Down
16 changes: 11 additions & 5 deletions doc/rocks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -426,14 +426,20 @@ api.source_runtime_dir() *api.source_runtime_dir*
@deprecated Use the rtp.nvim luarock


rocks.InstallOpts *InstallRockOpts*
Fields: ~
{config_path?} (string) Config file path to use for installing the rock relative to the base config file
{callback?} (fun(rock:Rock)) Invoked upon successful completion


*api.install*
api.install({rock_name}, {version?}, {callback?})
Invoke ':Rocks install' with a callback
api.install({rock_name}, {version?}, {opts?})
Invoke ':Rocks install'

Parameters: ~
{rock_name} (rock_name) #The rock name
{version?} (string) The version of the rock to use
{callback?} (fun(rock:Rock)) Invoked upon successful completion
{rock_name} (rock_name) #The rock name
{version?} (string) The version of the rock to use
{opts?} (rocks.InstallOpts) Installation options


==============================================================================
Expand Down
23 changes: 19 additions & 4 deletions lua/rocks/api/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,27 @@ function api.source_runtime_dir(dir)
require("rtp_nvim").source_rtp_dir(dir)
end

---Invoke ':Rocks install' with a callback
---@class rocks.InstallOpts
---@field skip_prompts? boolean Whether to skip any "search 'dev' manifest prompts
---@field cmd? 'install' | 'update' Command used to invoke this function. Default: `'install'`
---@field config_path? string Config file path to use for installing the rock relative to the base config file
---@field callback? fun(rock: Rock) Invoked upon successful completion

---Invoke ':Rocks install'
---@param rock_name rock_name #The rock name
---@param version? string The version of the rock to use
---@param callback? fun(rock: Rock) Invoked upon successful completion
function api.install(rock_name, version, callback)
operations.add({ rock_name, version }, callback)
---@param opts? rocks.InstallOpts Installation options
function api.install(rock_name, version, opts)
if type(opts) == "function" then
vim.deprecate(
"rocks.api.install(rock_name, version, callback)",
"rocks.api.install(rocks_name, version, { callback = cb })",
"3.0.0",
"rocks.nvim"
)
opts = { callback = callback }
end
operations.add({ rock_name, version }, callback, opts --[[@as rocks.AddOpts]])
end

return api
15 changes: 8 additions & 7 deletions lua/rocks/operations/add.lua
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,13 @@ 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'`
---@field config_path? string Config file path to use for installing the rock relative to the base config file
---@field callback? fun(rock: Rock) Invoked upon successful completion

--- 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)
add.add = function(arg_list, opts)
opts = opts or {}
opts.cmd = opts.cmd or "install"
local is_install = opts.cmd == "install"
Expand All @@ -87,7 +88,7 @@ add.add = function(arg_list, callback, opts)

nio.run(function()
helpers.semaphore.with(function()
local user_rocks = helpers.parse_rocks_toml()
local user_rocks = helpers.parse_rocks_toml(opts.config_path)
local handler = handlers.get_install_handler_callback(user_rocks, arg_list)
if type(handler) == "function" then
local function report_progress(message)
Expand All @@ -96,7 +97,7 @@ add.add = function(arg_list, callback, opts)
})
end
handler(report_progress, report_error, helpers.manage_rock_stub)
user_rocks:write()
user_rocks:_write_await()
nio.scheduler()
progress_handle:finish()
return
Expand Down Expand Up @@ -215,14 +216,14 @@ Use 'Rocks %s {rock_name}' or install rocks-git.nvim.
else
user_rocks.plugins[rock_name] = installed_rock.version
end
user_rocks:write()
user_rocks:_write_await()
cache.populate_all_rocks_state_caches()
vim.schedule(function()
helpers.postInstall()
if success then
progress_handle:finish()
if callback then
callback(installed_rock)
if opts.callback then
opts.callback(installed_rock)
end
else
progress_handle:cancel()
Expand Down
2 changes: 2 additions & 0 deletions lua/rocks/operations/handlers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function handlers.register_handler(handler)
table.insert(_handlers, handler)
end

---@overload fun(rocks_toml_ref: MultiMutRocksTomlWrapper, arg_list: string[]): rock_handler_callback | nil
---@param rocks_toml_ref MutRocksTomlRef
---@param arg_list string[]
---@return rock_handler_callback | nil
Expand Down Expand Up @@ -64,6 +65,7 @@ function handlers.get_sync_handler_callback(spec)
end)
end

---@overload fun(rocks_toml_ref: MultiMutRocksTomlWrapper): rock_handler_callback[]
---@param rocks_toml_ref MutRocksTomlRef
---@return rock_handler_callback[]
function handlers.get_update_handler_callbacks(rocks_toml_ref)
Expand Down
54 changes: 47 additions & 7 deletions lua/rocks/operations/helpers/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,63 @@ local helpers = {}

helpers.semaphore = nio.control.semaphore(1)

---Decode the user rocks from rocks.toml, creating a default config file if it does not exist
---@return MutRocksTomlRef
function helpers.parse_rocks_toml()
---Decode the user rocks from rocks.toml, creating a default config file if it does not exist.
---A config path can be provided to only the return the config for a specific file. This path can
---be relative to the config_path directory or absolute with path expansion supported.
---@param config_path? string
---@return MultiMutRocksTomlWrapper
function helpers.parse_rocks_toml(config_path)
if config_path then
local absolute_config_path = fs.get_absolute_path(vim.fs.dirname(config.config_path), config_path)
local base_rocks_toml =
require("toml_edit").parse(fs.read_or_create(config.config_path, constants.DEFAULT_CONFIG))
-- Check to see if the path provided was the base config path, if so, just return that
if absolute_config_path == config.config_path then
return multi_mut_rocks_toml_wrapper.new({ { config = base_rocks_toml, path = config.config_path } })
end

-- For non-base configs, add it to the list of imports in the base config
if base_rocks_toml.import then
local i = 0
local import_path
repeat
i = i + 1
import_path = base_rocks_toml.import[i]
until import_path == nil or import_path == config_path
base_rocks_toml.import[i] = config_path
else
base_rocks_toml.import = { config_path }
end

-- For non-base configs, return a combined config with the imported config having preference over the base.
-- Since we modified the base also, it will also need to be written, hence, we return it to allow the
-- caller to write the config when all other modifications are done/successful.
return multi_mut_rocks_toml_wrapper.new({
{
config = require("toml_edit").parse(fs.read_or_create(absolute_config_path, "")),
path = absolute_config_path,
},
{
config = base_rocks_toml,
path = config.config_path,
},
})
end

local rocks_toml_configs = {}
config.read_rocks_toml(function(file_str)
-- Parse
return require("toml_edit").parse(file_str)
end, function(rocks_toml, file_path)
---@type MutRocksTomlRefWithPath
local rocks_toml_config = { config = rocks_toml, path = file_path }
-- Append to config list in order of preference returned by the read function
table.insert(rocks_toml_configs, rocks_toml_config)
table.insert(rocks_toml_configs, { config = rocks_toml, path = file_path })
end)

return multi_mut_rocks_toml_wrapper.new(rocks_toml_configs) --[[@as MutRocksTomlRef]]
return multi_mut_rocks_toml_wrapper.new(rocks_toml_configs)
end

---@overload fun(rocks_toml: MultiMutRocksTomlWrapper, rock_name: rock_name): "plugins"|"rocks"|nil
---@overload fun(rocks_toml: MultiMutRocksTomlWrapper, rock_name: rock_name): rock_config_table|nil
---@param rocks_toml MutRocksTomlRef
---@param rock_name rock_name
---@return "plugins"|"rocks"|nil rocks_key The key of the table containing the rock entry
Expand Down
26 changes: 21 additions & 5 deletions lua/rocks/operations/helpers/multi_mut_rocks_toml_wrapper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ local fs = require("rocks.fs")
---@field config MutRocksTomlRef Config metatable
---@field path? string The path to the configuration

---@class MultiMutRocksTomlWrapper
---@field cache table<string, MultiMutRocksTomlWrapper> Cache for nested metatables
---@field configs MutRocksTomlRefWithPath[] A list of rocks toml configs
---@class MultiMutRocksTomlWrapper: MutRocksTomlRef
local MultiMutRocksTomlWrapper = {}

--- Table accessor: Retrieve the value of the key from the first matching inner table
---@param self MultiMutRocksTomlWrapper
---@param key string|integer
---@return any
MultiMutRocksTomlWrapper.__index = function(self, key)
-- Give preference to class methods/fields
if MultiMutRocksTomlWrapper[key] then
Expand Down Expand Up @@ -36,6 +39,12 @@ MultiMutRocksTomlWrapper.__index = function(self, key)
end
return nil
end

--- Table field assignment: Set value of the key from the first matching inner
--- table or the first table if not found in any
---@param self MultiMutRocksTomlWrapper
---@param key string|integer
---@param value any
MultiMutRocksTomlWrapper.__newindex = function(self, key, value)
local insert_index = 1
for i, tbl in ipairs(self.configs) do
Expand All @@ -52,9 +61,16 @@ MultiMutRocksTomlWrapper.__newindex = function(self, key, value)
self.configs[insert_index].config[key] = value
end

--- Write to all rocks toml config files in an async context
--- Run a function against the config tables
---@param self MultiMutRocksTomlWrapper
---@param func fun(configs: MutRocksTomlRefWithPath[])
MultiMutRocksTomlWrapper.__call = function(self, func)
func(self.configs)
end

--- Write the config tables to their appropriate paths in an async context
---@type async fun(self: MultiMutRocksTomlWrapper)
function MultiMutRocksTomlWrapper:write()
MultiMutRocksTomlWrapper._write_await = function(self)
for _, tbl in ipairs(self.configs) do
if tbl.path ~= nil then
fs.write_file_await(tbl.path, "w", tostring(tbl.config))
Expand Down
2 changes: 1 addition & 1 deletion lua/rocks/operations/pin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pin.pin = function(rock_name)
end
user_config[rocks_key][rock_name].pin = true
local version = user_config[rocks_key][rock_name].version
user_config:write()
user_config:_write_await()
vim.schedule(function()
vim.notify(("%s pinned to version %s"):format(rock_name, version), vim.log.levels.INFO)
end)
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 @@ -55,7 +55,7 @@ prune.prune = function(rock_name)
progress_handle:report({ message = message, title = "Error" })
success = false
end
user_config:write()
user_config:_write_await()
local user_rocks = config.get_user_rocks()
handlers.prune_user_rocks(user_rocks, report_progress, report_error)
adapter.synchronise_site_symlinks()
Expand Down
2 changes: 1 addition & 1 deletion lua/rocks/operations/unpin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ unpin.unpin = function(rock_name)
else
user_config[rocks_key][rock_name].pin = nil
end
user_config:write()
user_config:_write_await()
vim.schedule(function()
vim.notify(("%s unpinned"):format(rock_name), vim.log.levels.INFO)
end)
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 @@ -169,7 +169,7 @@ update.update = function(on_complete, opts)
user_rocks[rocks_key][rock_name] = installed_rock.version
end
end
user_rocks:write()
user_rocks:_write_await()
nio.scheduler()
if not vim.tbl_isempty(error_handles) then
local message = "Update completed with errors! Run ':Rocks log' for details."
Expand Down
4 changes: 2 additions & 2 deletions rocks.nvim-scm-1.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version = _MODREV .. _SPECREV
dependencies = {
"lua >= 5.1",
"luarocks >= 3.11.1, < 4.0.0",
"toml-edit >= 0.5.0",
"toml-edit >= 0.6.0",
"fidget.nvim >= 1.1.0",
"fzy",
"nvim-nio",
Expand All @@ -19,7 +19,7 @@ dependencies = {
test_dependencies = {
"lua >= 5.1",
"luarocks >= 3.11.1, < 4.0.0",
"toml-edit >= 0.5.0",
"toml-edit >= 0.6.0",
"fidget.nvim >= 1.1.0",
"fzy",
"nvim-nio",
Expand Down
45 changes: 45 additions & 0 deletions spec/operations/helpers_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,51 @@ pin = true
assert.same(nil, rocks_toml.luarocks.servers[3])
assert.is_not_nil(rocks_toml.import)
end)
it("Parse rocks toml passing base config path", function()
local config_content = [[
[rocks]
myrock = "1.0.0"
]]

local fh = assert(io.open(config.config_path, "w"), "Could not open rocks.toml for writing")
fh:write(config_content)
fh:close()

local rocks_toml = helpers.parse_rocks_toml(config.config_path)
assert.is_not_nil(rocks_toml.rocks)
assert.same("1.0.0", rocks_toml.rocks.myrock)
end)
it("Parse rocks toml passing new import path", function()
local config_content = [[
[rocks]
myrock = "1.0.0"
]]

local fh = assert(io.open(config.config_path, "w"), "Could not open rocks.toml for writing")
fh:write(config_content)
fh:close()

local _, _ = os.remove(vim.fs.joinpath(tempdir, "local-rocks.toml"))
local rocks_toml = helpers.parse_rocks_toml("local-rocks.toml")
assert.same("local-rocks.toml", rocks_toml.import[1])
end)
it("Parse rocks toml passing new import path, append to imports", function()
local config_content = [[
import = ["other-rocks.toml"]
[rocks]
myrock = "1.0.0"
]]

local fh = assert(io.open(config.config_path, "w"), "Could not open rocks.toml for writing")
fh:write(config_content)
fh:close()

local _, _ = os.remove(vim.fs.joinpath(tempdir, "local-rocks.toml"))
local rocks_toml = helpers.parse_rocks_toml("local-rocks.toml")
assert.same("other-rocks.toml", rocks_toml.import[1])
assert.same("local-rocks.toml", rocks_toml.import[2])
end)
end)

describe("operations.helpers.multi_mut_rocks_toml_wrapper", function()
Expand Down

0 comments on commit 9ad8634

Please sign in to comment.