Migrating to Vim Plug

I use lots of plugins with Vim - over 50 at the moment! In Shuogo's words I'm a dark powered Vim user who's trying to do everything from inside Vim. The upside of plugins is that they add capabilities to Vim, the downside is they can negatively impact Vim's performance and the plugins have to be managed themselves. I've been handling plugins with NeoBundle but it's no longer being developed so I'm migrating to Vim-plug. This is a bit of a no-op blog post since everyone else migrated years ago!

Vim Plug, a plugin manager for the Vim editor

Figure 1 (click to enlarge): Vim plug with my plugins!

Vim-plug seems to be the most popular plugin manager these days. Most plugins are now installed from Github which vim-plug handles well, and the overall user experience is good. Increasingly, plugins are using external binaries which vim-plug can handle by running commands during the plugins installation. The main difference from NeoBundle is that the developer doesn't really encourage the use of on-demand loading. There's also no dependency management between plugins which means you have to pay attention.

Using a temporary Vimrc

Migrating to the new set-up took a few days, I used a new vimrc temporarily and progressively added my plugins, making sure everything worked correctly. To use a different .vimrc:

$ cp ~/.vimrc ~/.vimrc-vimplug

# run vim with the following
$ vim -u ~/.vimrc-vimplug

One thing to note is that you must turn on vim's extensions if you use an alternative .vimrc. I didn't know this and kept getting odd errors from various functions as it was running in compatible mode. To change this add the following to the temporary .vimrc:

set nocompatible
set encoding=UTF-8

Using Vim-plug

The installation instructions on the Vim-plug GitHub page are pretty comprehensive. To install a plugin we define it in our .vimrc:

" Installs a plugin from github - https://github.com/vim-airline/vim-airline
Plug 'vim-airline/vim-airline'

This is the simplest form, where we've defined the plugins source as a Github location. Then to install it start Vim and run the command :PlugInstall. Use :PlugStatus to see the overall status of plugins, this is really useful when playing with on-demand loading.

In some cases the actual plugin is in a subdirectory of the Github URL, to specify that subdirectory you can use the rtcp option according to the docs: I've seen this with colour themes like vim-solarized.

Plugins can also be loaded on-demand at runtime. This should speed up performance as there's less to source and load when you start Vim. There are a couple of ways to do this with vim-plug:

" On-demand loading when a command is called
Plug 'sjl/gundo.vim', {'on': 'GundoToggle'}

" On-demand loading with multiple commands
Plug 'jeetsukumaran/vim-buffergator', {'on': ['BuffergatorOpen', 'BuffergatorToggle']}

" On-demand when a particular mapping is called
Plug 'dhruvasagar/vim-zoom', {'on': ['<Plug>(zoom-toggle)']}
nmap <F3> <Plug>(zoom-toggle)

" On-demand loading for a file type
Plug 'luochen1990/rainbow', {'for': ['clojure']}

I commonly load plugins when a particular command is called. There are two examples to show that you can either have a single command, or if you want to specify multiple commands use square brackets around the list.

The third ones shows using a plugins <Plug> mapping. In this case I'm setting up a mapping for the F3 button which calls zoom-toggle. This is a <Plug> mapping that the plugin defines itself, and I'm remapping it.

The last example shows using file type detection to load a plugin. As there's just one file type (Clojure) square brackets around the list aren't really needed.

Reproducibility

Vim-plug's base assumption is that users continuously install new versions of plugins from the master branch. The risk is that if a plugin breaks then a core workflow is broken - a day when I can't use my Vimwiki for work is a serious problem for me! What we need is to define precisely what is installed so we can reinstall it reproducibly.

Vim Plug showing the PlugDiff output

Figure 3: PlugDiff with a reproducible definition

Vim-plug offers a solution to this which is the :PlugSnapshot command which generates a script for restoring the current plugins.

" in Vim we take a snapshot
:PlugSnapshot ~/.vim/20210912-plug-snapshot

# run this on the command line to
# install this version of the plugins
$ vim -S ~/.vim/20210912-plug-snapshot

Note that the file it creates uses the specific commits that were being used for the plugins - this makes the install reproducible. If we run this command before we do :PlugUpdate then it's easy to return to a known good configuration.

Another approach is we can review updates before they are done and be explicit about allowing them. With this method we guarantee an installation is repeatable by telling vim-plug the specific git commit or tag to use for the install.

" use a specific commit or tag
Plug 'farmergreg/vim-lastplace', { 'commit': '48ba343'}
Plug 'farmergreg/vim-lastplace', { 'tag': 'v3.2.1'}

" use the v3.4 tag release of vim-fugitive
Plug 'tpope/vim-fugitive', {'tag': 'v3.4'}

Initially, when using a tag it will complain when PlugUpdate is run because it hasn't pulled the tag down yet with git. Run the command twice if needed.

To check what updates are available do PlugDiff. For the plugins where a specific version or tag was specified there is a section called Pending updates that shows the updates from the version or commit you've specified to HEAD on the master branch.

- vim-sneak: 7d82982
  * 95374ad label-mode: remove highlights on fall-through (5 months ago)
  * b999e0a doc: change to sneaker emoji (5 months ago)

Here we have the plugin name (vim-sneak) and the version we've pinned it at (7d82982), there are newer commits on the master branch (9537ad) which we could update with. It's nice to have a split with your .vimrc in, then you can update the plugin commits/tags in real time. The last step being to commit the .vimrc to git with the details of plugins that have been updated.

The second method is more long-winded but I like it because it's explicit. I use it for plugins like coc.nvim where I've seen bugs on the main branch, or vimwiki which is really important for my day job workflow.

Post update hooks

Vim-plug supports post update hooks which lets it run commands after the plugins install is done. This is often done when you need to run external commands.

For example, the vim-clap plugin is a general overlay window plugin. It contains a Rust binary that has to be compiled with cargo: Vim-plug can handle this with a do command:

" this will compile Clap if Cargo is on the system, otherwise it downloads the prebuild binary
" you can also do :call clap#installer#download_binary()
Plug 'liuchengxu/vim-clap', { 'do': ':Clap install-binary!', 'on': ['Clap'], 'for': ['clojure'] }

The do command will run the specified Vim command, you can also run external scripts such as Make.

Disabling a plugin

To disable a plugin simply comment it out in .vimrc and restart vim:

"Temporarily comment out a plugin
"Plug 'vim-scripts/sessionman.vim', {'rtcp': 'plugin'}
"Plug 'mhinz/vim-startify'

The files for the plugin are still in the plugin directory (by default ~/.vim/plugged), to remove these from within Vim do :PlugClean.

Upgrading vim-plug

Vim-plug itself is pretty stable, the last full release was a couple of years ago. To upgrade Vim-plug itself use the command:

PlugUpgrade

Final thoughts

There aren't that many resources on Vim-plug as it's pretty simple to use. It's worth reading the developers post on Writing your own Vim plugin manager. Overall, vim-plug is working well - as the GitHub page says it's a minimalist Vim plugin manager that's easy to set-up and use. The main missing feature I would have liked is dependencies, and it may be me but I've found loading on filetype to be a bit error prone.

It might seem that there's not much more development to be done on Plugin managers for Vim! However, Neovim and Vim 8.0 have standardised on a new package format that plugin managers like packer.nvim and vim-packager use. The advantage of this is that all plugins would use the same plugins paths and installs. For the moment I'm not ready to move.


Posted in Tech Saturday 01 May 2021
Tagged with tech vim