On Vim

I’ve been using Vim in various settings (in my IDE, in the terminal, in Obsidian, to prepare LaTeX documents, …) for a few years now. This post exists mainly to share my favourite Vim commands/resources, however also give a bit of advice on the process of learning Vim.

How to learn Vim

There are some good resources about to help you learn Vim which I’ll link below. In terms of best practices to aid your learning, I have two main bits of advice.

Firstly, it can be more pleasant to start in a text editor which supports Vim but also allows mouse-based interaction and other non-Vim paradigms. For me this happened by accident since I started using Vim in PyCharm with the IdeaVim plugin, but this is also possible in, for eg, MacVim (only for Mac users), and Obsidian with the Vim plugin enabled. It might not be optimal for learning to be able to opt-out of using Vim syntax sometimes, however it will allow you to integrate Vim into your workflow gradually, thus lowering the barrier to entry, and decreasing the chance you give up altogether. The idea of course is that over time you use proportionately more and more Vim commands, until using the mouse becomes less efficient and you stop altogether.

Secondly, and in slight opposition to the above, you must be willing to suffer an initial drop in productivity and rise in frustration. When I first switched on IdeaVim in PyCharm I spent a while getting very confused and annoyed when either Vim didn’t behave as expected, or when my now-outdated muscle memory from pre-Vim days misled me. The only real way through this is practice and perseverance: take the time to Google Vim-based ways of doing whatever it is you were trying to do, and if a command isn’t functioning as you expect, take the time to find out why. One case of this for me (among many) was not realising that having caps lock enabled totally changes the meaning of many Vim commands – eg D is the same as dd.

Vim commands I like

These commands straddle difficulties, though there’s nothing particularly advanced here. This section presumes basic knowledge of Vim and its syntax. Note <char> here represents a character on the keyboard, eg a, [, ., … Note also that anywhere I use +, it is to indicate concatenation of commands, rather than the literal plus symbol.

Editing

  • di<char>, where <char> is a ‘paired’ character (eg {, [, ' etc) will delete inside the paired character (so {name: int} would become {}). This has as special cases dib and diB, which are equivalent to di( and di{ respectively. As with all such commands, ci<char> will do the same but enter insert mode, yi<char> will yank, etc
  • ~ can be used to toggle case either for the character under the cursor if in normal mode, or for selected characters if in visual mode
  • gq<motion> reformats anything with in the scope of <motion> (eg gqG reformats from the current line to the bottom), with the special case gqq which reformats the line. I find this super useful for writing these articles, as I do so in Obsidian, which isn’t good at line breaks – I can just gq the doc once it’s in Vim proper and it gets fixed. (Note sometimes it can be a bit over-zealous in what it fixes, so I wouldn’t use indiscriminately.)
  • (Requires Surround.vim – see below for some stuff about plugins.) ysw<char> will surround to the end of the current word with <char> – if in the middle of a word you will probably want to use b to go to the start of the word first. This extends as you’d expect, eg ys$<char> is the same but for the whole line. Note <char> must be a ‘paired’ character as above
  • Following on from the above, ds<char> will deleted paired <char>s
  • ctrl + v will enter ‘multi-cursor’ mode. A common use-case for me is ctrl + v, then j to select a bunch of lines, I to enter insert mode (this will only show on the first selected line), then esc when done to apply the changes to all selected lines
  • You can record macros by pressing q<char> to start recording, then typing the command for the macro, then pressing q when done. To use the macro type @<char> (as with all Vim, this accepts a number of iterations, eg 10@a repeats the command stored in the a register 10 times)
  • . repeats the last command
  • >> indents a line – powerfully combines with visual mode
  • g?? scrambles a line, respecting word length (not sure of applications, but it’s fun)

Motion

  • f<char> moves the cursor the next occurrence of <char> on the line. F moves the cursor to the most recent previous such occurrence on the line. This can be combined very powerfully with d – eg df: deletes inclusively to the next colon
  • t<char> moves the cursor to the character before the next occurrence of <char>, as above for T. Again can be combined very powerfully with d
  • /string will search the file for the next occurrence of string. You can toggle through them with n
  • % moves the cursor to the paired character matching the one under the current cursor (if one exists)
  • You can set a marker at register <char> by typing m<char>: when you type `<char> it will move to where you placed the marker. Relatedly, '<char> takes you to the start of the marker’s line

My .vimrc

Some of this might be useful/interesting. It’s fairly basic as .vimrcs go, as I haven’t really defined any custom commands, but for now I find it does the job. Feel free to copy any bits you find useful (that’s probably how I ended up with them in the first place).

One or two bits merit explanations/warnings:

  • I use the vim-plug plugin manager (guide here) – you will need to install it before you’ll be able to use the below .vimrc
  • vimtex is a wonderful plugin for Vim/LaTeX – for a great guide on this topic and more see here
  • The typeface bit possibly (I did on OS X, I didn’t on Ubuntu) requires you to install ‘DejaVu Sans Mono for Powerline’ (this is needed for the theme) – you can see a guide on this here
  • If you have spellcheck enabled, you can add a word to the dictionary with zg (neat right?)
  • The tabbing stuff enables auto-mapping to tabs to spaces, as well as a few other niceties such as auto-line breaking at 80 chars. The stuff below makes Vim show me trailing whitespace, tabs, and other undesirable characters
  • I make no apology for leaving the mouse sometimes enabled – sometimes I come to Vim briefly from a non-Vim context, and it can just make things easier (eg for copying and pasting text outside of Vim – you can configure some settings to do this, but how to do so differs by OS and Vim version)
scriptencoding utf-8

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

Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
Plug 'scrooloose/nerdtree'
Plug 'lervag/vimtex'
Plug 'tpope/vim-surround'

call plug#end()

" font
set guifont=DejaVu\ Sans\ Mono\ for\ Powerline

" theme
let g:airline_theme='deus'
let g:airline_powerline_fonts = 1

" utf-8 encoding
set encoding=utf-8

" spellcheck
set spell spelllang=en_us

" basic tabbing stuff
set expandtab
set tabstop=4
set softtabstop=4
set shiftwidth=4
set autoindent
set textwidth=80

" whitespace
set listchars=tab:>-,trail:·,extends:>,precedes:<
set list

" function to strip whitespace on save
function! <SID>StripTrailingWhitespaces()
    let l = line(".")
    let c = col(".")
    %s/\s\+$//e
    call cursor(l, c)
endfun

autocmd BufWritePre * :call <SID>StripTrailingWhitespaces()

" show line numbers
set number

" no noises when wrong
set visualbell

" always allow mouse
if has('mouse')
    set mouse=a
endif

Other Vim resources

There are loads of other Vim resources out there; I’ll list a few here.