Skip to content

mezdelex/unpack.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UnPack is a minimal layer on top of vim.pack API to allow single file plugin configurations.


Important

vim.pack is currently under development in the neovim-nightly branch.

Installation

Add these lines to your init.lua:

local unpack_path = vim.fn.stdpath("data") .. "/site/pack/managers/start/unpack"

if not vim.uv.fs_stat(unpack_path) then
    vim.fn.system({
        "git",
        "clone",
        "--filter=blob:none",
        "https://github.com/mezdelex/unpack.nvim",
        unpack_path,
    })
end
vim.opt.runtimepath:prepend(unpack_path)

Setup

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
--- Options for vim.pack.add
---@field add_options? vim.pack.keyset.add
--- Options for vim.pack.update
---@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
})

Spec

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. The same command that is fired inside the event is provided as a standalone one. See Commands section.

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",
}

Build

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)
        local kind = args.data.kind ---@type string

        if kind == "install" or kind == "update" then
            local spec = args.data.spec ---@type UnPack.Spec

            commands.build({ spec })
        end
    end,
    group = group,
})

Conflicts

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",
}

Defer

Every spec marked with defer = true is going to be deferred using vim.schedule to avoid UI render delay. Dependencies follow the same rules.

Dependencies

The dependencies handling logic is pretty simple: the plugins are going to be loaded in order, so make sure to add the dependencies in order too. 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.

Commands

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
PackBuild Iterates over all the plugin specs and runs all the build hooks. (Triggered automatically on PackChanged event per changed package)
PackClean Removes any plugin present in your packages directory that doesn't exist as a plugin spec and cleans stale conflicts if any.
PackLoad Loads all the plugins in your plugins directory. (Runs on VimEnter; exposed for build timeouts)
PackPull Updates UnPack to the latest version. (Runs on VimEnter; calls vim.system asynchronously to pull changes)
PackUpdate Updates all the plugins present in your packages directory.

You can also use them this way if you prefer:

    local commands = require("unpack.commands")

    vim.keymap.set("n", "<your-keymap>", commands.build)
    vim.keymap.set("n", "<your-keymap>", commands.clean)
    vim.keymap.set("n", "<your-keymap>", commands.load)
    vim.keymap.set("n", "<your-keymap>", commands.pull)
    vim.keymap.set("n", "<your-keymap>", commands.update)

Roadmap

  • Single config file
  • Defer behavior
  • Simple dependency handling
  • Commands
  • Force pull UnPack updates on setup
    • Schedule helptags generation after pull
  • 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

About

UnPack is a small layer on top of vim.pack API to allow single file plugin configurations

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages