Pack
Nvim :help pages, generated from source using the tree-sitter-vimdoc parser.
Extending Nvim
Using Vim packages
be downloaded as an archive and unpacked in its own directory, so the files are not mixed with files of other plugins. be a git, mercurial, etc. repository, thus easy to update. contain multiple plugins that depend on each other. contain plugins that are automatically loaded on startup ("start" packages, located in "pack/*/start/*") and ones that are only loaded when needed with :packadd ("opt" packages, located in "pack/*/opt/*"). A Vim "package" is a directory that contains plugin s. Compared to normal plugins, a package can...
runtime-search-path
Nvim searches for 1. all paths in 'runtimepath' 2. all "pack/*/start/*" dirs Nvim searches for :runtime files in:
" List all runtime dirs and packages with Lua paths. :echo nvim_get_runtime_file("lua/", v:true) Using a package and loading automatically Note that the "pack/*/start/*" paths are not explicitly included in 'runtimepath' , so they will not be reported by ":set rtp" or "echo &rtp". Scripts can use nvim_list_runtime_paths() to list all used directories, and nvim_get_runtime_file() to query for specific files or sub-folders within the runtime path. Example:
% mkdir -p ~/.local/share/nvim/site/pack/foo % cd ~/.local/share/nvim/site/pack/foo % unzip /tmp/foopack.zip The directory name "foo" is arbitrary, you can pick anything you like. Let's assume your Nvim files are in "~/.local/share/nvim/site" and you want to add a package from a zip archive "/tmp/foopack.zip":The directory name "foo" is arbitrary, you can pick anything you like.
pack/foo/README.txt pack/foo/start/foobar/plugin/foo.vim pack/foo/start/foobar/syntax/some.vim pack/foo/opt/foodebug/plugin/debugger.vim On startup after processing your You would now have these files under ~/.local/share/nvim/site:On startup after processing your config , Nvim scans all directories in 'packpath' for plugins in "pack/*/start/*", then loads the plugins.
To allow for calling into package functionality while parsing your vimrc :colorscheme and autoload will both automatically search under 'packpath' as well in addition to 'runtimepath' . See the documentation for each for details.
In the example Nvim will find "pack/foo/start/foobar/plugin/foo.vim" and load it.
If the "foobar" plugin kicks in and sets the 'filetype' to "some", Nvim will find the syntax/some.vim file, because its directory is in the runtime search path.
Nvim will also load ftdetect files, if there are any.
Note that the files under "pack/foo/opt" are not loaded automatically, only the ones under "pack/foo/start". See pack-add below for how the "opt" directory is used.
Loading packages automatically will not happen if loading plugins is disabled, see load-plugins
To load packages earlier, so that plugin/ files are sourced: :packloadall This also works when loading plugins is disabled. The automatic loading will only happen once.
If the package has an "after" directory, that directory is added to the end of 'runtimepath' , so that anything there will be loaded later.
Using a single plugin and loading it automatically
% mkdir -p ~/.local/share/nvim/site/pack/foo/start/foobar % cd ~/.local/share/nvim/site/pack/foo/start/foobar % unzip /tmp/someplugin.zip You would now have these files: pack/foo/start/foobar/plugin/foo.vim pack/foo/start/foobar/syntax/some.vim From here it works like above. If you don't have a package but a single plugin, you need to create the extra directory level:You would now have these files:From here it works like above.
Optional plugins pack-add
To load an optional plugin from a pack use the :packadd command: :packadd foodebug This searches for "pack/*/opt/foodebug" in To load an optional plugin from a pack use thecommand:This searches for "pack/*/opt/foodebug" in 'packpath' and will find ~/.local/share/nvim/site/pack/foo/opt/foodebug/plugin/debugger.vim and source it.
This could be done if some conditions are met. For example, depending on whether Nvim supports a feature or a dependency is missing.
:packadd! foodebug The extra "!" is so that the plugin isn't loaded if Nvim was started with You can also load an optional plugin at startup, by putting this command in your config The extra "!" is so that the plugin isn't loaded if Nvim was started with --noplugin
It is perfectly normal for a package to only have files in the "opt" directory. You then need to load each plugin when you want to use it.
Where to put what
Since color schemes, loaded with :colorscheme , are found below "pack/*/start" and "pack/*/opt", you could put them anywhere. We recommend you put them below "pack/*/opt", for example "~/.config/nvim/pack/mycolors/opt/dark/colors/very_dark.vim".
:packadd . E.g. depending on the compiler version: if foo_compiler_version > 34 packadd foo_new else packadd foo_old endif The "after" directory is most likely not useful in a package. It's not disallowed though. Filetype plugins should go under "pack/*/start", so that they are always found. Unless you have more than one plugin for a file type and want to select which one to load with. E.g. depending on the compiler version:The "after" directory is most likely not useful in a package. It's not disallowed though.
Creating Vim packages
This assumes you write one or more plugins that you distribute as a package.
If you have two unrelated plugins you would use two packages, so that Vim users can choose what they include or not. Or you can decide to use one package with optional plugins, and tell the user to add the preferred ones with :packadd .
Decide how you want to distribute the package. You can create an archive or you could use a repository. An archive can be used by more users, but is a bit harder to update to a new version. A repository can usually be kept up-to-date easily, but it requires a program like "git" to be available. You can do both, github can automatically create an archive for a release.
start/foobar/plugin/foo.vim " always loaded, defines commands start/foobar/plugin/bar.vim " always loaded, defines commands start/foobar/autoload/foo.vim " loaded when foo command used start/foobar/doc/foo.txt " help for foo.vim start/foobar/doc/tags " help tags opt/fooextra/plugin/extra.vim " optional plugin, defines commands opt/fooextra/autoload/extra.vim " loaded when extra command used opt/fooextra/doc/extra.txt " help for extra.vim opt/fooextra/doc/tags " help tags Your directory layout would be like this:
mkdir ~/.local/share/nvim/site/pack cd ~/.local/share/nvim/site/pack git clone https://github.com/you/foobar.git myfoobar Here "myfoobar" is a name that the user can choose, the only condition is that it differs from other packages. This allows for the user to do:Here "myfoobar" is a name that the user can choose, the only condition is that it differs from other packages.
:packadd! fooextra You could add this packadd command in one of your plugins, to be executed when the optional plugin is needed. In your documentation you explain what the plugins do, and tell the user how to load the optional plugin:You could add this packadd command in one of your plugins, to be executed when the optional plugin is needed.
:helptags command to generate the doc/tags file. Including this generated file in the package means that the user can drop the package in the pack directory and the help command works right away. Don't forget to re-run the command after changing the plugin help: :helptags path/start/foobar/doc :helptags path/opt/fooextra/doc Dependencies between plugins packload-two-steps
Suppose you have two plugins that depend on the same functionality. You can put the common functionality in an autoload directory, so that it will be found automatically. Your package would have these files: Run thecommand to generate the doc/tags file. Including this generated file in the package means that the user can drop the package in the pack directory and the help command works right away. Don't forget to re-run the command after changing the plugin help:Suppose you have two plugins that depend on the same functionality. You can put the common functionality in an autoload directory, so that it will be found automatically. Your package would have these files:
call foolib#getit() pack/foo/start/two/plugin/two.vim call foolib#getit() pack/foo/start/lib/autoload/foolib.vim func foolib#getit() This works, because start packages will be searched for autoload files, when sourcing the plugins. pack/foo/start/one/plugin/one.vimpack/foo/start/two/plugin/two.vimpack/foo/start/lib/autoload/foolib.vimThis works, because start packages will be searched for autoload files, when sourcing the plugins.
Plugin manager
WORK IN PROGRESS built-in plugin manager! Early testing of existing features is appreciated, but expect breaking changes without notice.
vim.pack-directory $XDG_DATA_HOME/nvim/site/pack/core/opt . $XDG_DATA_HOME/nvim/site needs to be part of vim.pack . Manages plugins only in a dedicated(see packages ):needs to be part of 'packpath' . It usually is, but might not be in cases like --clean or setting $XDG_DATA_HOME during startup. Plugin's subdirectory name matches plugin's name in specification. It is assumed that all plugins in the directory are managed exclusively by
Uses Git to manage plugins and requires present git executable of at least version 2.36. Target plugins should be Git repositories with versions as named tags following semver convention v.. .
Example workflows
vim.pack.add({ -- Install "plugin1" and use default branch (usually `main` or `master`) 'https://github.com/user/plugin1', -- Same as above, but using a table (allows setting other options) { src = 'https://github.com/user/plugin1' }, -- Specify plugin's name (here the plugin will be called "plugin2" -- instead of "generic-name") { src = 'https://github.com/user/generic-name', name = 'plugin2' }, -- Specify version to follow during install and update { src = 'https://github.com/user/plugin3', -- Version constraint, see |vim.version.range()| version = vim.version.range('1.0'), }, { src = 'https://github.com/user/plugin4', -- Git branch, tag, or commit hash version = 'main', }, }) -- Plugin's code can be used directly after `add()` plugin1 = require('plugin1') Add vim.pack.add() call(s) to 'init.lua': Basic install and management:
add() call. Restart Nvim (for example, with :restart ). Plugins that were not yet installed will be available on disk in target state aftercall. To update all plugins with new changes: Execute vim.pack.update() . This will download updates from source and show confirmation buffer in a separate tabpage. Review changes. To confirm all updates execute :write . To discard updates execute :quit
Update 'init.lua' for plugin to have desired version . Let's say, plugin named 'plugin1' has changed to vim.version.range('*') . :restart . The plugin's actual state on disk is not yet changed. Execute vim.pack.update({ 'plugin1' }) . Review changes and either confirm or discard them. If discarded, revert any changes in 'init.lua' as well or you will be prompted again next time you run vim.pack.update() Switch plugin's version:
Update 'init.lua' for plugin to have version set to current commit hash. You can get it by running vim.pack.update({ 'plugin-name' }) and yanking the word describing current state (looks like abc12345 ). :restart. Freeze plugin from being updated:
Update 'init.lua' for plugin to have version set to whichever version you want it to be updated. :restart. Unfreeze plugin to start receiving updates:
Use vim.pack.del() with a list of plugin names to remove. Make sure their specs are not included in vim.pack.add() call in 'init.lua' or they will be reinstalled. Remove plugins from disk:
Available events to hook into PackChangedPre - before trying to change plugin's state. PackChanged - after plugin's state has changed.
kind - one of "install" (install on disk), "update" (update existing plugin), "delete" (delete from disk). spec - plugin's specification with defaults made explicit. path - full path to plugin's directory. Each event populates the following event-data fields:
Fields: {src} ( string ) URI from which to install and pull updates. Any format supported by git clone is allowed. {name} ( string ) Name of plugin. Will be used as directory name. Default: src repository name. {version} ( string|vim.VersionRange ) Version to use for install and updates. Can be: nil (no value, default) to use repository's default branch (usually main or master ). String to use specific branch, tag, or commit hash. Output of vim.version.range() to install the greatest/last semver tag inside the version constraint. {data} ( any ) Arbitrary data associated with a plugin.
{specs} , {opts} ) vim.pack.add()
Add plugin to current session For each specification check that plugin exists on disk in vim.pack-directory If exists, do nothing in this step. If doesn't exist, install it by downloading from src into name subdirectory (via git clone ) and update state to match version (via git checkout ). load function) making it reachable by Nvim. For each plugin execute :packadd (or customizablefunction) making it reachable by Nvim. add(Add plugin to current session
Notes: Installation is done in parallel, but waits for all to finish before continuing next code execution. version can be not the one actually present on disk. Execute If plugin is already present on disk, there are no checks about its present state. The specifiedcan be not the one actually present on disk. Execute vim.pack.update() to synchronize. Adding plugin second and more times during single session does nothing: only the data from the first adding is registered.
Parameters: {specs} ( (string|vim.pack.Spec)[] ) List of plugin specifications. String item is treated as src . {opts} ( table? ) A table with the following fields: {load} ( boolean|fun(plug_data: {spec: vim.pack.Spec, path: string}) ) Load plugin/ files and ftdetect/ scripts. If false , works like :packadd! . If function, called with plugin data and is fully responsible for loading plugin. Default false during startup and true afterwards. {confirm} ( boolean ) Whether to ask user to confirm initial install. Default true .
{names} ) vim.pack.del()
Remove plugins from disk del(Remove plugins from disk
Parameters: {names} ( string[] ) List of plugin names to remove from disk. Must be managed by ) List of plugin names to remove from disk. Must be managed by vim.pack , not necessarily already added to current session.
vim.pack.get()
Get data about all plugins managed by get()Get data about all plugins managed by vim.pack
Return: ( table[] ) A list of objects with the following fields: {spec} ( vim.pack.SpecResolved ) A ) A vim.pack.Spec with defaults made explicit. {path} ( string ) Plugin's path on disk. {active} ( boolean ) Whether plugin was added via ) Whether plugin was added via vim.pack.add() to current session. ) A list of objects with the following fields:
{names} , {opts} )
Update plugins Download new changes from source. Infer update info (current/target state, changelog, etc.). Depending on force : If false , show confirmation buffer. It lists data about all set to update plugins. Pending changes starting with > will be applied while the ones starting with < will be reverted. It has special in-process LSP server attached to provide more interactive features. Currently supported methods: gO via 'textDocument/documentSymbol' (via lsp-defaults or vim.lsp.buf.document_symbol() ) - show structure of the buffer. K via 'textDocument/hover' (via lsp-defaults or vim.lsp.buf.hover() ) - show more information at cursor. Like details of particular pending change or newer tag. Execute :write to confirm update, execute :quit to discard the update. If true , make updates right away. update(Update plugins
Notes: Every actual update is logged in "nvim-pack.log" file inside "log" stdpath()