In my time using Vim, I switched between MacVim, regular Vim and NeoVim, project drawers and fuzzy file finding, Janus and custom configurations, and so on. My current configuration is a result of that journey which tries to stay light on plugins and key mapping, while still providing a somewhat modern editor configuration.

My .vimrc file is extracted from this document1, meaning both should remain in sync. Unless I’m experimenting with something locally that I haven’t pushed yet, this document describes the configuration I’m currently using in my editor.


Recent versions of Vim caught up to most features NeoVim introduced, like true color support, background jobs and the inline terminal. However, NeoVim comes with sensible defaults out of the box by implementing most of vim-sensible by default.

As a good starting point, either install both Vim and vim-sensible, or NeoVim. This configuration chooses the latter.


brew install neovim
nvim --version | head -n1
NVIM v0.8.3


Vim and NeoVim use different locations for the configuration file and home directory. This configuration is checked out in Vim’s default home directory, which contains the configuration file:

Table 1: Home directory and configuration file locations for Vim, NeoVim and this configuration
  home directory configuration file
Vim ~/.vim ~/.vimrc
NeoVim ~/.config/nvim ~/.config/nvim/init.vim
This configuration ~/.vim ~/.vim/.vimrc

To use this configuration, clone its repository to Vim’s home directory path:

git clone git@github.com:jeffkreeftmeijer/.vim.git ~/.vim

To support NeoVim, symlink NeoVim’s home directory and configuration file paths to Vim’s:

mkdir -p ~/.config/
ln -s ~/.vim ~/.config/nvim
ln -s ~/.vim/.vimrc ~/.config/nvim/init.vim
ln -s ~/.vim/.vimrc ~/.vimrc

Package management

Vim 8 and NeoVim have a native package manager, which loads plugins from the ~/.vim/pack/plugins/start directory2. To install a package, clone its repository into ~/.vim/pack/plugins/start:

git clone git@github.com:tpope/vim-commentary.git \

When publishing dotfiles to a repository for reuse, it’s convenient to use Git submodules instead of regular checkouts. This allows for checking the dependencies into version control. Instead of cloning, add a plugin as a submodule:

git submodule add git@github.com:tpope/vim-commentary.git \

Another option for installing plugins is using a plugin manager like vim-plug. Instead of using Git checkouts or submodules, vim-plug handles the installing and updating of plugins.

The plugins themselves are configured through a list of calls to the Plug function in the ~/.vimrc file:

Plug 'tpope/vim-commentary'

This configuration uses the latter in an attempt to contain most of the configuration in a single file.


Vim-plug’s documentation mentions installing the plugin into the autoload directory directly by running a script that downloads plug.org to the ~/.vim/autoload directory:

curl -fLo ~/.vim/autoload/plug.vim --create-dirs \

Instead, to have Vim install vim-plug on startup (again, to contain the configuration to a single file), this snippet automatically downloads plug.vim from its repository if it doesn’t exist yet:

" Download plug.vim if it doesn't exist yet
if empty(glob('~/.vim/autoload/plug.vim'))
  silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs
    \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

Normally, plugins are installed when calling :PlugInstall from within Vim. To remove an extra step from the setup, this configuration runs a script on startup that checks for missing packages on startup and install them if needed:

" Run PlugInstall if there are missing plugins
autocmd VimEnter * if len(filter(values(g:plugs), '!isdirectory(v:val.dir)'))
  \| PlugInstall --sync | source ~/.vimrc
\| endif

All other plugins are installed through vim-plug.

Installing packages

To install plugins with vim-plug, call the Plug function inside the plug-block:

call plug#begin("~/.vim/plugged")

" TODO Add plugins

call plug#end()

The plug#begin("~/.vim/plugged") function sets up the plugin directory3 and the plug#end() function initializes the plugin system.

To install a plugin, call the Plug function inside the plug-block with an URL to a git repository:

Plug 'tpope/vim-commentary'

The user/repository assumes the plugin is hosted on GitHub.

Creating and restoring package snapshots

To write a lock file based on the currently installed plugin versions, run :PlugSnapshot ~/.vim/snapshot.vim inside Vim. This creates a snapshot file in the specified path, which lists all installed plugins with their commit hashes.

" Generated by vim-plug
" Sat Aug 14 14:14:55 2021
" :source this file in vim to restore the snapshot
" or execute: vim -S snapshot.vim

silent! let g:plugs['coc.nvim'].commit = '6a9a0ee38d2d28fc978db89237cdceb40aea6de3'
silent! let g:plugs['fzf'].commit = '7191ebb615f5d6ebbf51d598d8ec853a65e2274d'
silent! let g:plugs['fzf.vim'].commit = 'e34f6c129d39b90db44df1107c8b7dfacfd18946'
silent! let g:plugs['vim-commentary'].commit = '349340debb34f6302931f0eb7139b2c11dfdf427'
silent! let g:plugs['vim-dim'].commit = '8320a40f12cf89295afc4f13eb10159f29c43777'
silent! let g:plugs['vim-nightfall'].commit = '47c7c74e9ce605581a1492ed163b6b3ae7604c48'
silent! let g:plugs['vim-numbertoggle'].commit = '075b7478777e694fbac330ee34a74590dad0fee1'
silent! let g:plugs['vim-polyglot'].commit = 'ce31cd1d2f4e8eee9fd91325e4599f15cb9566fd'
silent! let g:plugs['vim-surround'].commit = 'f51a26d3710629d031806305b6c8727189cd1935'


To restore from a snapshot, source the snapshot file inside Vim:

:source ~/.vim/snapshot.vim


Vim uses the “unnamed” register as the clipboard when copying or deleting text from a buffer. To use the system clipboard, prefix the copy or delete command with "*. For example, to copy the current line to the system clipboard, use "*yy.

To always use the system clipboard, append unnamedplus to the clipboard setting:

" Always use the system clipboard
set clipboard+=unnamedplus

With this setting, yy copies the current line to the system clipboard, without needing to add the "* prefix.


This configuration consists of nine packages installed with vim-plug and configuration for some of the packages. The vim-plug plugin block lists all nine:

call plug#begin("~/.vim/plugged")
Plug 'sheerun/vim-polyglot'
Plug 'jeffkreeftmeijer/vim-dim'
Plug 'jeffkreeftmeijer/vim-nightfall'
Plug 'jeffkreeftmeijer/vim-numbertoggle'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-commentary'
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'
call plug#end()

The rest of this section lists each plugin and describes its configuration.


Polyglot is a curated and frequently updated list list of more than 600 language packs4 to auto-load syntax highlighting and indentation rules. Although the packs are downloaded on install, they’re loaded on demand, so using Polyglot does not affect Vim’s startup time.

Plug 'sheerun/vim-polyglot'


Vim’s default color scheme uses hardcoded color values (comments and some keywords are light blue, for example) that ignore the terminal’s set ANSI colors. Dim is a clone of Vim’s default colorscheme, with some improvements. It only uses ANSI colors, so specific color values are configured in the terminal emulator instead of in Vim itself.

Dim’s syntax highlighting is consistent to prevent color shifts between dark and light backgrounds. It also makes sure to use dimmed colors for comments and other non-code elements, to help distinguishing code from everything else.

Plug 'jeffkreeftmeijer/vim-dim'

After installing Dim, use it as the default colorscheme by setting colorscheme in ~/.vimrc:

" Use Dim as the default color scheme
colorscheme dim


Nightfall automatically switches Vim’s bg between “dark” and “light” based on macOS’s dark mode.

Plug 'jeffkreeftmeijer/vim-nightfall'

Surround.vim and commentary.vim

Tim Pope’s plugins are a staple of most Vim configurations. This configuration includes Tim’s surround.vim and commentary.vim for working with surroundings and comments.

The former enables s, allowing for ci" to replace the contents of a double-quoted string, among many other examples.

The latter adds quick commenting. Press gcc to comment out a line and gc to comment out a selection.

Plug 'tpope/vim-surround'
Plug 'tpope/vim-commentary'


Vim has absolute, relative and “hybrid” line numbers to help with locating lines in a file, and moving between files quickly. Vim-numbertoggle is a plugin that automatically switches between absolute and hybrid line numbers when switching between normal and insert mode, or when Vim loses focus.

Plug 'jeffkreeftmeijer/vim-numbertoggle'
" Turn on line numbers
set number


Coc.nvim is a language server plugin to add code completion, inline documentation and compiler checks.

Plug 'neoclide/coc.nvim', {'branch': 'release'}

After installing Coc.nvim, set g:coc_global_extensions to add language server extensions for Elixir, Ruby, Rust, Typescript and VimL:

" Install Coc extensions for Elixir, Ruby, Rust, Typescript and VimL
let g:coc_global_extensions = ['coc-elixir', 'coc-solargraph', 'coc-rls', 'coc-tsserver', 'coc-vimlsp']

Automatically format Elixir, Rust and Typescript files on save by setting g:coc_user_config instead of using Coc’s default JSON configuration file:

" Automatically format Elixir, Rust and Typescript files on save
let g:coc_user_config = {"coc.preferences.formatOnSaveFiletypes": ["elixir", "rust", "typescript"]}

Finally, use <cr> to select the topmost option during completion:

" Use <cr> to select the first completion
inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : "\<C-g>u\<CR>"


Fzf.vim is a Vim plugin for the fzf command-line fuzzy finder. It provides the :Files, :Buffers and :Rg commands to find and filter files, buffers, and lines, respectively.

Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'

  1. Initially, this configuration didn’t exist in this form, but I’ve published my Vim configuration in one way or another since 2010. This document is the current revision.

  2. The name of the start directory in ~/.vim/pack/plugins/start can be anything, but “start” seems to make sense.

  3. Vim-plug uses the ~/.config/nvim/plugged directory by default, but passing a different path to plug#begin() overwrites the plugin directory.

  4. Polyglot should have all languages you need. For language packs that aren’t yet included, add them by sending a pull request. For example, this pull request adds support for Gleam through gleam.vim.