Skip to content

A use-package inspired plugin manager for Neovim. Uses native packages, written in Lua, allows for expressive config

License

Notifications You must be signed in to change notification settings

tjdevries/packer.nvim

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

packer.nvim

use-package inspired plugin/package management for Neovim.

Features

  • 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

Quickstart

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):

-- We only need to run packer.init() once
local packer = nil
local function init()
  if packer == nil then
    packer = require('packer')
    packer.init()
  end

  local use = packer.use
  -- Clear state from previous operations
  packer.reset()

  -- 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
  use {
    'w0rp/ale',
    ft = {'sh', 'zsh', 'bash', 'c', 'cpp', 'cmake', 'html', 'markdown', 'racket', 'vim', 'tex'},
    cmd = 'ALEEnable',
    config = 'vim.api.nvim_command("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'}
end

-- Hack for convenience: make this module (1) ensure packer.init() is called and (2) re-export all
-- of packer's functions
local plugins = setmetatable({}, {
  __index = function(_, key)
    init()
    return packer[key]
  end
})

return plugins

packer does not provide any commands out of the box, only functions. As such, you may wish to add some commands to control common operations, such as the following. Note that these commands assume you have a plugins.lua similar to the above:

execute 'luafile ' . stdpath('config') . '/lua/plugins.lua'
command! PackerInstall packadd packer.nvim | lua require('plugins').install()
command! PackerUpdate packadd packer.nvim | lua require('plugins').update()
command! PackerSync packadd packer.nvim | lua require('plugins').sync()
command! PackerClean packadd packer.nvim | lua require('plugins').clean()
command! PackerCompile packadd packer.nvim | lua require('plugins').compile('~/.config/nvim/plugin/packer_load.vim')

Usage

The above snippets give some examples of packer features and use. You can find another (stupid) example of use in test_init.vim and a more realistic example in my dotfiles (also this file; here's an example of what packer's autogenerated lazy-loader code looks like)

The following is a more in-depth explanation of packer's features and use.

Initialization

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',
  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
  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
  },
  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
  }
}

Specifying plugins

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

  1. Absolute paths to a local plugin
  2. Full URLs (treated as plugins managed with git)
  3. username/repo paths (treated as Github git plugins)

A table given to use 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
  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
  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 or function,   -- Specifies a conditional test to load this plugin
  setup = string or function,  -- Specifies code to run before this plugin is loaded.
}

Custom installers

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.

Update/install hooks

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:

  1. If the first character of run is ":", it is treated as a Neovim command and executed.
  2. Otherwise, run is treated as a shell command and run in the installation directory of the plugin via $SHELL -c 'cd <plugin dir> && <run>'.

Dependencies

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.

Keybindings

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}.

Performing plugin management operations

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 installed
  • packer.update(plugins): Update the specified plugins, installing any that are missing
  • packer.clean(): Remove any disabled or no longer managed plugins
  • packer.sync(plugins): Perform a clean followed by an update
  • packer.compile(path): Compile lazy-loader code and save to path.

Extending packer

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.

Status

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! You can find a (stupid) example of use in test_init.vim and a more realistic example in my dotfiles (also this file; here's an example of what packer's autogenerated lazy-loader code looks like).

  • Basic package management seems to work (i.e. installation, updating, cleaning, start/opt plugins, displaying results)
  • Automatic generation of lazy-loading code seems to work
  • More testing is needed
  • The code is messy and needs more cleanup and refactoring

Current work

  • Luarocks support

TODO

  • Allow multiple packages
  • Optimizations?

About

A use-package inspired plugin manager for Neovim. Uses native packages, written in Lua, allows for expressive config

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Lua 99.1%
  • Vim Script 0.9%