Skip to content

Latest commit

Β 

History

134 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“¦ pack.nvim

A modern, high-performance Neovim plugin manager that leverages Neovim's built-in native package management (:help packages, vim.pack) while providing a rich, interactive, floating-window UI.

pack.nvim Dashboard

Unlike traditional native pack managers (like minpac or paq-nvim), pack.nvim focuses on developer experience with a beautiful dashboard, non-blocking asynchronous git operations, and real-time log streaming.

βœ… Requirements

  • Neovim 0.12+ β€” pack.nvim delegates all cloning, checkout, updating, pinning, and lockfile management to Neovim's built-in vim.pack API, which only exists in 0.12 and later. On an older Neovim, setup() warns and does nothing.
  • git on your PATH.

✨ Features

  • Native Backend: All plugins install under vim.pack's directory (<stdpath("data")>/site/pack/core/opt). Every plugin (lazy or not) is packadd-ed explicitly through pack rather than relying on Neovim's start/ auto-load, so lazy loading and ordered eager loading are fully under pack's control.
  • Native Git, Async Probes: Clone / checkout / update / pinning are handled by native vim.pack. pack.nvim layers on non-blocking, concurrency-limited read-only git probes (via vim.system) purely to power the dashboard's "outdated" indicator and commit preview.
  • Optional Background Git (use_git): With use_git = true, installs and updates run as backgrounded git jobs (via vim.system), so the UI stays responsive during large transfers; native vim.pack then runs cheaply afterwards β€” with objects already local β€” purely to register plugins and keep the lockfile in sync. Bulk updates (U/S/:Pack sync/:Pack update) are batched at a maximum of 5 plugins per call.
  • Interactive UI Dashboard: A centralized floating window showing real-time plugin statuses, log streaming, and pending-commit previews for outdated plugins.
  • Reproducible installs: Version pinning (branch / tag / commit / semver version ranges) is resolved to a native vim.pack spec; native owns the lockfile, and :Pack restore rolls every plugin back to it.
  • Persistent Disable State: Disabling a plugin (via x in the dashboard or set_disabled()) persists state to nvim-pack-extra.json without editing your raw Lua config.
  • Performance Caching: Pre-compiles lazy plugins' ftdetect files into a single cache block, sourced at startup so their filetypes are detected before the plugin loads.
  • Lazy Loading: Supports cmd, event (with patterns), ft (filetype), and keys (keymap) triggers to load plugins right when you need them. When a spec sets both ft and keys, the keymaps are never bound globally β€” they exist only in buffers whose filetype matches, before and after the plugin loads.
  • Modular Configuration: Keep your config clean by using { import = "plugins" } to split specs across multiple files.
  • Fine-Grained Loading Control: Toggle plugins with enabled or cond, and guarantee eager-load order using priority.
  • Help Tags: :help tags are generated automatically for every managed plugin's doc/ directory on load.
  • Health Check: Run :checkhealth pack to verify your Neovim version, git, the install directory, per-plugin status, and orphaned directories.

πŸš€ Installation & Bootstrapping

pack.nvim leverages Neovim 0.12's native vim.pack for bootstrapping. Add this snippet to the top of your init.lua:

-- 1. Enable Neovim's built-in bytecode cache (must be on Line 1 for full caching benefits)
vim.loader.enable()

vim.g.mapleader = " "
vim.g.maplocalleader = " "

-- 2. Bootstrap pack.nvim using Neovim's native vim.pack
vim.pack.add({ { src = "https://github.com/igmrrf/pack.nvim", branch = "main" } })
vim.cmd.packadd("pack.nvim")

-- 3. Initialize pack.nvim with options and plugin specs
require("pack").setup({
  performance = {
    vim_loader = true, -- Fallback to ensure vim.loader.enable() is called if omitted
  },
  use_git = false, -- Clone/update via backgrounded `git` so the UI never blocks; native vim.pack still syncs the lockfile afterwards
  ui = {
    border = "rounded", -- Options: "single", "double", "rounded", "solid", "shadow"
    auto_open = true,  -- Automatically open dashboard float after the first plugin install completes
    silent = nil,      -- Silences native vim.pack cmdline messages (defaults to auto_open setting)
    filter = "default", -- Options: "default" (vim.ui.input), "input" (vim.fn.input), or fun(opts, cb)
    icons = {
      loaded = "●",
      not_loaded = "β—‹",
      error = "βœ–",
      sync = "β†Ί",
      queued = "β—Œ",
    },
  },
  plugins = {
    { "igmrrf/pack.nvim" }, -- So pack.nvim can manage itself (updates, status, lockfile)
    { import = "plugins" },  -- Import specs from lua/plugins/* (files can call vim.pack.add or return a spec table)
  },
})

require("configs")

For alternative installation patterns (e.g. vim.uv.fs_stat fallback, raw git cloning), see the examples/ directory.

Adopting existing vim.pack plugins

pack.nvim and native vim.pack share the exact same install directory (<stdpath("data")>/site/pack/core/opt/<name>), so any plugin already installed via vim.pack.add() is recognized as installed immediately, with no re-clone β€” just list it in your spec:

require("pack").setup({
  plugins = {
    { "nvim-lua/plenary.nvim" }, -- already on disk from vim.pack.add() -> just gets packadd'd
  },
})

After setup(), pack.nvim replaces the global vim.pack.add/vim.pack.update/vim.pack.del with lazy-aware wrappers, so you can keep calling vim.pack.add({ ... }) and it flows through pack.nvim's loader. The install location and lockfile are owned by native vim.pack and are not configurable.

Known limitations when mixing styles

  • Raw vim.pack.add before setup(), then declared with config/opts: if a plugin is installed/activated via a raw vim.pack.add() call before require('pack').setup() runs, and is also declared in your plugins spec with a config or opts, that config will not run. Native vim.pack already considers the plugin active and early-returns when pack.nvim's wrapper re-adds it, so our loader never gets a chance to fire. Either declare such plugins only through pack.nvim, or avoid raw pre-setup() adds for anything that needs a config.
  • Imperative imports vs. declarative priority ordering: eager-load priority ordering is only guaranteed among declarative plugins. Plugins registered via imperative vim.pack.add calls inside { import = ... } files load in import order during setup(), not as part of the global priority sort.
  • First registration wins: if the same plugin is registered both by an imperative vim.pack.add during an import and by a declarative spec, the first registration wins and the later spec's fields (lazy/config/keys/opts) are silently ignored; declare each plugin once, in one style.

πŸ“‹ Plugin Specification

Plugin specifications can be defined as shorthand strings ("owner/repo"), tables, or URLs. Here is a comprehensive reference of supported spec keys:

Key Type Description
[1] / src string Plugin repository ("owner/repo"), full Git URL, or local path.
as / name string Custom name or directory alias for the plugin.
dir string Path to a local development plugin (bypasses git cloning).
lazy boolean When true, defers loading until triggered by cmd, event, ft, keys, or require().
priority number Load order priority for eager plugins (higher values load first, default 50).
enabled boolean|fun():boolean Toggle to enable or completely skip this plugin spec.
cond boolean|fun(plugin):boolean Conditional expression or callback function to gate plugin loading.
main string Overrides the target module name passed to opts auto-setup.
cmd string|table User command(s) that trigger lazy loading.
ft string|table Filetype(s) that trigger lazy loading.
event string|table Autocmd event(s) or pattern(s) that trigger lazy loading (e.g. "BufReadPre").
pattern string|table Pattern filter string or table for autocmd event lazy triggers.
keys string|table Keymap shortcut(s) that trigger lazy loading or register keybindings. Combined with ft, the keymaps are scoped buffer-locally to matching filetypes instead of being bound globally.
module string Custom module name for require() trigger tracking.
dependencies table List of dependent plugin specs loaded prior to this plugin.
init fun(plugin) Callback executed BEFORE the plugin is loaded (useful for setting vim.g options).
opts table Options table automatically passed to require(main).setup(opts).
config fun(plugin, opts)|true Custom callback executed AFTER the plugin is loaded (overrides default opts behavior). If true, invokes setup() without arguments.
build string|fun(plugin) Shell command or Lua function executed post-install / update.
branch string Track a specific git branch.
tag string Pin to a specific git tag.
commit string Pin to a specific git commit hash.
version string Pin to a semver version range (e.g. "^1.0.0").
category string Category metadata tag for filtering in the dashboard (/cat:lsp).
tags string|table Custom tag string or table of tags for dashboard filtering (/tag:ui).

πŸ”Œ Lua API

pack.nvim exposes programmatic Lua helper functions for configuration and key mapping:

  • require("pack").setup(opts): Initializes pack.nvim with user configuration and plugin specs.
  • require("pack").add(specs): Programmatically registers and installs new plugin specs post-startup.
  • require("pack").map_keys(keys): Registers a list of keymaps in a single call:
    require("pack").map_keys({
      { "<leader>e", "<cmd>Oil<cr>", desc = "Open Oil" },
      { "<leader>gg", function() require("snacks").lazygit() end, desc = "Lazygit", mode = { "n", "v" } },
    })
  • require("pack.state").set_disabled(name, disabled): Programmatically enables or disables a plugin and persists state to nvim-pack-extra.json.
  • require("pack").status(): Returns a table containing { total = <number>, loaded = <number>, disabled = <number> }.
  • require("pack").stats(): Returns a formatted status summary string (e.g. "πŸ“¦ 12/45 loaded").
  • require("pack").picker(): Displays an interactive plugin selector using snacks.picker (or vim.ui.select fallback) to navigate directly to plugin directories.

🎨 Lualine Integration

pack.nvim includes a built-in extension for lualine.nvim:

require("lualine").setup({
  extensions = { "pack" },
})

⚑ Advanced Capabilities & Examples

For full configuration examples and migration guides, see the examples/ directory:

πŸ’» Commands

Command Description
:Pack Opens the interactive dashboard UI to view current plugin status.
:Pack sync Updates all managed plugins via native vim.pack.
:Pack update [names...] Updates the given plugins (any count; batched at most 5 per native call), or all plugins if no name is given.
:Pack clean Removes plugin directories no longer referenced in your configuration.
:Pack restore Rolls every plugin back to the native vim.pack lockfile.
:Pack repair Realigns lockfile (nvim-pack-lock.json) revisions to installed plugin HEAD commits.
:Pack build [name] Re-runs the build hook for one plugin (or all plugins).
:Pack load <name> Immediately loads a lazy plugin.
:Pack delete <name> Removes a plugin from state and deletes it via native vim.pack.
:Pack profile Displays the startup profile with visual bar charts showing plugin load times.
:Pack diff Displays a structured diff of pending commits for outdated plugins before updating.
:Pack picker Opens interactive plugin picker (snacks.picker / vim.ui.select) to edit plugin files.

Subcommands with a <name> argument tab-complete against your configured plugins.

πŸ“Έ UI Showcase

Dashboard View Plugin Quick Details
All Plugins
All Plugins
Detailed Info Popup (<Enter> / K)
Plugin Info
Outdated Updates View
Outdated Plugins
Outdated Plugin Info & Diff
Outdated Info
Disabled Plugins View
Disabled Plugins
Disabled Plugin Info
Disabled Info

⌨️ Dashboard Keymaps

When inside the dashboard (opened via :Pack), you can use the following keymaps:

  • q - Close the dashboard or any popup window.
  • ? - Show the interactive keymap help popup.
  • S - Sync all managed plugins (install missing & pull updates; batched at most 5 plugins per update call).
  • s - Sync the single plugin under the cursor.
  • C - Clean unmanaged/deleted plugin directories.
  • Tab / Shift-Tab (or 1/2/3) - Cycle or jump directly across dashboard tabs (Plugins -> Updates -> Disabled).
  • <Space> - Toggle selection state for the plugin under the cursor.
  • v - Toggle selection UI mode or clear active selections.
  • <CR> - Toggle inline plugin details expansion.
  • K - Open full detail popup tailored to the current tab (branch, HEAD commit, revision info).
  • l - Show streaming git output logs for the plugin under the cursor.
  • p - Display the startup load time profiling chart (:Pack profile).
  • d - Delete the plugin under the cursor from disk.
  • D - Delete all disabled plugins from disk.
  • x - Toggle disable/enable for the plugin under the cursor. An already-loaded plugin requires a Neovim restart to fully unload.
  • c - Check for outdated plugins (concurrency-limited git fetch).
  • u - Update the selected or cursor plugin.
  • U - Update all outdated plugins (batched at most 5 plugins per update call).
  • f - Filter the dashboard by plugin name, category (cat:lsp), or tag (tag:ui).

Each tab also renders a contextual quick-help bar at the bottom with tab-relevant action shortcuts.

πŸ“Š Comparison Matrix

Feature / Dimension pack.nvim lazy.nvim pckr.nvim paq-nvim vim-plug
Minimum Neovim 0.12+ (Requires vim.pack) 0.8+ 0.7+ 0.5+ Vim 7.4 / Neovim 0.2+
Backend Engine Native C (vim.pack) Custom Lua engine Native packpath Native packpath Custom Vimscript engine
Storage Location <stdpath("data")>/site/pack/core/opt <stdpath("data")>/lazy <stdpath("data")>/site/pack/pckr/opt <stdpath("data")>/site/pack/paq/opt ~/.config/nvim/plugged
Lockfile Format Native nvim-pack-lock.json Custom lazy-lock.json Custom lockfile None None (snapshots)
Codebase Size ~2,000 lines of Lua ~20,000+ lines of Lua ~3,500 lines of Lua ~600 lines of Lua ~2,700 lines of Vimscript
Lazy Loading Triggers cmd, event, ft, keys, cond cmd, event, ft, keys, cond, custom cmd, event, ft, keys, cond None (Eager / packadd only) on (cmd), for (ft)
Dependency Support Yes (dependencies) Yes (dependencies) Yes (requires) No No (manual order)
Modular Specs Yes ({ import = "..." }) Yes ({ import = "..." }) No No No
Precompiled ftdetect Cache Yes (pack_ftdetect_cache.lua) Yes No No No
Interactive Dashboard Floating UI with tabs, / search, commit diffs Full-featured floating UI dashboard Minimal floating log buffer Minimal log buffer Vim split buffer
Startup Profiling Built-in (:Pack profile ASCII charts) Built-in (Timeline breakdown) Built-in (:Pckr profile) None Built-in (:PlugStatus)
Build Hooks build (shell, :cmd, fn, list) build (shell, :cmd, fn, list) run (shell, fn, list) build (shell, fn) do (shell, fn)
Native Plugin Adoption Yes (adopts disk plugins via vim.pack.get) No (requires managed dir) Partial Partial No
Maintenance Model Feature-frozen (stable & bug fixes) Active development Maintenance Minimal / Stable Maintenance

πŸ”’ Maintenance & Feature Policy

pack.nvim is feature-complete, fully tested, and production-ready.

To preserve its zero-dependency model, ultra-fast startup, and rock-solid stability, the codebase is under a strict feature freeze:

  • Bug fixes & Neovim compatibility updates are actively maintained.
  • New core features will only be considered if requested and endorsed by 10 or more active users via GitHub Issues/Discussions.

πŸ™ Acknowledgements

Several declarative spec and lazy-loading features (such as import, programmatic conditionals, advanced event pattern matching, and context-aware hook variables) were heavily inspired by zpack.nvim and its foundational homage to lazy.nvim. pack.nvim combines these elegant spec configurations with a rich asynchronous floating dashboard on top of Neovim's native vim.pack backend.

About

A modern, high-performance Neovim plugin manager that leverages Neovim's built-in native package management

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages