Writing my own Vim plugin manager
2013. 09. 18.

As the title says, I've finally ended up writing my own Vim plugin manager called vim-plug, so here's what happened.

State of the art

Today, we already have a whole range of Vim plugin managers, from the minimalistic, the father of all, Pathogen from the great tpope, to Vundle with which you get a nice usability improvement, and to seemingly the most fully-featured NeoBundle.

There are also a number of minorities such as vim-addon-manager and oh-my-vim, both of which manage the lists of available Vim plugins.

My choice was Vundle. I really admire the simplicity of Pathogen, but I don't want to manage git submodules, and having the explicit list of plugins in my .vimrc gives me a better sense of control. Later I found that NeoBundle could be a nice alternative, but realized that I didn't need any of its extra features at that point, so I decided to stick with Vundle.

Growing impatience

The number of plugins I use has been increased over time. Now I have more than 30 plugins specified in my .vimrc.

Now with 30 of them, it takes a considerable amount of time to update the plugins, or install them on a new machine. Maybe I'm exaggerating the problem a bit since it still takes less than 5 minutes. I could just fire up a new tmux session and do something else while they're being done.

It was obvious that the time could be greatly reduced if I could update plugins in parallel using multiple threads or processes. The simplest method would be to write a shell script that spawns multiple background processes each of which updates a plugin.

#!/bin/bash

cnt=0
cd ~/.vim/bundle &&
for d in *; do
  if [ -d $d/.git ]; then
    echo "Updating $d"
    cd $d && git pull &
    # Prevent having too many subprocesses
    (( (cnt += 1) % 16 == 0 )) && wait
  fi
done
wait

This simple script works pretty well if you have already installed the plugins. Maybe this is how it should be done considering that Vim in itself does not have any notion of parallel execution. But this irrational Vim addict just couldn't stop wanting to do the whole thing inside Vim without any external scripts.

Although Vim itself does not have threads or background processes, there are a couple of workarounds.

  1. NeoBundle supports parallel installation using multiple processes. However that requires compilation of another plugin called vimproc from the same author. As my instinct told me to stay away from any compiled extension and I wanted to simplify the initial setup process as much as possible, I looked for another option.

  2. With Ruby/Python interface we can create threads. I personally prefer Ruby and the current version of Mac OS X is shipped with a Ruby-enabled Vim, so I decided to write a parallel update procedure in Ruby.

And yes, it worked. You can see my first, now deprecated, prototype implementation here, which uses g:bundles variable from Vundle.

The next step

The obvious next step was to merge this parallel update procedure into Vundle, so if a user is running a Ruby-enabled Vim, Vundle will automatically install/update plugins in parallel. So many people would benefit from this update! But sadly that didn't happen. I couldn't find a place for my code in the Vundle codebase without introducing a major overhaul. Much of its logic relies too much on the content of the status window. And it was instantly obvious that it would require too much effort to make it flexible enough to embrace my code.

I could just stop there. Using Vundle as it is with my own parallel update function. But I then realized that I had accidentally reimplemented the core part of Vundle by writing that piece of code. Well then, why not just build my own custom-tailored plugin manager that is easier for me to use and gives me just what I need? So that's how vim-plug started.

Goals and achivements

I wanted my own plugin manager to be as follows:

To summarize, it's positioned somewhere between Pathogen and Vundle, but with the new parallel update feature.

vim-plug is indeed easier to setup. To install vim-plug you just need to download a single file from GitHub and put it in ~/.vim/autoload.

mkdir -p ~/.vim/autoload
curl -fLo ~/.vim/autoload/plug.vim \
  https://raw.github.com/junegunn/vim-plug/master/plug.vim

And it doesn't require any unfathomable boilerplate code in your .vimrc, you just write down the list of plugins with Plug command between plug#begin() and plug#end(). That's all you have to do. (And you see, Plug is 2-byte shorter than Bundle, so you save a huge amount of disk space!)

call plug#begin()

Plug 'junegunn/seoul256'
Plug 'junegunn/vim-easy-align'
" Plug 'user/repo', 'branch_or_tag'
" Plug 'git@github.com:junegunn/vim-github-dashboard.git'
" ...

call plug#end()

Now you are loaded with the essential PlugInstall, PlugUpdate, PlugClean, and PlugUpgrade commands.

PlugInstall and PlugUpdate will install and update plugins in parallel if you're running a Ruby-enabled Vim. It gives me an order of magnitude speedup.

Notice that the status window doesn't look as nice as that of Vundle, but well, that's good enough for me, for now.

Status of vim-plug

vim-plug works for me. And that's the whole point. I will not add any extra feature that I don't think I need just because somebody wants it. I don't expect people to abandon their current plugin managers and adopt vim-plug, and it is highly likely that I will be the only one using it in the end, but I don't care. It was fun building it, and I learned a lot more about Vim in the process.

If you're still curious try it, fork it, or even try writing a yet another plugin manager for yourself. Why? Because you can, and it's a lot of fun.

» capture | close