Vim Setup: 2019

Neovim Icon Vim icon

It's been a little while since I posted about my editor configuration1, and I thought I might post what I'm using now. I guess the most notable change is that (after much prodding from my coworker Drew Ditthardt) I've switched from Vim to Neovim. Neovim is a vim-compatible editor written in C and Lua (as opposed to Vim, which is written in C, Vimscript, and prayers). I upgraded to Vim 8 last year and have had a few too many segmentation faults in the editor, so I decided to switch to something where more functionality was implemented in a memory-safe language. So far, Neovim has been pretty good to me, although the new process model means that it's pretty hard to write functions which invoke an external process which takes interactive input from a user.

As is probably expected for this sort of thing, here's a couple of screenshots; the first is of VimR, and the second is from NeoVim in Terminal.app, both editing files from rust-mysql-binlog:

VimR editing a file Neovim editing a file

(the font in both cases is Adobe's lovely Source Code Pro, which I use anywhere I have a Retina/Hi-DPI screen)

If you decide to follow in my footsteps, the first step is to install Neovim. I'm using version 0.3.4 on all of my sytems. It's in Homebrew if you're on macOS, and if you're on Linux it's pretty easy to compile. I ended up writing a specfile which vendorizes/statically links most of the dependencies because I'm lazy and didn't want to backport a dozen other libraries, but I've seen configurations where people do it "the right way". If you're on macOS and you prefer a GUI editor, the best choice by far is VimR, which is the only good non-Electron Neovim GUI for macOS.

Plugins

Once you have the editor installed and are able to launch it with nvim, it's time to get configuring! The neovim configuration lives in $HOME/.config/nvim/init.nvim. The first thing I do is install a plugin manager; specifically, vim-plug. If you're comfortable installing something from Github sight-unseen, the installation is easy:

curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

Once this is done, you can install plugins by putting them into your init.nvim file, re-sourcing it ( :source ~/.config/nvim/init.nvim), and running :PlugInstall). Here's my plugin block:

call plug#begin('~/.local/share/nvim/plugged')
Plug 'junegunn/vim-easy-align'
Plug 'mhinz/vim-grepper'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-fugitive'
Plug 'tpope/vim-abolish'
Plug 'tpope/vim-vinegar'
Plug 'neomake/neomake'
Plug 'editorconfig/editorconfig-vim'
Plug 'ervandew/supertab'
Plug 'itchyny/lightline.vim'
Plug 'mkalinski/vim-lightline_neomake'
Plug 'airblade/vim-rooter'
Plug 'Roguelazer/neovim-fuzzy', { 'branch': 'prompt' }
Plug 'markonm/traces.vim'
Plug 'rodjek/vim-puppet'
Plug 'cespare/vim-toml'
Plug 'haishanh/night-owl.vim'
Plug 'jeffkreeftmeijer/vim-dim'
call plug#end()

WARNING: Vim plugins can execute arbitrary code as your user on your system. There is no sandboxing in any version of Vim that I'm aware of. Don't add a plugin if you aren't comfortable with the maintainer potentially having shell on your system. At the very least, look at the source and read the diffs before just blindly running :PlugInstall or :PlugUpdate. Eventually someone high profile is going to be breached through a vimscript plugin and it's going to be sad for all of us.

Aww jeez that's a lot of plugins! What do these do?

vim-easy-align
Automate aligning things around equal signs or => rockets or whatever your convention is. I actually kind of hate the style where you align code around equal signs, but if you're stuck in a codebase that does it, this plugin makes it quasi-tolerable.
vim-grepper
I don't like the built-in :grep command in Vim, and this is a replacement. I don't actually use it much -- I've trained myself to do :!rg query even though it's strictly worse.
vim-surround
I don't understand why these adjectives haven't just been added to Vim at some point. If you write anything that has quotes or parenthesis, you should have this plugin
vim-fugitive
tpope's great git frontend for Vim. I rarely use it for staging or committing2, but I do like :Gblameover git blame
vim-abolish
I only use this for :Subvert
vim-vinegar
Some improvements to netrw which, again, should just be upstream
neomake
This plugin is probably the main reason to use Neovim over Vim (aside from stability). It provides a much better set of tools for doing asynchronous building and linting. The difference is especially huge when working in a language like Rust where compilation is, um, not fast.
editorconfig-vim
Allows Vim/Neovim to read editorconfig files, which we use at my work.
supertab
I actually have no idea what this plugin adds on top of built-in omnicompletion. Maybe I should drop it and see what happens?
lightline
A prettier statusline that doesn't require installing a custom font and isn't written in Python.
vim-lightline-neomake
Just adds Neomake status to lightline
vim-rooter
Possibly my most important plugin. Automatically changes the working directory to the root of the project of the file being edited (guessing what that root is with a bunch of heuristics). Particularly handy in concert with the next plugin in the list.
neovim-fuzzy
Adds commands for searching for files using fzy. This is my fork of it because upstream seems kind of dead.
traces
I am addicted to this plugin! It previews searches and substitutions in real-time. I know it's probably a terrible way to use vi and is just throwing my CPU cycles out the window, but it's so damned user-friendly!
vim-puppet
Neovim doesn't ship with a syntax definition for Puppet (although normal Vim does). This plugin has a bunch of other stuff I don't use, but it also has syntax highlighting and formatting rules.
vim-toml
Syntax rules for TOML. This may have been merged into Neovim already.
night-owl
The theme I use in VimR.
vim-dim
The theme I use in my terminal Vim instance. This uses the defined terminal colors, which I find to be a much better experience than having the editor have its own color family. It's just some slight tweaks to the default colorscheme.

Other Important Settings

Here are some other things from my Neovim config that I think are helpful

" relative line numbers are far more useful than absolute line numbers
" with vi motions
set relativenumber

" use , as the leader
let mapleader = ","

" how often do you type these sequences anyway?
inoremap jk <ESC>
inoremap jj <ESC>
inoremap kj <ESC>

" faster buffer access
nnoremap <leader>n :bp<cr>
nnoremap <leader>m :bn<cr>
nnoremap <up> :buffers<cr>:buffer
nnoremap <down> :b#<cr>
nnoremap <left> :bp<cr>
nnoremap <right> :bn<cr>

" indentation settings -- unless otherwise specified by a filetype, use 4-space tab and expanded tabs
set shiftwidth=4
set softtabstop=4
set tabstop=4
set autoindent
set expandtab

" show special characters
set list
set listchars=tab:»_,extends:>,precedes:<
highlight Whitespace ctermfg=darkgrey guifg=#777777
set backspace=indent,eol,start

" themes
if has("gui_vimr")
    set background=dark
    colorscheme night-owl
else
    colorscheme dim
endif

let g:lightline = {
\   'colorscheme': 'jellybeans',
\   'active': {
\       'right': [
\           ['neomake'],
\           ['lineinfo'],
\           ['percent'],
\           ['fileformat', 'fileencoding', 'filetype'],
\       ],
\   },
\   'component_expand': {
\       'neomake': 'lightline_neomake#component',
\   },
\   'component_type': {
\       'neomake': 'error',
\   },
\}

" handy function for copying a single file remotely without involving netrw
function! ScpTo(hostname)
    if (a:hostname =~ ":")
        let target=a:hostname
    else
        let target=(a:hostname . ":")
    endif
    exe ("!scp % " . target)
endfunction

command! -nargs=1 Scpto call ScpTo("<args>")
noremap <leader>S :Scpto 

nnoremap <leader>e :FuzzyOpen<CR>
nnoremap <leader>g :FuzzyGrep<CR>

" Copy to clipboard
vnoremap  <leader>y  "+y
nnoremap  <leader>Y  "+yg_
nnoremap  <leader>y  "+y
nnoremap  <leader>yy  "+yy

" Paste from clipboard
nnoremap <leader>p "+p
nnoremap <leader>P "+P
vnoremap <leader>p "+p
vnoremap <leader>P "+P

" plugin configs
let g:puppet_align_hashes = 0
let g:grepper = {'tools': ['rg', 'git', 'grep'] }
set noshowmode
doautoall editorconfig
let g:netrw_liststyle = 3
autocmd FileType netrw setl bufhidden=wipe

After that , the rest of my init.vim is just a huge amount of config for specific filetypes and specific paths, things like

autocmd BufRead,BufNewFile LICENSE setlocal spell
autocmd FileType mkd,markdown setlocal spell nolist columns=120 tw=120 wrap linebreak showbreak=+++ colorcolumn=0
autocmd FileType ruby setlocal shiftwidth=2 sts=2 ts=2 expandtab

I'm trying to replace more of these with checked-in .editorconfig files, but I'm not making much headway.

Future Notes

I really should use fewer plugins. Or at least pin the SHAs in the Plugged config.

There's an interesting project called DinVim, which is a Neovim frontend that runs inside a Mac App Store sandbox. If it had the right entitlements to let me execute blessed binaries and open files from my homedir (probably would have to be through a file picker to work with the sandbox), then it could be a good solution for using untrusted plugins. Unfortunately, it's can't execute any shell commands right now, so it's pretty useless.

Other than that, I guess I'm actually kind of happy with my setup for now? Don't worry, I'm sure I'll think of something else to change soon!

🎵 Currently listening to: Phonovisions Symphonic Orchestra by Wax Tailor 🎵


  1. I think the last post might actually have been more than ten years ago

  2. I actually like the git command-line interface. I've been playing with some alternative version control systems lately (specifically, Darcs and Pijul), and they've really made me appreciate git's functionality and interface. 


Comments