Why I love vim, and my vimrc.
· Astrid Olson
I've been an avid Vim user since 2012. I like modal editing, I like a keyboard-driven workflow, and I have spent more time adjusting my editor than any reasonable accounting of the time saved would justify. Some of that is useful. Some of it is just enjoying a tool.
What keeps me interested is how much of the experience comes from a small set of ideas that fit together. My configuration has accumulated plenty of history, but the part I would explain to someone else starts with the editor itself.
Editing has a grammar
Vim lets me combine an operation with a motion or a text object. Once the pieces become familiar, a new combination often makes sense without needing a separate shortcut to memorize. A few examples I find useful:
ciwchanges the word under the cursor.di"deletes the contents of a pair of double quotes.dapdeletes a paragraph, including the surrounding whitespace selected by the paragraph object.yipcopies the inner paragraph into a register.
I can think about the piece of text I want to change and then describe the operation. That is the appeal for me: the commands have a structure I can reuse. Vim's text-object documentation is a useful reference when a combination is unfamiliar.
A good change is easy to repeat
The dot command, ., repeats the last change. That rewards making an edit as a coherent operation. If I change a quoted value with ci", type its replacement, and leave insert mode, I have a change I can repeat elsewhere. Search helps me reach the next place; repetition applies the edit again.
Macros extend the same idea to a sequence of actions: record into a register, then replay it where needed. I like that both features let a small, local editing decision turn into something reusable. The reference on repeating changes covers the details, including what dot actually remembers.
My vimrc has a lot of archaeology
My existing config tells a fairly clear story about the work I have done. There are Python and PHP indentation settings, Rails-related filetypes, Go tooling, NERDTree, FuzzyFinderTextMate, Syntastic, and Inkpot. There are also repeated settings, overlapping autocommands, and remnants of different plugin setups. A file can be useful and still have years of accumulated decisions inside it.
Below is my full vimrc with redundant definitions removed. It retains my clipboard mappings, language settings, GUI preferences, navigation shortcuts, whitespace highlighting, backup paths, and plugin configuration. Pathogen loads my plugins; NERDTree, FuzzyFinderTextMate, Syntastic, and YouCompleteMe each have their own settings.
I removed repeated syntax and filetype setup, the duplicate NERDTree startup command, earlier clipboard and Syntastic definitions that were overwritten later, and an empty vim-plug block. The final Syntastic settings are preserved, as are the separate shortcuts I use for the same split command.
You can download my vimrc or copy it below. It uses Vim's default backslash leader, the Inkpot colorscheme, and plugins installed separately in my Vim setup.
" My vimrc, with redundant and overridden definitions removed.
runtime! debian.vim
execute pathogen#infect()
if has("syntax")
syntax on
endif
vmap <C-v> c<ESC>"+p
imap <C-v> <C-r><C-o>+
" CTRL-X and SHIFT-Del are Cut
vnoremap <C-X> "+x
vnoremap <S-Del> "+x
" CTRL-C and CTRL-Insert are Copy
vnoremap <C-C> "+y
vnoremap <C-Insert> "+y
" CTRL-V and SHIFT-Insert are Paste
map <S-Insert> "+gP
cmap <C-V> <C-R>+
cmap <S-Insert> <C-R>+
if filereadable("/etc/vim/vimrc.local")
source /etc/vim/vimrc.local
endif
set guioptions-=m "remove menu bar
set guioptions-=T "remove toolbar
set nocompatible
set guifont=Operator\ Mono\ 12
set shiftwidth=2
set expandtab
set tabstop=2
set autoindent
autocmd BufRead,BufNewFile *.py setlocal ai ts=4 sts=4 et sw=4
autocmd BufRead,BufNewFile *.php setlocal ai ts=4 sts=4 et sw=4
filetype plugin indent on
retab
set syntax=automatic
set hlsearch
set number
highlight ExtraWhitespace ctermbg=red guibg=darkred
match ExtraWhitespace /\s\+$/
autocmd ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
autocmd BufWinLeave * call clearmatches()
augroup vimrc_autocmds
autocmd BufEnter * highlight OverLength ctermbg=red guibg=darkred
autocmd BufEnter * match OverLength /\%75v.*/
augroup END
colorscheme Inkpot
set columns=180
if has("gui_running")
set lines=999 columns=999
set guifont=Operator\ Mono:h16
autocmd VimEnter * NERDTree
autocmd VimEnter * wincmd p
endif
map <Leader>t :FuzzyFinderTextMate<Enter>
set backupdir=~/.vim/temp,.
set directory=~/.vim/temp,.
set backupcopy=yes
let NERDTreeIgnore=['.vim$', '\~$', '.*\.pyc$', 'pip-log\.txt$', 'whoosh_index', 'xapian_index', '.*.pid', 'monitor.py', '.*-fixtures-.*.json', '.*\.o$', 'db.db']
let NERDTreeMinimalUI = 1
let NERDTreeDirArrows = 1
let NERDTreeShowHidden = 1
map <leader><tab> :tabnext<cr>
map <leader><s-tab> :tabprev<cr>
map <leader>h :vsplit<cr>
map <leader>v :sp<cr>
map <leader>a :sp<cr>
map <leader>r :NERDTreeFind<cr>
au BufRead,BufNewFile *.go set filetype=go
au BufRead,BufNewFile *.twig set syntax=htmljinja
au BufRead,BufNewFile *.json.jbuilder set ft=ruby
au BufRead,BufNewFile *.es6 set filetype=javascript
au BufRead,BufNewFile *.rabl setf ruby
let g:ycm_allow_changing_updatetime = 0
let g:syntastic_eruby_ruby_quiet_messages = {'regex': 'possibly useless use of .* in void context'}
" syntastic: https://github.com/scrooloose/syntastic
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_open = 1
let g:syntastic_check_on_wq = 0
let g:syntastic_enable_sass_checker = 1
let g:syntastic_enable_scss_checker = 0
let g:syntastic_enable_css_checker = 0
let g:syntastic_enable_pep8_checker = 1
let g:syntastic_javascript_checkers = ['jsxhint']
let g:syntastic_python_checkers=["flake8"]
let g:syntastic_python_flake8_args='--ignore=F821,E302,E501,E731'
let g:syntastic_go_checkers = ['golint', 'govet']
let g:syntastic_mode_map = { 'mode': 'active', 'passive_filetypes': ['go'] }
let g:go_list_type = "quickfix"
autocmd BufNewFile,BufRead *.apxc,*.apxt,*.cls,*.trigger,*.tgr set filetype=apex
autocmd BufNewFile,BufRead *.vfp,*.vfc,*.page,*.component set filetype=visualforce
autocmd SwapExists * let v:swapchoice = "e"
Small choices make the editor comfortable
My config keeps search matches highlighted, shows line numbers, and makes trailing whitespace and long lines visible. Python and PHP get four-space indentation; my default is two spaces. The GUI settings remove menus and toolbars, set Operator Mono as the font, and open NERDTree while returning focus to the editing window. Vim's options reference is useful for looking up the individual settings.
There are some very personal defaults in here too: where backup and swap files go, the decision to edit when a swap file already exists, and the file patterns I hide in NERDTree. These settings reflect the environment and habits the config grew up with. Keeping them visible makes the file more useful as a record of how I actually use Vim.
I still enjoy the tinkering. Inkpot, a familiar set of motions, and an editor I can shape are part of the pleasure of sitting down to write code. Cleaning up the repetition makes it easier to understand those choices when I come back to the file later.