use-package
inspired plugin/package management for
Neovim.
- 2020-09-04: Due to changes to the Neovim
extmark
api (see: https://github.com/neovim/neovim/commit/3853276d9cacc99a2698117e904475dbf7033383), users will need to update to a version of Neovim after the aforementioned PR was merged. There are currently shims around the changed functions which should maintain support for earlier versions of Neovim, but these are intended to be temporary and will be removed by 2020-10-04. Therefore Packer will not work with Neovim v0.4.4, which was released before theextmark
change.
- Declarative plugin specification
- Support for dependencies
- (soon) Support for Luarocks dependencies
- Expressive configuration and lazy-loading options
- Automatically compiles efficient lazy-loading code to improve startup time
- Uses native packages
- Extensible
- Written in Lua, configured in Lua
- Post-install/update hooks
- Uses jobs for async installation
- Support for
git
tags, branches, revisions, submodules - Support for local plugins
- You need to be running Neovim v0.5.0+;
packer
makes use of extmarks and other newly-added Neovim features. - Your version of Neovim must be compiled with LuaJIT support;
packer
relies on this to detect whether you are running Windows or a Unix-like OS (for path separators) - If you are on Windows 10, you need developer mode enabled in order to use local plugins (
packer
needs to usemklink
, which requires admin privileges - credit to @TimUntersberger for this note)
To get started, first clone this repository to somewhere on your packpath
, e.g.:
git clone https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/opt/packer.nvim
Then you can write your plugin specification in Lua, e.g. (in ~/.config/nvim/lua/plugins.lua
):
-- This file can be loaded by calling `lua require('plugins')` from your init.vim
-- Only required if you have packer in your `opt` pack
vim.cmd [[packadd packer.nvim]]
-- Only if your version of Neovim doesn't have https://github.com/neovim/neovim/pull/12632 merged
vim._update_package_paths()
return require('packer').startup(function()
-- Packer can manage itself as an optional plugin
use {'wbthomason/packer.nvim', opt = true}
-- Simple plugins can be specified as strings
use '9mm/vim-closer'
-- Lazy loading:
-- Load on specific commands
use {'tpope/vim-dispatch', opt = true, cmd = {'Dispatch', 'Make', 'Focus', 'Start'}}
-- Load on an autocommand event
use {'andymass/vim-matchup', event = 'VimEnter *'}
-- Load on a combination of conditions: specific filetypes or commands
-- Also run code after load (see the "config" key)
use {
'w0rp/ale',
ft = {'sh', 'zsh', 'bash', 'c', 'cpp', 'cmake', 'html', 'markdown', 'racket', 'vim', 'tex'},
cmd = 'ALEEnable',
config = 'vim.cmd[[ALEEnable]]'
}
-- Plugins can have dependencies on other plugins
use {
'haorenW1025/completion-nvim',
opt = true,
requires = {{'hrsh7th/vim-vsnip', opt = true}, {'hrsh7th/vim-vsnip-integ', opt = true}}
}
-- Local plugins can be included
use '~/projects/personal/hover.nvim'
-- Plugins can have post-install/update hooks
use {'iamcco/markdown-preview.nvim', run = 'cd app && yarn install', cmd = 'MarkdownPreview'}
-- You can specify multiple plugins in a single call
use {'tjdevries/colorbuddy.vim', {'nvim-treesitter/nvim-treesitter', opt = true}}
-- You can alias plugin names
use {'dracula/vim', as = 'dracula'}
end)
packer
provides the following commands after you've run and configured packer
with require('packer').startup(...)
:
-- You must run this whenever you make changes to your plugin configuration
:PackerCompile
-- Only install missing plugins
:PackerInstall
-- Update and install plugins
:PackerUpdate
-- Remove any disabled or unused plugins
:PackerClean
-- Performs `PackerClean` and then `PackerUpdate`
:PackerSync
You can configure Neovim to automatically run :PackerCompile
whenever plugins.lua
is updated with an autocommand:
autocmd BufWritePost plugins.lua PackerCompile
This autocommand can be placed in your init.vim
, or any other startup file as per your setup.
The above snippets give some examples of packer
features and use. Examples include:
- Very simple:
test_init.vim
in this repo. - A more realistic example in my dotfiles:
- An example using the
startup
method: tjdevries- Using this method, you do not require a "loading" file. You can simply
lua require('plugins')
from yourinit.vim
- Using this method, you do not require a "loading" file. You can simply
The following is a more in-depth explanation of packer
's features and use.
packer
provides packer.startup(spec)
, which is used in the above examples.
startup
is a convenience function for simple setup and can be invoked as follows:
spec
can be a function:packer.startup(function() use 'tjdevries/colorbuddy.vim' end)
spec
can be a table with a function as its first element and config overrides as another element:packer.startup({function() use 'tjdevries/colorbuddy.vim' end, config = { ... }})
spec
can be a table with a table of plugin specifications as its first element and config overrides as another element:packer.startup({{'tjdevries/colorbuddy.vim'}, config = { ... }})
You are not required to use packer.startup
if you prefer a more manual setup with finer control
over configuration and loading.
To take this approach, load packer
like any other Lua module. You must call packer.init()
before
performing any operations; it is recommended to call packer.reset()
if you may be re-running your
specification code (e.g. by sourcing your plugin specification file with luafile
).
You may pass a table of configuration values to packer.init()
to customize its operation. The
default configuration values (and structure of the configuration table) are:
{
ensure_dependencies = true, -- Should packer install plugin dependencies?
package_root = util.is_windows and '~\\AppData\\Local\\nvim-data\\site\\pack' or '~/.local/share/nvim/site/pack',
compile_path = vim.fn.stdpath('config') .. '/plugin/packer_compiled.vim',
plugin_package = 'packer', -- The default package for plugins
max_jobs = nil, -- Limit the number of simultaneous jobs. nil means no limit
auto_clean = true, -- During sync(), remove unused plugins
compile_on_sync = true, -- During sync(), run packer.compile()
disable_commands = false, -- During `startup`, disable creating commands
opt_default = false, -- Default to using opt (as opposed to start) plugins
transitive_opt = true, -- Make dependencies of opt plugins also opt by default
transitive_disable = true, -- Automatically disable dependencies of disabled plugins
git = {
cmd = 'git', -- The base command for git operations
subcommands = { -- Format strings for git subcommands
update = '-C %s pull --ff-only --progress --rebase=false',
install = 'clone %s %s --depth %i --no-single-branch --progress',
fetch = '-C %s fetch --depth 999999 --progress',
checkout = '-C %s checkout %s --',
update_branch = '-C %s merge --ff-only @{u}',
current_branch = '-C %s branch --show-current',
diff = '-C %s log --color=never --pretty=format:FMT --no-show-signature HEAD@{1}...HEAD',
diff_fmt = '%%h %%s (%%cr)',
get_rev = '-C %s rev-parse --short HEAD',
get_msg = '-C %s log --color=never --pretty=format:FMT --no-show-signature HEAD -n 1',
submodules = '-C %s submodule update --init --recursive --progress'
},
depth = 1, -- Git clone depth
clone_timeout = 60, -- Timeout, in seconds, for git clones
},
display = {
open_fn = nil, -- An optional function to open a window for packer's display
open_cmd = '65vnew [packer]', -- An optional command to open a window for packer's display
working_sym = '⟳', -- The symbol for a plugin being installed/updated
error_sym = '✗', -- The symbol for a plugin with an error in installation/updating
done_sym = '✓', -- The symbol for a plugin which has completed installation/updating
removed_sym = '-', -- The symbol for an unused plugin which was removed
moved_sym = '→', -- The symbol for a plugin which was moved (e.g. from opt to start)
header_sym = '━', -- The symbol for the header line in packer's display
show_all_info = true, -- Should packer show all update details automatically?
}
}
packer
is based around declarative specification of plugins. You can declare a plugin using the
function packer.use
, which I highly recommend locally binding to use
for conciseness.
use
takes either a string or a table. If a string is provided, it is treated as a plugin location
for a non-optional plugin with no additional configuration. Plugin locations may be specified as
- Absolute paths to a local plugin
- Full URLs (treated as plugins managed with
git
) username/repo
paths (treated as Githubgit
plugins)
A table given to use
can take two forms:
- A list of plugin specifications (strings or tables)
- A table specifying a single plugin. It must have a plugin location string as its first element, and may additionally have a number of optional keyword elements, shown below:
use {
'myusername/example', -- The plugin location string
-- The following keys are all optional
disable = boolean, -- Mark a plugin as inactive
as = string, -- Specifies an alias under which to install the plugin
installer = function, -- Specifies custom installer. See "custom installers" below.
updater = function, -- Specifies custom updater. See "custom installers" below.
after = string or list, -- Specifies plugins to load before this plugin.
rtp = string, -- Specifies a subdirectory of the plugin to add to runtimepath.
opt = boolean, -- Manually marks a plugin as optional.
branch = string, -- Specifies a git branch to use
tag = string, -- Specifies a git tag to use
commit = string, -- Specifies a git commit to use
lock = boolean, -- Skip this plugin in updates/syncs
run = string or function, -- Post-update/install hook. See "update/install hooks".
requires = string or list -- Specifies plugin dependencies. See "dependencies".
config = string or function, -- Specifies code to run after this plugin is loaded.
-- The following keys all imply lazy-loading
cmd = string or list, -- Specifies commands which load this plugin.
ft = string or list, -- Specifies filetypes which load this plugin.
keys = string or list, -- Specifies maps which load this plugin. See "Keybindings".
event = string or list, -- Specifies autocommand events which load this plugin.
cond = string, function, or list of strings/functions, -- Specifies a conditional test to load this plugin
setup = string or function, -- Specifies code to run before this plugin is loaded.
}
You may specify a custom installer & updater for a plugin using the installer
and updater
keys.
Note that either both or none of these keys are required. These keys should be functions which take
as an argument a display
object (from lua/packer/display.lua
) and return an async function (per
lua/packer/async.lua
) which (respectively) installs/updates the given plugin.
Providing the installer
/updater
keys overrides plugin type detection, but you still need to
provide a location string for the name of the plugin.
You may specify operations to be run after successful installs/updates of a plugin with the run
key. This key may either be a Lua function, which will be called with the plugin
table for this
plugin (containing the information passed to use
as well as output from the installation/update
commands, the installation path of the plugin, etc.), or a string.
If run
is a string, then either:
- If the first character of
run
is ":", it is treated as a Neovim command and executed. - Otherwise,
run
is treated as a shell command and run in the installation directory of the plugin via$SHELL -c 'cd <plugin dir> && <run>'
.
Plugins may specify dependencies via the requires
key. This key can be a string or a list (table).
If requires
is a string, it is treated as specifying a single plugin. If a plugin with the name
given in requires
is already known in the managed set, nothing happens. Otherwise, the string is
treated as a plugin location string and the corresponding plugin is added to the managed set.
If requires
is a list, it is treated as a list of plugin specifications following the format given
above.
If ensure_dependencies
is true, the plugins specified in requires
will be installed.
Plugins specified in requires
are removed when no active plugins require them.
Plugins may be lazy-loaded on the use of keybindings/maps. Individual keybindings are specified either as a string (in which case they are treated as normal mode maps) or a table in the format {mode, map}
.
packer
exposes the following functions for common plugin management operations. In all of the
below, plugins
is an optional table of plugin names; if not provided, the default is "all managed
plugins":
packer.install(plugins)
: Install the specified plugins if they are not already installedpacker.update(plugins)
: Update the specified plugins, installing any that are missingpacker.clean()
: Remove any disabled or no longer managed pluginspacker.sync(plugins)
: Perform aclean
followed by anupdate
packer.compile(path)
: Compile lazy-loader code and save topath
.
You can add custom key handlers to packer
by calling packer.set_handler(name, func)
where name
is the key you wish to handle and func
is a function with the signature func(plugins, plugin, value)
where plugins
is the global table of managed plugins, plugin
is the table for a specific
plugin, and value
is the value associated with key name
in plugin
.
To optimize startup time, packer.nvim
compiles code to perform the lazy-loading operations you
specify. This means that you do not need to load packer.nvim
unless you want to perform some
plugin management operations.
To generate the compiled code, call packer.compile(path)
, where path
is some file path on your
runtimepath
, with a .vim
extension. This will generate a blend of Lua and Vimscript to load and
configure all your lazy-loaded plugins (e.g. generating commands, autocommands, etc.) and save it to
path
. Then, when you start vim, the file at path
is loaded (because path
must be on your
runtimepath
), and lazy-loading works.
If path
is not provided to packer.compile
, the output file will default to the value of
config.compile_path
.
The option compile_on_sync
, which defaults to true
, will run packer.compile()
during
packer.sync()
, if set to true
. Note that otherwise, you must run packer.compile
yourself
to generate the lazy-loader file!
tl;dr: Beta. Things seem to work and most features are complete, but certainly not every edge case has been tested. People willing to give it a try and report bugs/errors are very welcome!
- Basic package management works (i.e. installation, updating, cleaning, start/opt plugins, displaying results)
- Automatic generation of lazy-loading code works
- More testing is needed
- The code is messy and needs more cleanup and refactoring
- Luarocks support
- Allow multiple packages
- Optimizations?