This readme is a work in progress.
A versatile, declarative and correct neovim package manager in Lua. Originally written for personal use by luaneko.
What does that mean?
versatile
- packages can be declared in any Lua file in any order of your liking.declarative
- packages are declared using simple Lua tables.correct
- packages are always loaded in a correct and consistent order.
See also luaneko's neovim-configs for an example of how dep can be used in practice.
- Create
lua/bootstrap.lua
in your neovim config directory.
-- ~/.config/nvim/lua/bootstrap.lua:
-- automatically install `chiyadev/dep` on startup
local path = vim.fn.stdpath("data") .. "/site/pack/deps/opt/dep"
if vim.fn.empty(vim.fn.glob(path)) > 0 then
vim.fn.system({ "git", "clone", "--depth=1", "https://github.com/chiyadev/dep", path })
end
vim.cmd("packadd dep")
- In
init.lua
, calldep
with an array of package specifications.
require "bootstrap"
require "dep" {
-- list of package specs...
}
:DepSync
- installs new packages, updates packages to the latest versions, cleans removed packages and reloads packages as necessary.:DepClean
- cleans removed packages.:DepReload
- reloads all packages.:DepList
- prints the package list, performance metrics and dependency graphs.:DepLog
- opens the log file.:DepConfig
- opens the file that called dep, for convenience.
A package must be declared in the following format.
{
-- [string] Specifies the full name of the package.
-- This is the only required field; all other fields are optional.
"user/package",
-- [function] Code to run after the package is loaded into neovim.
function()
require "package".setup(...)
end,
-- [function] Code to run before the package is loaded into neovim.
setup = function()
vim.g.package_config = ...
end,
-- [function] Code to run after the package is installed or updated.
config = function()
os.execute(...)
end,
-- [string] Overrides the short name of the package.
-- Defaults to a substring of the full name after '/'.
as = "custom_package",
-- [string] Overrides the URL of the git repository to clone.
-- Defaults to "https://github.com/{full_name}.git".
url = "https://git.chiya.dev/user/package.git",
-- [string] Overrides the name of the branch to clone.
-- Defaults to whatever the remote configured as their HEAD, which is usually "master".
branch = "develop",
-- [boolean] Prevents the package from being loaded.
disable = true,
-- [boolean] Prevents the package from being updated.
pin = true,
-- [string|array] Specifies dependencies that must be loaded before the package.
-- If given a string, it is wrapped into an array.
requires = {...},
-- [string|array] Specifies dependents that must be loaded after the package.
-- If given a string, it is wrapped into an array.
deps = {...}
}
When a string is given where a package specification table is expected, it is assumed to be the package's full name.
require "dep" {
-- these two are equivalent
"user/package",
{ "user/package" },
}
A package can be declared multiple times. Multiple declarations of the same package are combined into one. This is useful when declaring dependencies, which is explored later.
require "dep" {
{
"user/package",
requires = "user/dependency",
disabled = true,
config = function()
print "my config hook"
end
},
{
"user/package",
requires = "user/another_dependency",
deps = "user/dependent",
disabled = false,
config = function()
os.execute("make")
end
}
}
-- the above is equivalent to
require "dep" {
{
"user/package",
requires = { "user/dependency", "user/another_dependency" },
deps = "user/dependent",
disabled = true,
config = function()
print "my config hook"
os.execute("make")
end
}
}
The dependencies and dependents declared in a package specification are themselves package specifications. If a dependency or dependent is declared multiple times, they are combined into one just like normal package specifications.
require "dep" {
{
"user/package",
requires = {
{
"user/dependency1",
requires = "user/dependency2"
}
}
}
}
-- the above is equivalent to
require "dep" {
{
"user/dependency2",
deps = {
{
"user/dependency1",
deps = "user/package"
}
}
}
}
-- which is equivalent to
require "dep" {
{
"user/dependency1",
requires = "user/dependency2",
deps = "user/package"
}
}
-- which is equivalent to
require "dep" {
{
"user/dependency1",
requires = "user/dependency2"
},
{
"user/package",
requires = "user/dependency1"
}
}
-- which is equivalent to
require "dep" {
{
"user/dependency2",
deps = "user/dependency1"
},
{
"user/dependency1",
deps = "user/package"
}
}
-- all of the above are guaranteed to load in the following order: dependency2, dependency1, package
If dep detects a circular dependency cycle, it reports the problematic packages instead of hanging or crashing.
-- this throws an error saying package1 depends on package2 which depends on package1
require "dep" {
{
"user/package1",
requires = "user/package2"
},
{
"user/package2",
requires = "user/package1"
}
}
A dependency can be marked as disabled, which disables all dependents automatically.
require "dep" {
{
"user/dependency",
disabled = true
},
{
"user/package1",
disabled = true, -- implied
requires = "user/dependency"
},
{
"user/package2",
disabled = true, -- implied
requires = "user/dependency"
}
}
If a dependency fails to load for some reason, all of its dependents are guaranteed to not load.
require "dep" {
{
"user/problematic",
function()
error("bad hook")
end
},
{
"user/dependent",
requires = "user/problematic",
function()
print "unreachable"
end
}
}
Suppose you split your init.lua
into two files packages/search.lua
and
packages/vcs.lua
, which declare the packages telescope.nvim and vim-fugitive respectively.
-- ~/.config/nvim/lua/packages/search.lua:
return {
{
"nvim-telescope/telescope.nvim",
requires = "nvim-lua/plenary.nvim"
}
}
-- ~/.config/nvim/lua/packages/vcs.lua:
return {
"tpope/vim-fugitive"
}
Package specifications from other modules can be loaded using the modules
option.
require "dep" {
modules = {
prefix = "packages.",
"search",
"vcs"
}
}
-- the above is equivalent to
require "dep" {
modules = {
"packages.search",
"packages.vcs"
}
}
-- which is equivalent to
local packages = {}
for _, package in ipairs(require "packages.search") do
table.insert(packages, package)
end
for _, package in ipairs(require "packages.vcs") do
table.insert(packages, package)
end
require("dep")(packages)
-- which is ultimately equivalent to
require "dep" {
{
"nvim-telescope/telescope.nvim",
requires = "nvim-lua/plenary.nvim"
},
"tpope/vim-fugitive"
}
-- all of the above are guaranteed to load plenary.nvim before telescope.nvim.
-- order of telescope.nvim and vim-fugitive is consistent but unspecified.
Entire modules can be marked as disabled, which disables all top-level packages declared in that module.
return {
disable = true,
{
"user/package",
disabled = true, -- implied by module
requires = {
{
"user/dependency",
-- disabled = true -- not implied
}
},
deps = {
{
"user/dependent",
disabled = true -- implied by dependency
}
}
}
}
dep accepts configuration parameters as named fields in the package list.
require "dep" {
-- [string] Specifies when dep should automatically synchronize.
-- "never": disable this behavior
-- "new": only install newly declared packages (default)
-- "always": synchronize all packages on startup
sync = "new",
-- [array] Specifies the modules to load package specifications from.
-- Defaults to an empty table.
-- Items can be either an array of package specifications,
-- or a string that indicates the name of the module from which the array of package specifications is loaded.
modules = {
-- [string] Prefix string to prepend to all module names.
prefix = "",
},
-- list of package specs...
}
dep is licensed under the MIT License.