Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(experimental): a way to recognise plugins managed by extensions as dependencies #492

Merged
merged 1 commit into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 45 additions & 17 deletions doc/rocks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rocks.nvim ··································
························································· |rocks-toml.luarocks|
rocks.nvim commands ··········································· |rocks-commands|
rocks.nvim configuration ········································ |rocks-config|
experimental features ····································· |rocks.experimental|
rocks.nvim |User| |event|s ·································· |rocks.user-event|
Lua API for rocks.nvim extensions ·································· |rocks-api|
rocks.nvim API hooks ········································· |rocks-api-hooks|
Expand Down Expand Up @@ -138,33 +139,51 @@ RocksOpts *RocksOpts*

Fields: ~
{rocks_path?} (string)
Local path in your file system to install rocks
(Default: a `rocks` directory in `vim.fn.stdpath("data")`).
Local path in your file system to install rocks
(Default: a `rocks` directory in `vim.fn.stdpath("data")`).
{config_path?} (string)
Rocks declaration file path (Default: `rocks.toml`) in `vim.fn.stdpath("config")`.
Rocks declaration file path (Default: `rocks.toml`) in `vim.fn.stdpath("config")`.
{luarocks_binary?} (string)
Luarocks binary path. Defaults to the bundled installation if executable.
Luarocks binary path. Defaults to the bundled installation if executable.
{lazy?} (boolean)
Whether to query luarocks.org lazily (Default: `false`).
Setting this to `true` may improve startup time,
but features like auto-completion will lag initially.
Whether to query luarocks.org lazily (Default: `false`).
Setting this to `true` may improve startup time,
but features like auto-completion will lag initially.
{dynamic_rtp?} (boolean)
Whether to automatically add freshly installed plugins to the 'runtimepath'.
(Default: `true` for the best default experience).
Whether to automatically add freshly installed plugins to the 'runtimepath'.
(Default: `true` for the best default experience).
{generate_help_pages?} (boolean)
Whether to re-generate plugins help pages after installation/upgrade. (Default: `true`).
Whether to re-generate plugins help pages after installation/upgrade. (Default: `true`).
{update_remote_plugins?} (boolean)
Whether to update remote plugins after installation/upgrade. (Default: `true`).
Whether to update remote plugins after installation/upgrade. (Default: `true`).
{reinstall_dev_rocks_on_update?} (boolean)
Whether to reinstall 'dev' rocks on update
(Default: `true`, as rocks.nvim cannot determine if 'dev' rocks are up to date).
Whether to reinstall 'dev' rocks on update
(Default: `true`, as rocks.nvim cannot determine if 'dev' rocks are up to date).
{enable_luarocks_loader?} (boolean)
Whether to use the luarocks loader to support multiple dependencies (Default: `true`).
Whether to use the luarocks loader to support multiple dependencies (Default: `true`).
{luarocks_config?} (table)
Extra luarocks config options.
rocks.nvim will create a default luarocks config in `rocks_path` and merge it with this table (if set).
Extra luarocks config options.
rocks.nvim will create a default luarocks config in `rocks_path` and merge it with this table (if set).
{experimental_features?} (rocks.ExperimentalFeature[])
List of experimental features to enable.
See |rocks.experimental|.


==============================================================================
experimental features *rocks.experimental*

WARNING: Experimental features may change or be removed
without a major SemVer version bump.

rocks.ExperimentalFeature
*rocks.ExperimentalFeature*

Values:
"ext_module_dependency_stubs"
Install rocks stubs when using extensions
like rocks-git.nvim or rocks-dev.nvim
so that luarocks recognises them as dependencies.

==============================================================================
rocks.nvim |User| |event|s *rocks.user-event*

Expand Down Expand Up @@ -368,14 +387,23 @@ MutRocksTomlRef *MutRocksTomlRef*
{string} (unknown)


rock_handler.on_success.Opts *rock_handler.on_success.Opts*

Fields: ~
{action} ("install"|"prune")
{rock} (Rock)


rock_handler_callback *rock_handler_callback*

Type: ~
fun(report_progress:fun(message:string),report_error:fun(message:string))
fun(on_progress:fun(message:string),on_error:fun(message:string),on_success?:fun(opts:rock_handler.on_success.Opts))


An async callback that handles an operation on a rock.

- The `on_success` callback is optional for backward compatibility.

RockHandler *RockHandler*

Fields: ~
Expand Down
8 changes: 7 additions & 1 deletion lua/rocks/api/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,15 @@ end
---@field plugins? rock_config_table
---@field [string] unknown

---@alias rock_handler_callback fun(report_progress: fun(message: string), report_error: fun(message: string))
---@class rock_handler.on_success.Opts
---@field action 'install' | 'prune'
---@field rock Rock

---@alias rock_handler_callback fun(on_progress: fun(message: string), on_error: fun(message: string), on_success?: fun(opts: rock_handler.on_success.Opts))
---@brief [[
---An async callback that handles an operation on a rock.
---
--- - The `on_success` callback is optional for backward compatibility.
---@brief ]]

---@class RockHandler
Expand Down
31 changes: 31 additions & 0 deletions lua/rocks/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,37 @@ local config = {}
--- Extra luarocks config options.
--- rocks.nvim will create a default luarocks config in `rocks_path` and merge it with this table (if set).
---@field luarocks_config? table
---
--- List of experimental features to enable.
--- See |rocks.experimental|.
---@field experimental_features? rocks.ExperimentalFeature[]

---@mod rocks.experimental experimental features
---
---@brief [[
---WARNING: Experimental features may change or be removed
---without a major SemVer version bump.
---@brief ]]

-- TODO(mrcjkb): Remove this when https://github.com/mrcjkb/vimcats/pull/18 is completed
---@brief [[
---rocks.ExperimentalFeature
--- *rocks.ExperimentalFeature*
---
--- Values:
--- "ext_module_dependency_stubs"
--- Install rocks stubs when using extensions
--- like rocks-git.nvim or rocks-dev.nvim
--- so that luarocks recognises them as dependencies.
---@brief ]]

---@enum rocks.ExperimentalFeature
config.ExperimentalFeature = {
--- Install rocks stubs when using extensions
--- like rocks-git.nvim or rocks-dev.nvim
--- so that luarocks recognises them as dependencies.
ext_module_dependency_stubs = "ext_module_dependency_stubs",
}

---@type RocksOpts | fun():RocksOpts
vim.g.rocks_nvim = vim.g.rocks_nvim
Expand Down
18 changes: 18 additions & 0 deletions lua/rocks/config/internal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,14 @@ local default_config = {
end,
---@type fun():string
luarocks_config_path = nil,

---@type rocks.ExperimentalFeatureFlags
experimental_features = {},
}

---@class rocks.ExperimentalFeatureFlags
---@field [rocks.ExperimentalFeature] boolean

---@type RocksOpts
local opts = type(vim.g.rocks_nvim) == "function" and vim.g.rocks_nvim() or vim.g.rocks_nvim or {}

Expand Down Expand Up @@ -251,6 +257,18 @@ if not opts.luarocks_config or type(opts.luarocks_config) == "table" then
end
end

if type(opts.experimental_features) == "table" then
config.experimental_features = vim.iter(opts.experimental_features):fold(
{},
---@param acc table<rocks.ExperimentalFeature, boolean>
---@param feature rocks.ExperimentalFeature
function(acc, feature)
acc[feature] = true
return acc
end
)
end

return config

--- config.lua ends here
15 changes: 15 additions & 0 deletions lua/rocks/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,21 @@ constants.DEFAULT_DEV_SERVERS = {
constants.ROCKS_BINARIES_DEV,
}

constants.STUB_ROCKSPEC_TEMPLATE = [==[
package = "%s"
version = "%s-1"

source = {
url = 'https://github.com/nvim-neorocks/luarocks-stub/archive/548853648d7cff7e0d959ff95209e8aa97a793bc.zip',
dir = 'luarocks-stub-548853648d7cff7e0d959ff95209e8aa97a793bc',
}

build = {
type = "builtin",
modules = {}
}
]==]

return constants

--- constants.lua
2 changes: 1 addition & 1 deletion lua/rocks/operations/add.lua
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ add.add = function(arg_list, callback, opts)
message = message,
})
end
handler(report_progress, report_error)
handler(report_progress, report_error, helpers.manage_rock_stub)
fs.write_file_await(config.config_path, "w", tostring(user_rocks))
nio.scheduler()
progress_handle:finish()
Expand Down
10 changes: 6 additions & 4 deletions lua/rocks/operations/handlers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

local handlers = {}

local helpers = require("rocks.operations.helpers")

---@type RockHandler[]
local _handlers = {}

Expand Down Expand Up @@ -80,13 +82,13 @@ end

---Tell external handlers to prune their rocks
---@param user_rocks table<rock_name, RockSpec>
---@param report_progress fun(message: string)
---@param report_error fun(message: string)
handlers.prune_user_rocks = function(user_rocks, report_progress, report_error)
---@param on_progress fun(message: string)
---@param on_error fun(message: string)
handlers.prune_user_rocks = function(user_rocks, on_progress, on_error)
for _, handler in pairs(_handlers) do
local callback = type(handler.get_prune_callback) == "function" and handler.get_prune_callback(user_rocks)
if callback then
callback(report_progress, report_error)
callback(on_progress, on_error, helpers.manage_rock_stub)
end
end
end
Expand Down
42 changes: 42 additions & 0 deletions lua/rocks/operations/helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,46 @@ function helpers.postInstall()
end
end

---Installs or removes a rock stub so that luarocks will recognise rocks installed by
---extensions like rocks-git and rocks-dev as dependencies
---@param opts rock_handler.on_success.Opts
helpers.manage_rock_stub = nio.create(function(opts)
if not config.experimental_features.ext_module_dependency_stubs then
log.debug("Installing stubs is disabled")
return
end
if opts.action == "install" then
local rock = opts.rock
log.info(("Installing stub %s"):format(vim.inspect(rock)))
local rockspec_content = constants.STUB_ROCKSPEC_TEMPLATE:format(rock.name, rock.version)
nio.scheduler()
local tempdir = vim.fn.tempname()
local ok = fs.mkdir_p(tempdir)
if not ok then
log.error(("Could not create temp directory for rock stub %s rockspec"):format(vim.inspect(rock)))
return
end
local rockspec_file = ("%s-%s-1.rockspec"):format(rock.name, rock.version)
local rockspec_path = vim.fs.joinpath(tempdir, rockspec_file)
fs.write_file_await(rockspec_path, "w", rockspec_content)
-- XXX For now, we ignore failures and only log them
local future = nio.control.future()
luarocks.cli(
{ "install", rockspec_file },
---@param sc vim.SystemCompleted
function(sc)
if sc.code ~= 0 then
log.error(("Failed to install stub from rockspec %s"):format(rockspec_path))
end
future.set(true)
end,
{ cwd = tempdir }
)
future.wait()
elseif opts.action == "prune" then
local future = helpers.remove(opts.rock.name)
pcall(future.wait)
end
end)

return helpers
4 changes: 2 additions & 2 deletions lua/rocks/operations/sync.lua
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ operations.sync = function(user_rocks, on_complete)
-- Sync actions handled by external modules that have registered handlers
for _, callback in pairs(sync_status.external_actions) do
ct = ct + 1
callback(report_progress, report_error)
callback(report_progress, report_error, helpers.manage_rock_stub)
end

-- rocks.nvim sync handlers should be installed now.
Expand All @@ -145,7 +145,7 @@ operations.sync = function(user_rocks, on_complete)
ct = ct + 1
local callback = handlers.get_sync_handler_callback(spec)
if callback then
callback(report_progress, report_error)
callback(report_progress, report_error, helpers.manage_rock_stub)
else
report_error(("Failed to install %s: %s"):format(spec.name, skipped_rock.reason))
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 @@ -148,7 +148,7 @@ update.update = function(on_complete, opts)
message = message,
})
end
handler(report_progress, report_error)
handler(report_progress, report_error, helpers.manage_rock_stub)
progress_handle:report({
percentage = get_percentage(ct, total_update_count),
})
Expand Down
21 changes: 21 additions & 0 deletions spec/operations/helpers_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ vim.fn.mkdir(tempdir, "p")
vim.g.rocks_nvim = {
luarocks_binary = "luarocks",
rocks_path = tempdir,
experimental_features = { "ext_module_dependency_stubs" },
}
local nio = require("nio")
vim.env.PLENARY_TEST_TIMEOUT = 60000
describe("operations.helpers", function()
local helpers = require("rocks.operations.helpers")
local config = require("rocks.config.internal")
local state = require("rocks.state")
vim.system({ "mkdir", "-p", config.rocks_path }):wait()
nio.tests.it("install/remove", function()
helpers.install({ name = "plenary.nvim" }).wait()
Expand Down Expand Up @@ -48,4 +50,23 @@ describe("operations.helpers", function()
assert.is_nil(result.baz)
assert.same("foo 7.0.0 -> 8.0.0", tostring(result.foo))
end)
nio.tests.it("Install rock stub", function()
local installed_rocks = state.installed_rocks()
assert.is_nil(installed_rocks["stub.nvim"])
helpers.manage_rock_stub({
rock = { name = "stub.nvim", version = "1.0.0" },
action = "install",
})
installed_rocks = state.installed_rocks()
assert.same({
name = "stub.nvim",
version = "1.0.0",
}, installed_rocks["stub.nvim"])
helpers.manage_rock_stub({
rock = { name = "stub.nvim", version = "1.0.0" },
action = "prune",
})
installed_rocks = state.installed_rocks()
assert.is_nil(installed_rocks["stub.nvim"])
end)
end)
Loading