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: Rocks [pin|unpin] {rock} command #280

Merged
merged 1 commit into from
Apr 18, 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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@ Or
:Rocks install neorg 7.0.0 pin=true
```

You can also pin/unpin installed plugins with:

```vim
:Rocks [pin|unpin] {rock}
```

## :package: Extending `rocks.nvim`

This plugin provides a Lua API for extensibility.
Expand Down
5 changes: 4 additions & 1 deletion doc/rocks.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ rocks.nvim commands *rocks.commands*
It may take more than one sync to prune all rocks that can be pruned.
update Search for updated rocks and install them.
edit Edit the rocks.toml file.
pin {rock} Pin {rock} to the installed version.
Pinned rocks are ignored by ':Rocks update'.
unpin {rock} Unpin {rock}.
packadd {rock} Search for an optional rock and source any plugin files found.
The rock must be installed by luarocks.
It is added to the 'runtimepath' if it wasn't there yet.
Expand Down Expand Up @@ -167,7 +170,7 @@ api.get_rocks_toml_path() *api.get_rocks_toml_path*
RockSpec *RockSpec*


{ name: rock_name, version?: string, opt?: boolean, [string]: V }
{ name: rock_name, version?: string, opt?: boolean, pin?: boolean, [string]: V }

Specification for a rock in rocks.toml. May be extended by external modules.

Expand Down
4 changes: 2 additions & 2 deletions lua/rocks/api/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ function api.get_rocks_toml_path()
return config.config_path
end

---@class RockSpec: { name: rock_name, version?: string, opt?: boolean, [string]: unknown }
---@class RockSpec: { name: rock_name, version?: string, opt?: boolean, pin?: boolean, [string]: unknown }
---@brief [[
--- { name: rock_name, version?: string, opt?: boolean, [string]: V }
--- { name: rock_name, version?: string, opt?: boolean, pin?: boolean, [string]: V }
---
---Specification for a rock in rocks.toml. May be extended by external modules.
---@brief ]]
Expand Down
60 changes: 57 additions & 3 deletions lua/rocks/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
--- It may take more than one sync to prune all rocks that can be pruned.
--- update Search for updated rocks and install them.
--- edit Edit the rocks.toml file.
--- pin {rock} Pin {rock} to the installed version.
--- Pinned rocks are ignored by ':Rocks update'.
--- unpin {rock} Unpin {rock}.
--- packadd {rock} Search for an optional rock and source any plugin files found.
--- The rock must be installed by luarocks.
--- It is added to the 'runtimepath' if it wasn't there yet.
Expand All @@ -29,12 +32,11 @@
---@brief ]]
---

-- Copyright (C) 2023 Neorocks Org.
-- Copyright (C) 2024 Neorocks Org.
--
-- Version: 0.1.0
-- License: GPLv3
-- Created: 24 Oct 2023
-- Updated: 24 Oct 2023
-- Updated: 17 Apr 2024
-- Homepage: https://github.com/nvim-neorocks/rocks.nvim
-- Maintainers: NTBBloodbath <[email protected]>, Vhyrro <[email protected]>, mrcjkb <[email protected]>

Expand All @@ -44,6 +46,7 @@ local fzy = require("rocks.fzy")
local cache = require("rocks.cache")
local fs = require("rocks.fs")
local constants = require("rocks.constants")
local config = require("rocks.config.internal")
local log = require("rocks.log")

---@param name string
Expand Down Expand Up @@ -84,6 +87,25 @@ local function complete_names(query)
return fzy.fuzzy_filter(query, rock_names)
end

---@param predicate fun(spec: RockSpec):boolean
---@param query string
---@return rock_name[]
local function fuzzy_filter_user_rocks(predicate, query)
local user_rocks = config.get_user_rocks()
if vim.tbl_isempty(user_rocks) then
return {}
end
---@type rock_name[]
local rock_names = vim.iter(vim.fn.values(user_rocks))
:filter(predicate)
:map(function(spec)
---@cast spec RockSpec
return spec.name
end)
:totable()
return fzy.fuzzy_filter(query, rock_names)
end

---Completion for installed rocks that are not dependencies of other rocks
---and can be removed.
---@param query string | nil
Expand Down Expand Up @@ -153,6 +175,38 @@ local rocks_command_tbl = {
vim.cmd.e(config_path)
end,
},
pin = {
impl = function(args)
local rock_name = args[1]
if not rock_name then
vim.notify("'pin {rock}: Missing argument {rock}", vim.log.levels.ERROR)
return
end
require("rocks.operations").pin(rock_name)
end,
complete = function(query)
return fuzzy_filter_user_rocks(function(spec)
---@cast spec RockSpec
return not spec.pin
end, query)
end,
},
unpin = {
impl = function(args)
local rock_name = args[1]
if not rock_name then
vim.notify("'pin {rock}: Missing argument {rock}", vim.log.levels.ERROR)
return
end
require("rocks.operations").unpin(rock_name)
end,
complete = function(query)
return fuzzy_filter_user_rocks(function(spec)
---@cast spec RockSpec
return spec.pin
end, query)
end,
},
packadd = {
impl = function(args, opts)
if #args ~= 1 then
Expand Down
52 changes: 52 additions & 0 deletions lua/rocks/operations/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -669,4 +669,56 @@ operations.prune = function(rock_name)
end)
end

---@param rock_name rock_name
operations.pin = function(rock_name)
nio.run(function()
local user_config = parse_rocks_toml()
local rocks_key = (user_config.plugins and user_config.plugins[rock_name] and "plugins")
or (user_config.rocks and user_config.rocks[rock_name] and "rocks")
if not rocks_key then
vim.schedule(function()
vim.notify(rock_name .. " not found in rocks.toml", vim.log.levels.ERROR)
end)
return
end
if type(user_config[rocks_key][rock_name]) == "string" then
local version = user_config[rocks_key][rock_name]
user_config[rocks_key][rock_name] = {}
user_config[rocks_key][rock_name].version = version
end
user_config[rocks_key][rock_name].pin = true
vim.schedule(function()
local version = user_config[rocks_key][rock_name].version
fs.write_file(config.config_path, "w", tostring(user_config))
vim.notify(("%s pinned to version %s"):format(rock_name, version), vim.log.levels.INFO)
end)
end)
end

---@param rock_name rock_name
operations.unpin = function(rock_name)
nio.run(function()
local user_config = parse_rocks_toml()
local rocks_key = (user_config.plugins[rock_name] and "plugins") or (user_config.rocks[rock_name] and "rocks")
if not rocks_key then
vim.schedule(function()
vim.notify(rock_name .. " not found in rocks.toml", vim.log.levels.ERROR)
end)
return
end
if type(user_config[rocks_key][rock_name]) == "string" then
return
end
if not user_config[rocks_key][rock_name].opt then
user_config[rocks_key][rock_name] = user_config[rocks_key][rock_name].version
else
user_config[rocks_key][rock_name].pin = nil
end
vim.schedule(function()
fs.write_file(config.config_path, "w", tostring(user_config))
vim.notify(("%s unpinned"):format(rock_name), vim.log.levels.INFO)
end)
end)
end

return operations
132 changes: 132 additions & 0 deletions spec/operations/pin_commands_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
local tempdir = vim.fn.tempname()
vim.system({ "rm", "-r", tempdir }):wait()
vim.system({ "mkdir", "-p", tempdir }):wait()
vim.g.rocks_nvim = {
rocks_path = tempdir,
config_path = vim.fs.joinpath(tempdir, "rocks.toml"),
}

local nio = require("nio")
local fs = require("rocks.fs")
local config = require("rocks.config.internal")
local commands = require("rocks.commands")
commands.create_commands()

local function parse_config()
local config_file = fs.read_or_create(config.config_path, "")
return require("toml_edit").parse(config_file)
end

vim.env.PLENARY_TEST_TIMEOUT = 60000 * 5
describe("Rocks pin/unpin", function()
nio.tests.it("pin/unpin plugin with only version", function()
local rocks_toml = parse_config()
rocks_toml.plugins = {}
rocks_toml.plugins.foo = "1.0.0"
local completion = vim.fn.getcompletion("Rocks pin ", "cmdline")
assert.same({}, completion)
local fh = assert(io.open(config.config_path, "w"), "Could not open rocks.toml for writing")
fh:write(tostring(rocks_toml))
fh:close()
completion = vim.fn.getcompletion("Rocks pin ", "cmdline")
assert.same({ "foo" }, completion)
completion = vim.fn.getcompletion("Rocks unpin ", "cmdline")
assert.same({}, completion)
vim.cmd.Rocks({ "pin", "foo" })
nio.sleep(1000) -- wait for rocks.toml to be written
rocks_toml = parse_config()
assert.same(
[[
[plugins]

[plugins.foo ]
version = "1.0.0"
pin = true
]],
tostring(rocks_toml)
)
completion = vim.fn.getcompletion("Rocks unpin ", "cmdline")
assert.same({ "foo" }, completion)
vim.cmd.Rocks({ "unpin", "foo" })
nio.sleep(1000) -- wait for rocks.toml to be written
rocks_toml = parse_config()
assert.same(
[[
[plugins]
foo = "1.0.0"
]],
tostring(rocks_toml)
)
end)
nio.tests.it("pin plugin with version and opt", function()
local rocks_toml = parse_config()
rocks_toml.plugins = {}
rocks_toml.plugins.foo = {}
rocks_toml.plugins.foo.version = "1.0.0"
rocks_toml.plugins.foo.opt = true
local completion = vim.fn.getcompletion("Rocks pin ", "cmdline")
assert.same({ "foo" }, completion)
local fh = assert(io.open(config.config_path, "w"), "Could not open rocks.toml for writing")
fh:write(tostring(rocks_toml))
fh:close()
vim.cmd.Rocks({ "pin", "foo" })
nio.sleep(1000) -- wait for rocks.toml to be written
rocks_toml = parse_config()
assert.same(
[[
[plugins]

[plugins.foo]
version = "1.0.0"
opt = true
pin = true
]],
tostring(rocks_toml)
)
vim.cmd.Rocks({ "unpin", "foo" })
nio.sleep(1000) -- wait for rocks.toml to be written
rocks_toml = parse_config()
assert.same(
[[
[plugins]

[plugins.foo]
version = "1.0.0"
opt = true
]],
tostring(rocks_toml)
)
end)
nio.tests.it("pin plugin with only version as table", function()
local rocks_toml = parse_config()
rocks_toml.plugins = {}
rocks_toml.plugins.foo = {}
rocks_toml.plugins.foo.version = "1.0.0"
local fh = assert(io.open(config.config_path, "w"), "Could not open rocks.toml for writing")
fh:write(tostring(rocks_toml))
fh:close()
vim.cmd.Rocks({ "pin", "foo" })
nio.sleep(1000) -- wait for rocks.toml to be written
rocks_toml = parse_config()
assert.same(
[[
[plugins]

[plugins.foo]
version = "1.0.0"
pin = true
]],
tostring(rocks_toml)
)
vim.cmd.Rocks({ "unpin", "foo" })
nio.sleep(1000) -- wait for rocks.toml to be written
rocks_toml = parse_config()
assert.same(
[[
[plugins]
foo= "1.0.0"
]],
tostring(rocks_toml)
)
end)
end)
Loading