Vim is a terminal-based editor optimized for speed. Keys are hotkeys by default. Vim does not even use Ctrl
for most of them. Maneuvering via hotkeys is so efficient mouse input is often disabled by default.
This guide focuses on the features I use most frequently. It has nothing about with native Vim windows because I use i3 or Spacemacs instead. If you feel I left out a valuable feature then please let me know about it in the comments.
Movement
The simplest way to move is via the hjkl
keys.
key | action |
---|---|
h | moves the cursor one character left |
j | moves the cursor one line down |
k | moves the cursor one line up |
l | moves the cursor one character right |
The hl
keys operate within a single line. Pressing h
at the start of a line does nothing. Pressing l
at the end of a line does nothing. By line I mean a string of characters ending with a carriage return. The fact that a long paragraph appears to wrap around on your screen is irrelevant.
The jk
keys move the cursor down and up a single line. If the cursor is in the 3rd column of the 6th line then pressing j
moves the cursor to the 3th column of the 7th line.
Most commands can be prefixed with a number. 4h
moves the cursor left 4 characters. 101j
moves the cursor down 101 lines.
A sequence of nonempty lines is called a paragraph. The {}
keys move the cursor forward and back by paragraphs.
key | action |
---|---|
} | moves the curser forward one paragraph |
{ | moves the curser backward one paragraph |
There are two kinds of "words" in Vim. A word is a sequence of alphanumeric characters. A Word is a sequence of non-whitespace characters. The wbe
keys move the cursor around words. The WBE
keys move the cursor around Words.
key | action |
---|---|
w | moves the cursor forward to the next start of a word |
e | moves the cursor forward to the next end of a word |
b | moves the cursor backward to the next start of a word |
W | moves the cursor forward to the next start of a Word |
E | moves the cursor forward to the next end of a Word |
B | moves the cursor backward to the next start of a Word |
The #*
keys move the cursor forward and backward to the next "idenfitier". If the cursor hovers over the word "marmot" then pressing *
will move the cursor forward to the next instance of the word "marmot" and pressing #
will move the cursor backward to the previous instance of the word "marmot".
key | action |
---|---|
# | moves the cursor backward to the previous identifier |
* | moves the cursor forward to the previous identifier |
The f
key takes a character as an argument and moves the cursor forward to that character. For example, fa
moves the cursor forward to the next instance of a
. The f
key operates within a line. It will never take the cursor to another line. The F
command is like f
except it searches backward. The t
and T
commands are like f
and F
exvcept they move the cursor one fewer character.
The ;
command repeats the most recent fFtT
command. The ,
command is like ;
except backwards. If you use an F
command followed by a ,
then the double-backwardsness will cancel itself out.
key | action |
---|---|
f<char> | moves forward to <char> |
F<char> | moves backward to <char> |
t<char> | moves forward to the character before <char> |
T<char> | moves backward to the character after <char> |
; | repeats the previous fFtT command |
, | repeats the previous fFtT command except reversed |
The 0^$
keys move the cursor to the beginning and end of a line.
key | action |
---|---|
0 | moves the cursor to the beginning of the line |
^ | moves the cursor to the beginning of the line, excluding whitespace |
+ | moves the cursor to the beginning of the next line, excluding whitespace |
$ | moves the cursor to the end of the line |
The HLM
keys move the cursor around relative to the viewing window itself.
key | action |
---|---|
H | moves the cursor the the start of the first visible line |
M | moves the cursor the the start of the middle visible line |
L | moves the cursor the the start of the last visible line |
You can move the viewing window itself with z
and Ctrl
keys.
key | action |
---|---|
Ctrl-e | moves the viewing window down one visual line |
Ctrl-y | moves the viewing window up one visual line |
Ctrl-f | moves the viewing window forward by one viewing window |
Ctrl-b | moves the viewing window backward by one viewing window |
Ctrl-d | moves the viewing window forward by half of one viewing window |
Ctrl-u | moves the viewing window backward by half of one viewing window |
zz | moves the viewing window to position the cursor in the middle |
zt | moves the viewing window to position the cursor at the top |
zb | moves the viewing window to position the cursor at the bottom |
You can move the cursor itself up and down visible lines with the g
prefix.
key | action |
---|---|
gj | moves the cursor down one visual line |
gk | moves the cursor up one visual line |
The g
key can also go to a particular line.
key | action |
---|---|
gg | jumps the cursor to the beginning of the buffer[1] |
G | jumps the cursor to the end of the buffer |
<num>gg | jumps the cursor to the specified line |
<num>G | '' |
The |
key is like g
except for columns.
key | action |
---|---|
<num>| | moves the cursor to the <num>th column within a line |
The /
key performs search. You type /
then the string you are searching for and then press Enter
. The ?nN
keys are analogous to F;n
.
key | action |
---|---|
/ | initiates a search |
? | initiates a backwards search |
n | repeats the previous search |
N | repeats the previous search except reversed |
The ()
characters move forward and backward one sentence.
key | action |
---|---|
) | moves forward to the next start of a sentence |
( | moves backward to the next start of a sentence |
The []
characters move the cursor forward and backward to a variety of things. My favorite uses of []
is to go forward and backward to matching parenthesis. For example, ])
moves forward to the next unmatched closing parenthesis. It is indispensable when writing Lisp. Inner quotes and tags behave similarly to parentheticals.
key | action |
---|---|
]) | move forward to matching parenthesis |
]( | '' |
[) | move backward to matching parenthesis |
[( | '' |
[[ | jump to function start |
]] | jump to function end |
You can (invisibly) mark places in your buffer to return to later. You can set one mark per letter. Lowercase letters a-z are buffer-specific e.g. each buffer can have its own "a" mark. Uppercase letters A-Z are global e.g. only one buffer at a time can have an "A" mark.
key | action |
---|---|
m<a-z> | create a buffer-specific mark |
m<A-Z> | create a global mark |
'<mark> | jump to the beginning of the line with mark <mark> |
`<mark> | jump to mark <mark> |
Some marks are populated automatically. Of the automatic marks, I only use ''
. You can find the others here.
mark | meaning |
---|---|
'' | jump to last jump point |
Editing
Now that you know how to move, we can edit some text. Most of the time you edit text you should use the dyp
keys.
d
stands for delete, which is similar to "cut".y
stands for yank, which is similar to "copy".p
stands for put, which is similar to "paste".
The d
key operates on text objects and movement commands. For example dtA
deletes everything up to (but not including) the next "A" on the current line.
Every delete operation operates on characters or lines. dtA
operates on characters and thus behaves similarly to other editors. A delete operation which operates on lines deletes an integer number of lines. Delete operations which operate on lines delete the current line and the destination line. Thus, dj
deletes the current line and the next line.
y
is identical to d
except it leaves your buffer unchanged.
delete or yank key | action |
---|---|
d<char> | deletes every character from the starting character up to (and including) the destination character |
d<line> | deletes every line from the starting line up to (and including) the destination line |
dd | deletes the current line |
y<char> | yanks every character from the starting character up to (and including) the destination character |
y<line> | yanks every line from the starting line up to (and including) the destination line |
yy | yanks the current line |
p
puts the selection before the cursor. P
puts the selection after the cursor. "Before" and "after" refer to characters or lines depending on whether you deleted/yanked characters or lines.
key | action |
---|---|
p | put after the cursor |
P | put before the cursor |
By default, p
and P
will put the last text you deleted or yanked. You can save text into registers by prefixing "<register>
before your delete or yank command. For example, "add
deletes the current line and saves it into register "a".
There is one register for each letter a-z. If you use a capital letter then your delete or yank command will append to the register instead of overwriting it. You can put from a register by prefixing your p
or P
command with the register. For example, "ap"
will put from register "a".
You can find a list of special registers here.
Text Objects
Remember how I said you could follow d
and y
with a movement command or a text object? Remember from earlier the concept of "words" and "paragraphs"? Here we put them together into a simple grammar.
da
deletes a text object.
key | action |
---|---|
daw | delete a word |
daW | delete a Word |
das | delete a sentence |
dap | delete a paragraph |
da( | delete a parenthetical |
da) | '' |
yaw | yank a word |
yaW | yank a Word |
yas | yank a sentence |
yap | yank a paragraph |
ya( | yank a parenthetical |
ya) | '' |
You can replace the a
with an i
to delete an inner word, an inner paragraph, etcetera. An inner text object is just like the regular text object except it does not include any surrounding delimiters. In the case of a parenthetical, the "inner parenthetical" does not include the outside parenthesis. For words/sentences/paragraphs, the delimiter is whitespace.
key | action |
---|---|
diw | delete inner word |
diW | delete inner Word |
dis | delete inner sentence |
dip | delete inner paragraph |
di( | delete inner parenthetical |
di) | '' |
yiw | yank inner word |
yiW | yank inner Word |
yis | yank inner sentence |
yip | yank inner paragraph |
yi( | yank inner parenthetical |
yi) | '' |
Other text objects include '"[]{}<>
. They all do what you would expect them to do. t
refers to an HTML-like tag.
Insert Mode
You cannot write everything by yanking and putting pieces of existing text together. Sometimes you have to insert text into a document. Several different keys drop you into insert mode.
key | action |
---|---|
i | inserts text before the cursor |
a | inserts text after the cursor |
I | inserts text at the beginning of the line |
A | inserts text at the end of the line |
o | creates a newline below the current line and inserts text there |
O | creates a newline above the current line and inserts text there |
s | deletes the current character and inserts text |
S | deletes the current line and inserts text |
You can exit insert mode by pressing Escape
but it is faster to remap your CapsLock
key to Ctrl
and then exit insert mode with Ctrl-[
. Some hotkeys are valid only while in insert mode.
insert mode key | action |
---|---|
Escape | exits insert mode |
Ctrl-[ | '' |
Ctrl-F | moves cursor forward one character |
Ctrl-b | moves cursor backward one character |
Ctrl-w | deletes one word backward (without saving it to a register) |
The c
key is like d
except it drops you into insert mode afterward.
key | action |
---|---|
c<movement> | clear text |
c<text_object> | clear text |
After you exit insert mode, the whole insertion counts as a single edit. So if you type ci(
followed by an insertion then the entire replacement of text inside the parenthetical counts as a single edit. You can repeat an edit with the .
operator.
key | action |
---|---|
. | repeat previous edit |
The .
key might be the most important key in all of Vim. Generally-speaking, the more you use the .
operator the better you are at Vim.
Miscellaneous edit commands
key | action |
---|---|
x | delete the character under the cursor |
X | delete the character before the cursor |
~ | toggle capitalization and move the cursor forward one character |
r<char> | replace a single character |
The Undo Tree
u
and Ctrl-r
operate like the undo and redo stack you are familiar with.
key | action |
---|---|
u | undo last edit |
Ctrl-r | redo next edit |
If you undo a long series of edits and then mistakenly make an edit you can undo the damage with g+
and g-
which traverse the nodes of the tree in chronological and reverse-chronological order.
key | action |
---|---|
g+ | traverse undo tree in chronological order |
g- | traverse undo tree in reverse chronological order |
Macros
A macro is a series of keystrokes. Macros are good for automating repetitive tasks, especially editing structured text.
To define a macro, start by pressing the q
key. Then pick a letter a-z at which to save the macro. Then execute the macro manually. When you are done typing the macro, press q
again.
Once you have a macro defined, you can press @
followed by the macro's letter to execute the macro.
key | action |
---|---|
q | define a macro |
@<a-z> | execute a macro |
@@ | execute previous macro |
Macros are well-behaved. If a macro modifies a line and then goes down one line and you tell Vim to execute the macro 1000 times but your buffer only has 700 lines then Vim will stop when it gets to the end of your buffer.
Visual mode
Visual modes are similar to highlighting. Visual modes have their uses, but it is usually faster to avoid them.
key | action |
---|---|
v | enter visual mode |
V | enter visual line mode |
Ctrl-v | enter visual rectangle mode |
Find and Replace
You can find and replace all the instances of a string by typing :%s/
followed by the original string followed by /
followed by the replacement string followed by Enter…assuming you did not type any escape sequences. Find and replace is a complex subject I will not delve into here even though I do use it.
Enter and exit
You can enter Vim by typing vim
into your terminal followed by the file you would like to create or edit. You can exit vim by typing :
followed by a quit command.
command | action |
---|---|
:q | exit if no changes are pending |
:q! | force an exit even if changes are pending |
:w | write to disk |
:wq | write to disk and then exit |
Typing ZZ
does the same thing as the :wq
command.
Cheatsheet
If you are not from a Unix back then the term "buffer" may be unfamiliar. Just translate "buffer" to "file" or "document" in your head even though, technically, a buffer is more general than a document. ↩︎
A question for the people who use vim keybindings in IDEs: how do you deal with keybindings for IDE tasks which are not part of vim (like using the debugger, refactoring, code completion, etc.)? The last time I tried to use vim bindings in an IDE there were quite some overlaps with these so I found myself coming up with compromise systems which didn't work that well because they weren't coherent.
At least for PyCharm, this was somewhat easier on macOS than on Windows, since you have
control
,option
,command
andshift
, instead of justCtrl
,Alt
, andShift
(well, and theWin
key, but the OS reserves too many bindings there.) On macOS, The IDE usescommand
for most things, while Vim usually usescontrol
when it needs a modifier at all. On Windows they both want to useCtrl
, so it's more difficult to configure all the bindings.