Chris Bailey

Micromanaging my Vim configuration

Every few months or so; I always find myself going back to my Vim configuration and either adding new things which solve irksome problems I've noticed as of late, or I end up removing a tonne of stuff I don't use.

The last time I did this, a colleague of mine convinced me to put more effort into micromanaging—architecting—my configuration to make it saner, compositional and easier to manage after months of not looking at it. When I came to revisit it today, I ended up being eternally greatful and decided it'd be nice to share the kind of things I'm doing.

Unifying Vim and Neovim

I personally use Neovim instead of Vim. I made the switch a few years ago when I felt the need to asynchronous plugins prior to Vim8 and I just never felt the need to change back.

Therefore, I keep my configuration in $XDG_DATA_HOME/.config/nvim/init.vim instead of $XDG_DATA_HOME/.vimrc but I've discovered that in an attempt (perhaps a futile one, I'm not sure) to minimize friction, it's possible to essentially source the .../init.vim in your vimrc with:

let s:config_dir = exists('$XDG_CONFIG_DIR') ? $XDG_CONFIG_DIR : $HOME . '/.config'
let s:data_dir = exists('$XDG_DATA_HOME') ? $XDG_DATA_HOME : $HOME . '/.local/share'

exec 'set runtimepath^='.s:config_dir.'/nvim,'.s:data_dir.'/nvim/site'
exec 'set packpath^='.s:data_dir.'/nvim/site'

syntax on
filetype plugin indent on

set laststatus=2
set wildmenu

runtime init.vim

Initial configuration

The high level idea behind this approach is that we should be splitting up Vim's configuration into different places for different concerns.

My top level Vim configuration file literally does nothing except:

  1. Set up vim-plug to enable me to easily use plugins
  2. Set up reading in configuration from different places

This is what my init.vim looks like:

" ===============================================
" # Configure Plug + Plugins               
" ===============================================
call plug#begin('~/.config/nvim/bundle')

" General stuff
Plug 'scrooloose/nerdtree'
Plug 'jistr/vim-nerdtree-tabs'
Plug 'neoclide/coc.nvim', {'branch': 'release'}
Plug 'easymotion/vim-easymotion'
Plug 'majutsushi/tagbar'
Plug 'ludovicchabant/vim-gutentags'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-repeat'
Plug 'tpope/vim-eunuch'
Plug 'machakann/vim-swap'

" Setup fzf
Plug '/usr/local/opt/fzf'
Plug '~/.fzf'
Plug 'junegunn/fzf.vim'

" Git plugins
Plug 'Xuyuanp/nerdtree-git-plugin'
Plug 'airblade/vim-gitgutter'
Plug 'tpope/vim-fugitive'

" Erlang plugins
Plug 'vim-erlang/vim-erlang-tags'         , { 'for': 'erlang' }
Plug 'vim-erlang/vim-erlang-omnicomplete' , { 'for': 'erlang' }
Plug 'vim-erlang/vim-erlang-compiler'     , { 'for': 'erlang' }
Plug 'vim-erlang/vim-erlang-runtime'      , { 'for': 'erlang' }

" Elixir plugins
Plug 'elixir-editors/vim-elixir' , { 'for': 'elixir' }
Plug 'mhinz/vim-mix-format'      , { 'for': 'elixir' }

" Handlebar Templates
Plug 'mustache/vim-mustache-handlebars' , { 'for': 'html' }

" Bunch of nice themes
Plug 'flazz/vim-colorschemes'

call plug#end()

" ===============================================
" # Load all additional configuration files
" ===============================================
for config in split(glob('~/.config/nvim/config/*.vim'), '\n')
  exe 'source' config

filetype plugin indent on 

The for ... in ... loop under # Load all additional configuration files just recursively loads all .vim files under the $XDG_DATA_HOME/.config/nvim/config. Whenever I need to configure a particular plugin or language (for syntax highlighting, tabs versus spaces etc) I simply add a <THING>.vim file under that directory.

My nvim/config directory currently at the time of writing looks like the following, for example:

├── coc_config.vim
├── ctag_config.vim
├── easymotion_config.vim
├── editor_config.vim
├── elixir_config.vim
├── erlang_config.vim
├── fzf_config.vim
├── jsonc_config.vim
└── nerdtree_config.vim

I might even take this a step further and split up my configuration files into different domains such as nvim/config/plugins/ nvim/config/languages/. Right now, editor_config.vim contains all of my top level Vim configuration (i.e. set mouse=a amongst other things), which might very well end up going in nvim/config/config.vim.

Language configuration

You can look at my elixir_config.vim language configuration to see how I write configuration for specific programming languages:

function! LoadElixirSettings()
  set tabstop=2
  set softtabstop=2
  set shiftwidth=2
  set textwidth=80
  set expandtab
  set autoindent
  set fileformat=unix

au BufNewFile,BufRead *.ex,*.exs,*.eex call LoadElixirSettings()

" Mix format on save
let g:mix_format_on_save = 1

Plugin configuration / Editor configuraton

These end up just being normal viml files such as:

" Ctags
set tags=./.tags;/
nnoremap <C-Space><Return> <C-]>
nnoremap <C-Space><BS> <C-t>
nnoremap <C-Space><C-Space> :silent !ctags -f .tags -R .<return><return><C-l>
nnoremap <C-m> :TagbarToggle<CR>

" Elixir Tagbar
let g:tagbar_type_elixir = {
    \ 'ctagstype' : 'elixir',
    \ 'kinds' : [
        \ 'f:functions',
        \ 'functions:functions',
        \ 'c:callbacks',
        \ 'd:delegates',
        \ 'e:exceptions',
        \ 'i:implementations',
        \ 'a:macros',
        \ 'o:operators',
        \ 'm:modules',
        \ 'p:protocols',
        \ 'r:records',
        \ 't:tests'
    \ ]
\ }

" Autocompletion
let g:deoplete#enable_at_startup = 1
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"

" Ignore these filetypes
set wildignore+=*.pdf,*.o,*.obj,*.bin,*.jpg,*.png,*.beam


I find that this lets me better manage my configuration and know where and how to update them / remove them if the need arises.

If you're interested, you can look through my configuration via my dotfiles 😅

Return to Posts →