One of the powerful things about Vim which is unlike other editors is that
its commands are able to be combined with each other to form larger commands.
For instance, the 'd
' command is for deleting things. We have seen using
'dd
' for deleting a line, but 'd
' is really a Vim "verb"
which can be combined with "movements" which allow us to delete other things:
dw
- Delete from here to the end of the word.dj
- Delete this line and the one below.dG
- Delete from here to the end of the file.In fact, we can make "verb-movement" commands from any of these verbs:
Verb | Meaning |
d | Delete. |
c | Change (delete and enter insert mode). |
y | Copy. |
= | Re-indent. |
> | Indent one more level. |
< | Indent one fewer level. |
And these movements:
Movement | Meaning |
h | The character to the left. |
j | This line and the one below it. |
k | This line and the one above it. |
l | The character to the right. |
^ | From here to the start of the line. |
$ | From here to the end of the line. |
gg | From here to the start of the file. |
G | From here to the end of the file. |
{ | From here to the start of the paragraph. |
} | From here to the end of the paragraph. |
% | From here to the matching parenthesis, brace or bracket. |
f{char} | From here until the next instance of {char} on the current line. For instance fw will move to the next w character on the current line. |
t{char} | From here until the character right before the next instance of {char} on the current line. |
F{char} | From here until the previous instance of {char} on the current line. |
T{char} | From here until the character right after the previous instance of {char} on the current line. |
For example, the command =G
will re-indent everything from
the current cursor location up until the end of the file, and yfP
will copy everything from the current location until (and including) the
next 'P' character.
As we've seen, we can also use "verb-verb" commands such as "dd
"
or "yy
" which mean to apply whichever verb for just the current
line.
Vim also allows applying verbs to higher level textual structures it calls text objects. With these we can perform "verb-text object" commands which apply the verb to the text object. Some useful text objects are listed below:
Text Object | Meaning |
i} | Text inside curly braces. |
a} | Text inside curly braces and the braces themselves. |
i" | Text inside quotations. |
a" | Text inside quotations and the quotations themselves. |
i) | Text inside parentheses. |
a) | Text inside parentheses and the parentheses themselves. |
iw | The current word. |
aw | The current word and the space after it. |
is | The current sentence. |
as | The current sentence and the space after it. |
ip | The current paragraph. |
ap | The current paragraph and the new line after it. |
The first six are most useful for programming, while the last six are more useful for writing regular text.
These allow us to build very succinct shortcuts. For instance, suppose we are editing a program with the line:
print("Hello, how are you?")
And we wish to change the printed message. This can be done using the
ci"
command which will jump to the quotes, erase everything inside
of it, and put us into insert mode. This works even if the cursor is not inside
the quotation marks.
Likewise, if we are writing a paper, and wish to move a sentence, we can issue
the das
command, then move to where we wish to place it and enter the
p
command.
It takes time to get used to using these bigger commands, and taking advantage of text objects, but it allows you to edit text at a higher level. Instead of dealing with just a string of characters, you can edit text at the sentence, paragraph, or code block level.
Vim commands can also be prefaced by counts which are integers
which indicate how many times the command should be performed. For instance,
3dd
says to delete three lines.
Movements can contain counts as well. For instance, we can type 3w
which moves us three words forwards and is equivalent to typing www
.
Movements within commands can have counts as well. For instance the command
y2fw
will copy from here until the second instance of the 'w' character.
Counts can also be used with text objects which can be really helpful. Suppose we have the following code:
if (x == y) {
// some comment
while (x < 0) {
x = x + 1;
print("Some stuff is happening!");
}
}
Also suppose that the cursor is within the while loop body, on the
print statement line. If we wanted to delete the entire body of the
while loop, we could use di}
which would give us:
if (x == y) {
// some comment
while (x < 0) {
}
}
The text object i}
matches the most nested set of braces we are in.
If we wanted to remove everything in the if block, however, we could instead
use d2i}
which deletes the second most nested brace pair, giving
us:
if (x == y) {
}
When we use the d
or y
verb in a command, it saves
the text which is deleted or yanked into a register which is
like a Vim variable used to hold text (similar to the "clipboard" used
by GUI systems). When we use the p
or
P
commands, the text in a register is put into the file we
are editing.
By default, Vim uses the "unnamed register" for these purposes. If you only ever use the unnamed register, then each time you delete or yank something, it will overwrite the previous text of the register.
Vim also allows us to specify a named register which can be any lowercase letter. This is done by prefacing a command with a " character, then the register we wish to use.
For instance, to delete a line into the "j" register, we could use
"jdd
. Then if we wish to paste from that register, we could
use "jp
.
Using registers is nice since it lets you save something in a clipboard location and then be able to use the regular copy and cut commands without losing it.
Vim marks are like bookmarks in the file you are editing. They allow you to mark specific points in the file which you can quickly return to later.
You can create a mark with the m
command followed by a lowercase
letter. This gives you 26 marks to work with. So ma
will mark the
current location as mark 'a'.
We can then jump back to mark 'a' at any point with either the command 'a
or `a
. The only difference is the first will take you back to the start
of the line containing mark a, while the second will take you to the exact position
you marked (the line and column).
Because our mark jumps are Vim motions, they can also be used with verbs
to form commands. For instance, d'a
says to delete everything from
here until the line containing mark a.
Your marks in a file will persist across multiple launches of Vim. So you can assign a mark to an important part of a file, then jump to it each time you open that file.
We have seen that the i
key can be used to enter insert mode,
but there are several other ways to enter insert mode which are convenient
in some situations:
Key | Meaning |
i | Enter insert mode starting here. |
I | Enter insert mode at the beginning of this line. |
a | Enter insert mode one character to the right. |
A | Enter insert mode at the end of this line. |
o | Add a line after this line and enter insert mode. |
O | Add a line before this line and enter insert mode. |
s | Delete the current character and enter insert mode. |
The '.' command is, in my opinion, the best feature of Vim, that I miss most sorely when I need to use an inferior editor. The '.' command repeats the last action that you performed. For instance, if we have the text:
Hello this is a sentence.
And the cursor is on the word "this". We can delete the word "this" by
typing daw
. This will give us:
Hello is a sentence.
Now if we wanted to delete "is", we could again hit daw
, or we
could just repeat the last one with .
which will give us:
Hello a sentence.
Tap .
two more times an all we have is:
Hello.
The .
command can allow you to save a lot of time. As another
example, suppose we have some code like this:
name = input()
while name == "":
print("Invalid name!")
name = input()
And suppose we wanted to comment out the while loop. We could do that by
manually moving to the beginning of each line and adding a '#' character. Or
we could go to the first line to comment and type I
to insert at
the beginning of the line. Then type the #
and then hit escape.
Now the action we just did is "insert a # at the beginning of the line", and
this whole action can be repeated. To comment the other lines, we can just
type j.j.
as j
moves down a line and .
comments it out.
Copyright © 2024 Ian Finlayson | Licensed under a Creative Commons BY-NC-SA 4.0 License.