Barbarian Meets Coding
barbarianmeetscoding

WebDev, UX & a Pinch of Fantasy

29 minutes readvim

Vim - The Mighty Text Editor that lets you edit text at the speed of thought

Getting Started With Vim

Basic Vim Motions

  • hjkl - left, up, down, right

  • 0 - beginning of line

  • ^ - beginning of text (within a line)

  • $ - end of line

  • gg - beginning of file

  • H - top of the visible text within the window

  • M - middle of the visible text within the window

  • L - bottom of the visible text within the window

  • G - end of file

  • w - move to the beginning of the next word

  • e - move to the end of the next word

  • t<char> - move before the next appearance of character <char> in this line (from left to right)

  • T<char> - like above but the previous appearance (from right to left)

  • f<char> - move to the next appearance of character <char> in this line (from left to right)

  • F<char> - like above but the previous appearance (from right to left)

Basic Vim Commands

  • i - go into insert mode before the current character

  • I - go into insert mode at the beginning of the line (shortcut for ^i)

  • a - go into insert mode after current character

  • A - go into insert mode at the end of the line (shorcut for $a)

  • c - change (delete and insert)

  • cw - change (delete and insert) word

  • cc - change line

  • C - change everything till the end of the line (shorcut for c$)

  • s - change a letter (shorcut for cl)

  • S - change complete line (_shortcut for ^c)

  • x - delete a character

  • dw - delete word

  • dd - delete line

  • gu - change to lowercase

  • gU - change to uppercase

  • guw - change word to lowercase

  • guu - change line to lowercase

  • . - repeat last change

  • > and < - indent/unindent

  • >> - Increase indentation in current line

  • << - Decrease indentation in current line

  • >G - Increase indendation till the end of the file

  • = - fix indentation

  • o - new line and insert mode (shortcut for A<ENTER> or A<CR>)

  • O - new line above and insert mode (shortcut for ko)

Undo and Redo

Normal Mode

  • u - undo last change
  • U - Undo all latest changes on one line, the line where the latest change was made. U itself also counts as a change, and thus U undoes a previous U.
  • C-R - redo
  • Check :help undo for more info

Insert Mode

These are equivalents to undo (although not exactly) when in insert mode:

  • CTRL-H delete character before the cursor
  • CTRL-W delete word before the cursor
  • CTRL-U delete all characters before the cursor in the same line

Combining commands (operators) and motions to get things done

These are Vim’s more common commands (:h operator). They are used to perform operations on text:

c - change
d - delete
y - yank into register (does not change the text)
g - swap case
gu - make lowercase
gU - make uppercase
! - filter through an external program
gq - text formatting
> - shift right
< - shift left

You can combine these operators with motions and counts (:h cursor-motions) to apply them to specific bits of text.

// motions
w - move to beginning of next word
b - move backwards to beginning of previous word
e - move to end of next word
ge - move backwards to the end of previous word
0 - move to beginning of the line
$ - move to the end of the line
etc...

// text objects
aw - around a word
iw - inside a word

etc...

A complete command will follow a pattern like this:

{operator}{count}{motion}

or

{count}{operator}{motion}

For instance:

  • dw - delete a word
  • cw - change a word
  • d2w - delete 2 words
  • cta - change until the next occurrence of the letter a (non inclusive)
  • f - find next occurrence of character in current line (mnemonic find)
  • F - find previous occurrence of character in current line
  • t - find next occurrence of character in current line and place cursor before it (mnemonic till or until)
  • T - find previous occurrence of character in current line and place cursor after it
  • ; - repeat last f,t,F,T commands
  • , - repeat last f,t,F,T commands in the opposite direction
  • /{pattern}<CR> - search forward for occurence of pattern
  • ?{pattern}<CR> - search backward for occurence of pattern
  • n and N work with / and ? as ; and , above
  • * - go to next occurrence of pattern under cursor (repeat search)
  • # - go to previous occurrence of pattern (repeat search)

A common worflow to search stuff in Vim is to use the /{pattern} motion and press enter immediately after to exist command-line mode and jump back into normal mode in the first match of the pattern. A useful alternative to keep refining a search is to use:

  1. /{pattern} to search for a pattern
  2. Use CTRL-G and CTRL-T to jump to the next or previous match respectively without leaving the command-line mode

Notice how near the repeating commands are (;, . and ,), and how # and * are symmetric and correspond to the middle finger in each hand.

Substitute (replace text with other text)

:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]

Subsitute text with other text:

  • range over which to apply the substitution
  • pattern to match and substitute
  • string to use as replacement
  • flags like
    • g global (substitute all matches within a line)
    • c to confirm each substitution
    • n to ignore substitution and report number of matches
    • :h :s_flags for more
  • replace count lines

Some examples:

  • :%s/foo/bar/g replaces foo for bar in the whole document
    • % matches every line in the document
    • g applies the substitution to every match within a line
  • :%s/foo//gn counts matches of foo in the whole document
  • :%s/https.*/[&]()/g replaces urls for markdown links. You can use the & to inject the matched pattern in the substitution

Repeaters:

  • &: repeats last substitution from normal mode (resets flags)
  • :s or :&: repeats last substitution (resets flags)
  • :&&: repeats last substitution with same flags
  • :%&: like :& but for the whole file
  • :%&&: like :&& but for the whole file

Global

  • :g/{pattern}/{command} - execute ex command command on lines that match pattern
  • :g/foo/d - delete all lines that have foo
  • :g!/foo/d - delete all lines that don’t have foo
  • :v/foo/d - shorthand for :g!/foo/d

File Navigation

  • :e# - go to previous buffer
  • :e#n - go to buffer number n (where n=1,2,3...)
  • gf - go to file (when the cursor is on top of a file)
  • CTRL+O - go back
  • :buffer - list buffers

Splits

  • :split - create a horizontal split of the current buffer
  • :split {file} - open file in horizontal split
  • :split +{line} - open horizontal split in line {line}
  • :split + - open horizontal split at the end of the line
  • :split +/{pattern} - open horizontal split at an specific pattern
  • :vsplit - like :split but opens vertical splits
  • CTRL-W o - close all other splits (also :only). There’s a plugin called Zoomwintab.vim that allows you to zoom into a split using the same binding instead of closing all other tabs. This allows you to come back to the original disposition of splits by just using the binding again. It behaves just like tmux zooming but within Vim.

Moving Between Splits

  • CTRL-W h - Move to window on the left
  • CTRL-W j - Move to window below
  • CTRL-W k - Move to window above
  • CTRL-W l - Move to window on the right

Moving splits

  • CTRL-W H - Move window to the left
  • CTRL-W J - Move window below
  • CTRL-W K - Move window above
  • CTRL-W L - Move window to the right

Resizing Splits

  • :resize -{number} - decrease window height by {number}
  • :resize {number} - increase window height by {number}
  • :vertical resize -{number} - decrease window width by {number}
  • :vertical resize {number} - increase window width by {number}
  • [count] CTRL-W - - decrease window height by one line (or [count] lines)
  • [count] CTRL-W + - increase window height by one line (or [count] lines)
  • [count] CTRL-W < - decrease window width by one line (or [count] lines)
  • [count] CTRL-W > - increase window width by one line (or [count] lines)
  • [count] CTRL-W = - make all windows of equal height and width (or [count] lines)

Visual mode

Visual mode allows you to select bits of text and perform operations on them.

  • v - go into character-wise visual mode
  • V - go into line-wise visual mode
  • C-v - go into block-wise visual mode

When in visual mode you’ll move one of the ends of the selection while the other one remains fixed. In order to be able to change either end you can type o to change the current end you’re moving.

Vim And The Command Line Mode

To run a command-line command from Vim use:

$ => execute this in external shell
: => execute this internally in command-line mode
/ => use command-line mode to perform a forward search
? => use command-line mode to perform a backward search
= => use command-line mode to evaluate a Vim script expression

The Vim Way

Vim is optimized for repetition. Take this into account when editing and you’ll be that much efficient:

  1. Use the . command to repeat the last change
  2. Use the ;, ,, *, # commands to repeat motions
  3. Use the @: to repeat Ex commands
  4. Use the & to repeat substitution (:h :substitute)
  5. Remove extraneous, unnecessary movement (f.i. use I instead of 0i or A instead of $a)
  6. Use compound commands that leave you directly in insert mode instead of separate commands (f.i. use cw instead of dwi or C instead of d$i)
  7. Attempt to make changes repeatable

The dot command (.) lets us repeat the last change. It is the most powerful and versatile command in Vim. From Practical Vim

How to Configure and Customize Vim

You normally configure Vim by adding configuration commands inside the .vimrc file which typically lives in ~/.vimrc. This file is loaded when you run Vim and is where you want to place information such as the plugins you want to load, color schemes you want to you and custom key bindings that represent the personal way you use Vim.

Customize Vim Key Mappings

Vim lets you define your own key mappings and change existing ones to your heart’s content. This is a powerful way to optimize the way that you write code and be more productive when using Vim.

Vim offers two commands that let you create and modify mappings in different ways: map and noremap. You can use map to map a key sequence to any other key sequence or command:

map tab

The difference between map and noremap is that, the first one allows for recursive mappings whilst the second one (noremap - no recursive mappings) doesn’t. A recursive mapping means that Vim will continue scanning the result of a mapping and seeing whether there are other mappings that correspond with this result. A non recursive mapping means that Vim won’t scan the result of these mappings and will use Vim’s default meaning of the result. For instance:

" map E to B, B is recursively mapped to G, go to the end of a file
map E B
noremap B G
" G maps to gg, go to the top of a file
" This mapping doesn't affect the mappings above
" because the second mapping is a no recursive mapping, that is,
" it will not be scanned for more mappings recursively
noremap G gg

This is a list of all mapping commands:

  • map {lhs} {rhs}: Maps the key sequence {lhs} to {rhs} for the modes where the map command applies. The result, including {rhs}, is then further scanned for mappings. This allows for nested and recursive use of mappings.
  • noremap {lhs} {rhs}: As above but it disallows the mapping of {rhs}, to avoid nested and recursive mappings. Often used to redefine a command.
    • nnoremap {lhs} {rhs} - same but only normal mode and so on for inoremap, vnoremap, etc
  • unmap {lhs}: Remove mapping of {lhs} for the modes where the command applies
  • mapclear: Remove ALL mappings for the mode where the map command applies
  • map: List all key mappings for the modes where the map applies
  • map {lhs}: List the key mappings for the key sequence starting with {lhs} in the modes that apply

Note that you can prepend modes to these commands above to make the command apply to a specific mode. For instance, nmap applies to normal mode, imap applies to insert mode and so on.

You can use special characters in mappings with added functionality:

  • <Plug> - you may have seen some mapping including <Plug>. <Plug> allows plugin authors to make it easier for users of their plugin to map funcionality to new key-bindings. It allows you to temporarily bind <Plug>SomeAction to an action and then let the user map whichever sequence to that binding nnoremap wop <Plug>SomeAction

Check out :help key-mapping for more info.

The Minimal Basic Configuration To Make Vim Play Nicely

I recommend you to take a look at Vim-sensible if you’re using Vim. If you want a better first time experience, then try Vim. Most of the commands from Vim-sensible and those you can find below are set by default if you use NeoVim. You can find more info about NeoVim’s sane defaults at NeoVim.org.

" Color scheme (using a nice built-in dark color scheme)
colorscheme industry

" Filetypes
" This enables file type detection (like filetype on)
" and also loading plugins and indentation per filetype
filetype plugin indent on  
syntax enable    " enable syntax highlighting

set autoindent   " Copy indent from current line when starting a new line
set clipboard+=unnamedplus " use system clipboard
set showcmd      " display incomplete commands
set showmode     " display the mode you're in
set backspace=indent,eol,start "intuitive backspacing"
set hidden       " Handle multiple buffers better
set wildmenu     " enhanced command line completion
set wildmode=list:longest " complete files like a shell

""" Search
set ignorecase   " case-insensitive search
set smartcase    " but case-sensitive if expression contains a capital letter
set relativenumber "show relative line number
set ruler        " show cursor position
set incsearch    " highlight matches as you type
set hlsearch     " highlight matches

set wrap         " turn on line wrapping
set scrolloff=3  " show 3 lines of context around cursor
set display+=lastline "Display as much as possible of a window's last line
set title        " show terminal title
set visualbell   " no beeping
set list         " show invisible characters

"" Global tabs/spaces
set smarttab     " use spaces instead of tabs
set expandtab    " use spaces instead of tabs
set tabstop=2    " global tab width
set shiftwidth=2
set laststatus=2 " Always show a status line

set nobackup " no backups
set nowritebackup " No backups
set noswapfile " No swap files
set autoread " Automatically re-read files changed outside of Vim
set nofoldenable " Disable folding

Here you can find more inspiration for your vimrc:

  • Vim-sensible
  • Another example of a minimal vimrc
  • Vim plugin authors and other Vim users usually have their vimrc (and other dotfiles) open to the public. That’s a great place to draw inspiration from. For example here’s Tim Pope’s, Shougo’s and noopkat. Just go into github and search for dotfiles, then find the .vimrc or init.vim for neoVim.
  • Vim-bootstrap.com can generate a Vim configuration based on the type of development that you use. This can be helpful for finding useful plugins or mappings.
  • vimawesome.com is a huge repository of Vim plugins. Just search by a keyword of interest and you’ll be able to find loads of plugins that can solve that problem you’re interested in.
  • My Vim Plugins notes where I collect, curate and write notes about useful Vim plugins.

Tabs and Spaces

"" Global tabs/spaces
set smarttab     " use spaces instead of tabs
set tabstop=2    " global tab width
set shiftwidth=2
set expandtab    " use spaces instead of tabs
set laststatus=2 " Always show a status line

To convert all current existing tabs to spaces use the :retab command. Use :h :retab for more inof.

Configure Invisible Characters

Vim lets you configure how certain characters are displayed in your buffer window. This is specially useful for whitespace that is often not visible. To see whether you are mixing tabs and spaces or trailing whitespace you can use listcharts:

" shows invisible characters
set list 
" Configures how to visualize invisible characters
set listchars=tab:>-,trail:-,nbsp:+

Having a more comfortable leader key

The <Leader> key in Vim allows for additional custom mappings in a convenient way by providing another modifier key (like CTRL, SHIFT, etc) that you can use in combination with other keys.

By default the <Leader> is assigned to \\ which can be slightly inconvenient since it is quite far for your right pinkie finger. If that is the case, consider remapping it to another key. By far the most common custom mapping for the <Leader> key is , but I find more <space> to be more convenient (it’s always under my right thumb) and less intrussive (as it doesn’t shadow any common Vim binding that I know of). To remap <Leader> to <space> add this to your .vimrc:

let mapleader="\<Space>"

Now whenever you define new bindings like this one:

nnoremap <leader><space> :noh<cr>

You can use <space> as your leader. In this case typing <space> twice will clear all highlighted text which you’ll typically want to do after a search.

Custom Navigation

You can add these mappings to be able to move faster than with the normal jk commands:

nmap J 5j
nmap K 5k

This lets you move up and down in a fine grained fashion with kj and also move faster by using KJ just by keeping SHIFT pressed.

There’s a caveat which is that this removes the default :join mapping to join lines which is pretty handy (particularly when writing markdown). You can remap this using your <Leader> key which will make joining lines more convenient:

nnoremap <Leader>j J

By default j and k will move up a line. However, if there is text that it is wrapped (that is, when the text is too long for the width of the window), then j and k will bring you up and down the complete line without taking into account wrapped lines. This is very unintuitive behavior and veeeery irritating. By default, Vim offers additional mappings gj and gk to go down and up a “visual line”, that is, taking into account wrapped lines. You can add these mappings to your Vim configuration to remap j to gj, etc and have a more intuitive default behavior:

nnoremap j gj
nnoremap k gk
nnoremap 0 g0
nnoremap ^ g^
nnoremap $ g$

Moving between windows (or buffers):

nmap gh <C-w>h
nmap gj <C-w>j
nmap gk <C-w>k
nmap gl <C-w>l

Moving between tabs:

nmap <C-l> gt
nmap <C-h> gT

Mappings To Experiment With

" using ; as : from http://stevelosh.com/blog/2010/09/coming-home-to-Vim/
" that way you avoid the need to type two letters everytime you want to
" access command mode.
" It's slightly irritating that you'll lose forward searches though
nnoremap ; :

" You can escape with <ESC> C-C or C-[ but the one below is much quicker
" Quicker escaping from http://stevelosh.com/blog/2010/09/coming-home-to-Vim/
inoremap jj <ESC>
" This doesn't work in visual mode. But in that case C-C is pretty ok

Creating custom commands

You can create your own commands in Vim by using the :command command. For example:

" This creates a new command that can be used from 
" Command-line mode by using :DeleteFirst to delete
" the first line in a buffer
:command DeleteFirst 1delete

If you want to pass arguments to your custom commands you need to use -nargs=N to describe how many arguments your command supports:

  • -nargs=0 No arguments
  • -nargs=1 One argument
  • -nargs=* Any number of arguments
  • -nargs=? Zero or one argument
  • -nargs=+ One or more arguments

You can access those arguments from your command definition using the <args> keyword:

:command -nargs=+ Say :echo "<args>"

<args> comes with two siblings that can be useful in some special situations:

  • <q-args> escapes all special characters in the arguments so they are represented as an string
  • <f-args> spreads the arguments used when calling the command so they can be used to call a function. myFunction(<f-args>) would be equivalent to myFunction(arg0, arg1, arg2, etc).

If you need to feed the result of a function into your command then you need to use execute. execute (See :h :execute) allows you to compose an ex-command from evaluating an expression (in this case evaluating a function). For example:

command! -nargs=* BlogNewNotes execute "!mkdir src/pages/notes/".ReplaceSpacesWithDashes(<q-args>) | execute "e src/pages/notes/".ReplaceSpacesWithDashes(<q-args>)."/index.md"

// Function that given some text transforms it to lower case and replaces
// spaces with dashes:
//   input: "Botany the study of plants"
//   output: "botany-the-study-of-plants"
function ReplaceSpacesWithDashes(text)
  return tolower(substitute(a:text, ' ', '-', 'g'))
endfunction

You can also take advantage of | to execute multiple ex-commands when calling your custom command:

command! -nargs=* BlogNewNotes execute "!mkdir src/pages/notes/".ReplaceSpacesWithDashes(<q-args>) | execute "e src/pages/notes/".ReplaceSpacesWithDashes(<q-args>)."/index.md"

To list all user defined commands use the :command command without any arguments. For more information about :command take a look at the help :h :command and the user manual :h 40.2.

Language Support

Enable filetype support if you haven’t done it already:

filetype on         " enable file type detection
filetype plugin on  " enable loading plugins for ft
filetype indent on  " enable indent per ft

Markdown

  1. Install the vim-markdown plugin.
  2. Setup vim-markdown following your needs. It comes with sensible defaults but you need to enable stuff like front-matter yaml (f.i. let g:vim_markdown_frontmatter = 1)
  3. If you don’t see syntax highlighting inside fenced code blocks you may need to install a specific plugin that provides syntax highlighting for that specific language. For instance, elm-vim

Miscellaneous tips

Run Vim With Factory Settings

To run with with factory settings use the -u option to point Vim to the following configuration file (vim -u factory.vimrc):

# factory.vimrc
set notcompatible
filetype plugin on

or alternatively run Vim -u NONE -N (the -N sets notcompatible) and then run :filetype plugin on manually once Vim is opened.

Count words

  • To find the number of words in the whole document type g and then C-g
  • It also works with a selection (if you select some text first)
  • You can use externals programs as well :w !wc
  • For more info :h word-count

Spellchecking

  • Select language :set spelllang=en
  • Enable current session :set spell
  • Enable current buffer :setlocal spell
  • ]s to move to the next misspelled word
  • [s to move backwards
  • zg to add word under cursor as a good word
  • zb to add word under cursor as a bad word
  • z= to get suggestions for bad words (awesome!!)
  • You can use counts to 2zg set two words as good words, or 1z= select the first suggestion for a bad word
  • You can run autocorrection within insert mode by running C-x and then s (also C-xC-s). The cool thing about this one is that you don’t need to have the cursor on top of the word for it to work. You can run it at the end of a line while you’re typing and suddenly realize you made a mistake
  • You can set multiple spellfiles to save your own custom words based on the context or on the project you’re working on :set spellfile+={path}/{filename}.{encoding}.add

Recovering Fleeting Error Messages When Starting Vim

If you ever run into fleeting error messages on startup try any of these:

  • Using the :messages command which shows all warnings, errors, and informational messages that have ever appeared in the Vim statusline. (:h messages)
  • The :echo errmsg command prints the most recent error message.
  • Use g< to see the last page of previous command output. Find out more here :h g<.

Changing the current working directory

  • :pwd shows current printing directory (it literally means print working directory)
  • :cd %:p:h to change the current directory to the one of the opened file (% expands to the current file, %:p is its full path and %:p:h refers to its directory)
  • :lcd %:p:h does the same as above but only to the current window
  • More info in Vimtips

Running Multiple Commands at once

  • You can run multiple Ex commands at once by separating them with the | operator
  • You can follow the same approach inside your vimrc, but you need to use the <bar> code to refer to | instead of | itself.
  • More Info.

Showing messages in Vim

  • You can use the :echo command to show messages in the status bar
  • Alternatively, you can use :echom which shows a message just like :echo and it also saves it in the history so that it can be later accessed via :messages.

Changing Multiple Files At Once with Args List

You can change multiple files at once taking advanced of the argument list or :args. This works great in combination with macros:

  1. Record a macro as usual (let’s say we’ve saved it in register q)
  2. Add files you want to change to the argument list using the :args command. It accepts blobs. For instance :args ./**/*.markdown adds all the markdown files to the argument list.
  3. Run macro on all these files with :argdo like so: :argdo normal @q which means: Grab all the files in the argument list and run the :normal @q command which in essence consists in running @q in normal mode.

You can also edit files in the argument list one by one like so:

  1. Add files you want to change to the argument list using :args. For instance, :args ./**/*.markdown.
  2. The first file that matches the blob is opened automatically when running the command above.
  3. Make necessary changes and type :wn (write next) (:h :wn) to save and jump to the next file in the argument list.

Some helpful argument list commands for reference:

  • :args - check what’s in the argument list
  • :args {file} - adds file to argument list
  • :args {blob} - adds files matching blob to argument list
  • :sall - open files in argument list in horizontal splits
  • :all - open files in argument list in vertical splits

For more info check out :h :args.

Searchng for Patterns using Vimgrep

Vim comes with grep-like functionality in the form of the :Vimgrep command. You can use it like so:

:Vimgrep /{pattern}/ files

Where {pattern} is a regular expression and files is a glob of files. For instance:

:Vimgrep /draft: false/ ./**/*.markdown

Searches all mardown files for the string draft: false.

Vimgrep populates the quickfix list with all matches (run :copen to see all matches within a window) and sends you to the first match if at least one is found.

For more info about Vimgrep check the help :h :Vimgrep and :h quickfix.

Inserting Unicode Characters in Vim

You can insert unicode characters in Vim using the unicode character code by typijng: <C-V>u{code}. This is super helpful when you want to insert unicode code characters for icon fonts to make your Vim experience be nicer.

Interesting Tips from Vim’s Help

Jump to a subject: Position the cursor on a tag (e.g. |bars|) and hit CTRL-]. Use CTRL-T or CTRL-O to come back to the original position

Getting Specific Help With Context

It is possible to go directly to whatever you want help on, by giving an argument to the |:help| command. It is possible to further specify the context:

WHAT                    PREPEND     EXAMPLE  ~
Normal mode command     (nothing)   :help x
Visual mode command     v_          :help v_u
Insert mode command     i_          :help i_<Esc>
Command-line command    :           :help :quit
Command-line editing    c_          :help c_<Del>
Vim command argument    -           :help -r
Option                  '           :help 'textwidth'

Syntax Highlighting

Syntax highlighting in Vim works in the following manner:

  1. You create syntax groups with :syntax to match the syntax of a given language (most of the times you can ust reuse syntax groups that come with Vim or provided by a plugin)
  2. You create highlighting groups with :highlight to connect syntax to highlighting styles.

For more info, check the user manual at :h usr_44.

Showing Invisible Characters

  • To show invisible characters use :set list
  • To hide them use :set nolist
  • You can find more information in :help :list and :help 'list'

Resources

Interesting Talks on Vim


Jaime González García

Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.Jaime González García