{Portable, Maintainable, Easy-to-use} - Choose Three

tl;dr See the template at github.com/VVVFO/vim-configuration-template

As you may know, I’m a huge fan of Vim. Just like most other heavy Vim users out there, I have been maintaining a non-trivial amount of Vim configurations. As I use more than one machine and would often need to set up new ones, I have been looking for an easy way of managing, syncing, and deploying my Vim configurations. Here’s my solution that achieves:

  1. Easy deployment on new machines (./setup.sh and voila)
  2. Easy plugin management (powered by Vundle)
  3. Easy syncing and version control (with git)

Inspired by Casecommons/vim-config.

Note: if you use Vim without any plugins, this may not be right for you as there are simpler ways to do that. This solution aims at managing plugins efficiently across multiple devices.

Table of Contents

  1. Directory Structure
  2. Workflows
    1. Setting up on a New Machine
    2. Installing/Removing a Plugin
    3. Updating a Plugin
  3. What’s in setup.sh?
    1. Check and Execute
    2. Neovim Configuration
  4. Template Project

Directory Structure

The configuration is managed in a git repo with the following structure:

├── bundle
│   ├── L9
│   ├── Vundle.vim
│   ├── ... and many more plugin directories
│   ├── vim-pandoc-syntax
│   ├── vim-repeat
│   └── vim-surround
├── gvimrc
├── setup.sh
└── vimrc

I’ll go through what each one of these files do.

First, setup.sh. It does the following things:

  1. Symlink $HOME/.vim directory to the current directory
  2. Initialize Vundle with git submodule init and git submodule update
  3. Install plugins declared in vimrc by executing vim +PluginInstall +qall (starting up Vim and let Vundle install everything, then quit)
  4. (If you use Neovim) Configure Neovim by creating a init.vim that sources vimrc

vimrc and gvimrc are just normal configuration files (they don’t need to be linked to ~/.vimrc and ~/.gvimrc, Vim will pick them up as long as they are in ~/.vim/.

bundle/ is a bit interesting. In .gitmodules, the Vundle submodule is defined to be located at bundle/Vundle.vim, and after Vundle being cloned by setup.sh, Vundle will install all the other plugins under bundle/.


For reference and a demonstration of how easy everything is.

Setting up on a New Machine

As mentioned earlier, setting up is as easy as ./setup.sh because it does all the work for you.

git clone your_repo
cd your_repo

You will need to hit Enter once in the process.

Installing/Removing a Plugin

  1. Add/remove a line like Plugin "vim-airline/vim-airline" in your vimrc (standard Vundle configuration)
  2. Run :PluginInstall or :PluginClean
  3. Commit and push (and do step 2 on other machines after pulling in the changes)

Updating a Plugin

Just run :PluginUpdate.

What’s in setup.sh?

A minimal version of setup.sh looks like this (note that this is only an untested proof-of-concept and this is not the version that I use):

# Link basic configuration
ln -s $PWD $HOME/.vim

# Install Vundle
git submodule init
git submodule update

# Install plugins
vim +PluginInstall +qall

Note that you don’t need to link vimrc to ~/.vimrc (same for gvimrc) as long as they are under ~/.vim/.

However, from there I also wanted to let this repo manage IdeaVIM’s .ideavimrc, Neovim’s init.vim, Jupyter Notebook’s custom.js, … Things were getting messy, the following sections explains what I did for managing these.

Note: you can stop reading from here if that’s enough for you, the following section mainly discusses the management of other Vim-related configuration files like .ideavimrc.

Check and Execute

In the minimal example above, there’s an ln -s $PWD $HOME/.vim. What if a .vim/ directory already exists? This issue is not exclusive to symlinking, for example, as Jupyter Notebook don’t pick up symlinks so you need to actually copy the file. In order to automate that process of “check existence and take actions”, I wrote this bash function:

# Check for the existence of a file/path, and executed the command if it does not exist.
# Does file exist?
#   Yes: Do you want to rename it to filename.old?
#     Yes: Rename and execute command
#     No: Abort and do nothing
#   No: Execute command
# $1: file/directory to check
# $2: command to run (with eval)
check_existence_and_do () {
    # If target already exists
    if [[ -e $1 ]]
        echo "Warning: $1 already exists!"
        read -p "Do you want me to rename $1 to $1.old in order to link the new file? (y/n): " -r
        if [[ $REPLY =~ ^[Yy]$ ]]
            mv "$1" "$1.old"
            echo "Renamed $1 to $1.old"
            echo "Aborting."

    # Made sure that $2 does not exist
    eval "$2"

So with this function, we can change:

ln -s $PWD $HOME/.vim


check_existence_and_do "$HOME/.vim" "ln -s $PWD $HOME/.vim"

You get the idea.

Neovim Configuration

By default Neovim uses init.vim instead of vimrc, but they are compatible. Therefore with check_existence_and_do, we can just do:

mkdir -p "$HOME/.config/nvim"
check_existence_and_do "$HOME/.config/nvim/init.vim" "echo 'set runtimepath^=~/.vim runtimepath+=~/.vim/after
let &packpath = &runtimepath
source $PWD/vimrc' > '$HOME/.config/nvim/init.vim'"

Template Project

I have created a template configuration project at github.com/VVVFO/vim-configuration-template