unpack is a minimal layer on top of vim.pack
Add this line to your init.lua:
vim.pack.add({ "https://github.com/mezdelex/unpack.nvim" }, { confirm = false })unpack automatically loads its default config on startup via plugin directory.
Call setup right after the installation with your preferred options if you don't like the defaults.
Defaults are set with minimal interaction in mind, so if you want to be notified about all the changes, set confirm to true and force to false.
Available options:
---@class Unpack.Config.UserOpts
---@field add_options vim.pack.keyset.add?
---@field update_options vim.pack.keyset.update?See :h vim.pack.add and :h vim.pack.update opts.
Tip
Make sure you set vim.g.mapleader beforehand.
require("unpack").setup({
-- example optional setup call
add_options = { confirm = true }, -- default false
update_options = { force = false }, -- default true
})This layer extends vim.pack.Spec to allow single file configurations.
---@class Unpack.Spec : vim.pack.Spec
---@field config fun()?
---@field defer boolean?
---@field dependencies Unpack.Spec[]?It also leverages PackChanged event triggered by vim.pack internals to run plugin build hooks.
Example config using unpack as daily driver.
Example plugin spec setups under /lua/plugins/:
return {
config = function()
...
end,
data = { build = "your build --command" },
defer = true,
src = "https://github.com/<vendor>/plugin1",
}return {
config = function()
...
end,
data = {
build = "your build --command",
conflicts = { "conflicting_file_name1.dll", "conflicting_file_name2.dll" }
},
defer = true,
src = "https://github.com/<vendor>/plugin1",
}return {
config = function()
...
end,
defer = true,
dependencies = {
{
defer = true,
src = "https://github.com/<vendor>/plugin2"
},
},
src = "https://github.com/<vendor>/plugin3",
}unpack expects a build field inside data table for the build hook, so make sure you add it like shown in the first example.
This is because vim.pack handles the event trigger internally and exposes vim.pack.Spec, not the extended one, so we need to rely on that table.
The build hook is planned to be part of and handled by the plugin itself, that's why there's no build hook exposed on purpose, but for now this is the workaround.
For reference, this is the autocmd that listens to the event triggered by vim.pack internals whenever there's a change in any package.
Note
This is already set, you don't need to worry about it.
vim.api.nvim_create_augroup(group, { clear = true })
vim.api.nvim_create_autocmd("PackChanged", {
callback = function(args)
if args.data.kind == "install" or args.data.kind == "update" then
commands.build(args.data)
end
end,
group = group,
})Under WinOS, there are some permission problems related to the write rights on locked files and this affects the build hook when updating some plugins like blink.cmp.
To address this, together with the build hook, unpack expects you to use conflicts hook inside the data table. This is like this because the build hook
will eventually be handled by the plugin itself with the incoming spec changes as we stated before, so it makes sense to keep them together.
Until then, this is the workaround for WinOS users:
return {
config = function()
-- example configuration
require("blink.cmp").setup({
completion = {
documentation = { auto_show = true },
},
keymap = { preset = "enter" },
})
end,
data = {
build = "cargo build --release",
conflicts = { "blink_cmp_fuzzy.dll" },
},
defer = true,
src = "https://github.com/saghen/blink.cmp",
}Every spec marked with defer = true is going to be deferred using vim.schedule to avoid UI render delay. Dependencies follow the same rules.
The dependencies handling logic is pretty simple: the plugins are going to be loaded following the plugins directory name order, so make sure to add the dependencies properly.
For example, if any of your plugins relies on plenary as a dependency, add it in the first plugin that requires it following your plugins directory name order, and that's pretty much it.
Note that unpack treats every spec separately, so you could still include the dependencies in order, defer a specific plugin and still eagerly load its dependencies so they would be available for the following eagerly loaded ones that might require those dependencies.
Whatever makes more sense to you.
Note
All the notifications are wrapped in a vim.schedule call to avoid command line overflow.
If you want to see the recap after executing any command, use :messages.
The commands provided are:
| Command | Description |
|---|---|
PackClean |
Removes any plugin present in your packages directory that doesn't exist as a plugin spec and cleans stale conflicts if any. |
PackUpdate |
Updates all the plugins present in your packages directory. |
You can also use them this way if you prefer:
local unpack = require("unpack")
vim.keymap.set("n", "<your-keymap>", unpack.clean)
vim.keymap.set("n", "<your-keymap>", unpack.update)- Single config file
- Defer behavior
- Simple dependency handling
- Commands
- Better error handling
- Performance improvements
- CI
- Style check job (stylua)
- Tests check job (busted)
- commands
- config
- extensions
- unpack
- Doc generation job (panvimdoc)
- Add dependabot
- Enforce PR ruleset