Open-Source Wikis

/

Neovim

/

Features

/

Package manager (`vim.pack`)

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 docs

The 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 by vim.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 log

The 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.pack doesn't manage these but understands their format.
  • vim.version — semver parsing and range matching.
  • vim.system — every git invocation 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.pack is git-only. The internal git_* helpers in pack.lua would 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 data for 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.

Package manager (`vim.pack`) – Neovim wiki | Factory