neovim/neovim
Package manager (vim.pack)
Purpose
vim.pack is the built-in plugin manager. It clones plugin repositories from a specification list, manages their versions (tags, branches, commits), keeps an update log, and integrates with Vim's existing :packadd runtime-path mechanism. It removes the chicken-and-egg "you need a plugin manager to install plugin managers" bootstrap step. Introduced in v0.12.
Directory layout
runtime/lua/vim/
├── pack.lua (~53k bytes) Top-level: vim.pack.add, sync, install, ...
├── pack/
│ ├── _meta.lua
│ └── ... Sub-helpers
runtime/doc/pack.txt User docsThe whole feature is Lua. The C side just provides Vim's existing package machinery (runtime/pack/ discovery + :packadd).
Key abstractions
| Function | Description |
|---|---|
vim.pack.add(spec) |
Declare a plugin to install/use. |
vim.pack.update(opts) |
Pull all known plugins to their target version. |
vim.pack.del(name) |
Remove a plugin. |
vim.pack.get(name) |
Get info about an installed plugin. |
vim.pack.toggle(name) |
Disable/enable without uninstalling. |
How it works
A "spec" is a table describing a plugin:
vim.pack.add({
{
src = 'https://github.com/folke/which-key.nvim',
version = '*', -- latest semver tag
-- or version = 'v3.13.0',
-- or version = vim.version.range('>=3 <4'),
},
{ src = 'https://github.com/nvim-lua/plenary.nvim' },
})Specs declare where to fetch from and what version to use. Resolution rules:
- A tag matching a semver range — pick the newest matching tag.
- A literal tag, branch, or commit — pin exactly.
- No version — pick the default branch's latest commit.
Resolution and clone are done by spawning git via vim.system. The output is parsed; the result is stored in stdpath('data') .. '/pack/. The runtime-path is updated so the plugin is :source-able from init.lua.
sequenceDiagram
participant Init as init.lua
participant Pack as vim.pack
participant FS as Filesystem
participant Git as git
Init->>Pack: vim.pack.add({...})
Pack->>FS: scan stdpath('data')/site/pack
Pack->>Pack: diff installed vs requested
par for each new plugin
Pack->>Git: git clone <src>
and for each existing
Pack->>Git: git fetch / version-check (background)
end
Pack->>Pack: link into runtimepath
Pack->>Init: returns
Init->>Init: require('plugin').setup(...)Update flow
:lua vim.pack.update() walks the install root, fetches each plugin, and presents a confirmation buffer with the diff. The user can :write to apply or :q to abort. The diff/update buffer is implemented via floating windows and is pure Lua.
Versioning
Versions can be pinned by:
- A literal tag or branch name.
- A
vim.version.range(spec)— semver range parsed byvim.version(runtime/lua/vim/version.lua). - A literal commit sha.
Tags are listed via git tag --list and parsed with vim.version.parse. Non-semver tags are ignored when a range is specified.
Disable / enable
vim.pack.toggle(name) flips a flag in the install record without removing the directory. Disabled plugins are not added to the runtimepath. Useful for bisecting plugin issues.
Storage layout
$XDG_DATA_HOME/nvim/site/pack/core/opt/<plugin>/ # one directory per plugin
$XDG_DATA_HOME/nvim/site/pack/core/.lock # lockfile (versions)
$XDG_STATE_HOME/nvim/pack/ # update logThe structure follows Vim's existing pack/<group>/{start,opt}/<plugin>/ convention so a plugin installed by vim.pack is interchangeable with one installed by hand or by another package manager.
Integration points
- Vim's
:packadd— used internally to lazy-load opt plugins. runtime/pack/dist/opt/— bundled plugins shipped with Neovim itself follow the same layout.vim.packdoesn't manage these but understands their format.vim.version— semver parsing and range matching.vim.system— everygitinvocation goes through it.vim.fs— directory walking and atomic moves.stdpath('data')— install root.- Lockfile — JSON record of installed plugins, their requested versions, and resolved commits. Generated/updated by
vim.pack.
Entry points for modification
- Add a new VCS backend. Currently
vim.packis git-only. The internalgit_*helpers inpack.luawould need to be abstracted. - Tweak the update UI. The diff/update buffer is constructed in
pack.lua. Floating window options are knobs. - Per-plugin options (post-install hooks, build). The spec table accepts
datafor arbitrary user data; expanding the post-install hook surface is a Lua-only change.
Key source files
| File | Purpose |
|---|---|
runtime/lua/vim/pack.lua |
Top-level public API and the bulk of the implementation |
runtime/lua/vim/version.lua |
Semver parsing, used by version pinning |
runtime/lua/vim/system.lua |
Process spawning helper |
runtime/lua/vim/fs.lua |
Directory walking |
runtime/doc/pack.txt |
User-facing docs |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.