Updated 2019-05-04

About a year ago I finally bit the bullet and commited to learning a real text editor. I went with Vim. Once you break through its initial steep learning curve, its truly life changing. Level up to text surgeon today.

I usually clone my dotfiles git repo straight into my home directory i.e. ~/dotfiles. A couple of symlinks later, I’m away:

ln -nfs ~/dotfiles/vim/vimrc ~/.vimrc
ln -nfs ~/dotfiles/vim ~/.vim

Vim is incredibly customisable. Its important to take the time to craft your own .vimrc. Type :options to understand the various levers you can pull to make Vim your editor.


Vim has brilliant built-in help. Its only one :help away, :h operator or :h motion.

  • :h cmd normal mode cmd help
  • :h i_cmd insert mode cmd help
  • :h v_cmd visual mode cmd help
  • :h c_cmd command line cmd help


Trigger Effect
c change
d delete
y yank
g~ swap case
gu lower case
gU upper case
g? ROT13 encode
> shift right
< shift left
= autoindent
! filter through a program

See :h operator for more. All of these support being combined with a motion (or a visual mode selection). Some examples:

  • gUaw - make a word shout case
  • dap - delete entire paragraph
  • g?ap - ROT13 encode paragraph
  • gUgU - shout case entire line (factoid: when two operators are invoked in duplicate, applies to current line)

Object Selection

Learning object selectors is one of the best ways of becoming more efficient with Vim. Commands that start with i select inner objects without white space, and thus always select less text than their a equivalents. When you discover these can be paired with operators (discussed above), life changing, e.g. daw delete a word, gUis uppercase inner sentence, and so on.

Selector Effect
aw a word
iw inner word
as a sentence
is inner sentence
ap a paragraph
ip inner paragraph
a] a[ a [] block
i] i[ inner [] block
a) a( a block
i) i( inner block
a> a< a <> block
i> i< inner <> block
at tag block, as in XML tags <a> to </a>
it inner tag block
a} a{ a {} block
i} i{ inner {} block
a" a' quoted string including back ticks
i" i' quoted string including back ticks


Leverage the built-in windows manager, which can do splits to view multiple files at the same time.

  • :sp horizontal split
  • :vs vertical split
  • ctrl+w o - close all windows other than the active one.
  • ctrl+w x - exchange active window with the next one.
  • ctrl+w c - close the current window.
  • ctrl+w r - rotate windows clockwise (or counter clockwise).

The Edit (e) Command

Vim’s built-in edit command, will present you with a nice file system explorer, for example :e . to present the current working directory.

Cool Things


Makes line numbering relative. So good! Makes it fast to figure out how many lines up or down you need to move, to get to the line you want. Example, 14j to jump 14 lines down.

  2 I usually clone my `scripts` git repo straight into my home
  1 ¬                                                          
13      ln -nfs ~/git/scripts/linux/vim/vimrc ~/.vimrc¬        
  1     ln -nfs ~/git/scripts/linux/vim ~/.vim¬                
  2 ¬                                                          
  3 Vim has brilliant built-in help. Its only one `:help` away.

Escalated Save

Editing a file, but don’t have privileges to save.

:w !sudo tee %

:w writes to sudo tee %. tee flows the output of the file write to %, the name of the current file. I have a handy key binding w!! to do this:

cmap w!! w !sudo tee %


While core Vim functionality is like a rock, changing rarely, the plugin eco-system is where you can make Vim level up to doing tasks you commonly do with it. Consequently plugin selection can be quite personal based on specific langs one works with.

The Vim community seems obsessed with writing new plug-in managers, there’s quite a bit of choice and tradeoffs, I have used a few in the past (Pathogen, Vundle) and have settled with vim-plug for the time being.

Coming from bloated power tool IDE’s like IntelliJ or Resharper, in order to be remotely productive, I need to be able to efficiency locate and jump between files within a large code base.


NERDTree add’s a file system tree pane to Vim (like Open Folder in VSCode). Personally I find fuzzy finding with denite and ripgrep to be vastly more efficient at jumping between files, but NERDTree is handy when it comes to browsing a code base.

NERDTree keyboard customisations:

" Toggle NERDTree on/off
nmap <leader>n :NERDTreeToggle<CR>

" Opens current file location in NERDTree
nmap <leader>f :NERDTreeFind<CR>

" PageDown
noremap <Space> <PageDown>

" PageUp
noremap - <PageUp>

NERDTree general customisations:

let g:NERDTreeShowHidden = 1 "show hidden files
let g:NERDTreeMinimalUI = 1 " remove bookmarks and help text
let g:NERDTreeDirArrowExpandable = '⬏' "custom icons for expandable
let g:NERDTreeDirArrowCollapsible = '⬎' "custom icons for expandable
let g:NERDTreeIgnore = ['^\.DS_Store$', '^tags$', '\.git$[[dir]]', '\.idea$[[dir]]', '\.sass-cache$'] "ignore list

coc (Conquer of Completion)

It’s a completion framework and language server client which supports extension features of VSCode.

Like VSCode, Emacs and many others, can sit upon the Langserver.org Protocol (LSP) originally created by Microsoft, to provide language aware intelligence such as auto complete, go to definition and find all references.

To install a coc extension that supports a language, have a look on npm for one, and install with CocInstall for example to install Go language completion CocInstall coc-gocode. Make sure to also install gocode for this particular plugin, with a quick go get -u github.com/nsf/gocode.

Go support

You can manually register LSP servers in jsonc format in the ~/.config/nvim/coc-settings.json (use :CocConfig to auto jump you to this file). For example, the below registers gopls, Google’s offical LSP for golang:

  "languageserver": {
    "golang": {
      "command": "gopls",
      "args": [],
      "rootPatterns": ["go.mod", ".vim/", ".git/", ".hg/"],
      "filetypes": ["go"]

Web dev (JS, ES6 and CSS)

:CocInstall coc-tsserver coc-eslint coc-json coc-prettier coc-css

Denite (and ripgrep)

Fuzzy finder. Very customisable, for example, mine is configured to use ripgrep. What is ripgrep?

ripgrep is a line-oriented search tool that recursively searches your current directory for a regex pattern. By default, ripgrep will respect your .gitignore and automatically skip hidden files/directories and binary files.

In my .vimrc:

" Use ripgrep in place of "grep"
call denite#custom#var('grep', 'command', ['rg'])

" Custom options for ripgrep
"   --vimgrep:  Show results with every match on it's own line
"   --hidden:   Search hidden directories and files
"   --heading:  Show the file name above clusters of matches from each file
"   --S:        Search case insensitively if the pattern is all lowercase
call denite#custom#var('grep', 'default_opts', ['--hidden', '--vimgrep', '--heading', '-S'])

" Recommended defaults for ripgrep via Denite docs
call denite#custom#var('grep', 'recursive_opts', [])
call denite#custom#var('grep', 'pattern_opt', ['--regexp'])
call denite#custom#var('grep', 'separator', ['--'])
call denite#custom#var('grep', 'final_opts', [])