MOVING AROUND

Learning Vim

Chapter 3 — Moving Around

Why Movement Matters

In most editors, moving around the file is an afterthought — you use the mouse, or hold arrow keys until you get where you want to go. In Vim, movement is a first-class skill. Because all editing happens in Normal mode using keyboard commands, the speed at which you can navigate directly determines the speed at which you can edit.

Vim's movement commands are also the foundation of its operator + motion grammar. Commands like "delete the next three words" or "copy from here to the end of the paragraph" are built by combining an editing operator with a movement command. Learning to move precisely and quickly therefore unlocks not just navigation but the entire editing system.

Everything in this chapter is a NORMAL mode command unless stated otherwise.

The HJKL Keys — Character Movement

Vim uses h, j, k, and l as arrow keys. This is not an arbitrary choice — it has its roots in the original ADM-3A terminal from the 1970s, on which the arrow symbols were actually printed on those keys. The layout has a practical advantage: these four keys sit on the home row of the keyboard, right where your fingers already rest. You never need to move your hand to the arrow cluster.

KeyDirectionMemory aid
hLeft (← )Leftmost of the four keys
jDown (↓ )The letter j has a tail that points down
kUp (↑ )k is above j on the keyboard
lRight (→ )Rightmost of the four keys
Arrow keys still work — Vim accepts arrow keys for movement in both Normal and Insert mode. However, using them forces your right hand off the home row and across to the arrow cluster on every navigation. Most Vim users find that after a few days of consciously using HJKL, it becomes automatic and they never want to go back.

Multiplying movement with counts

Any movement command can be prefixed with a number to repeat it that many times. This is one of the most efficient features of Vim navigation — instead of holding a key down and watching the cursor drift, you specify exactly how far you want to go.

5j      # move 5 lines down
10l     # move 10 characters to the right
3k      # move 3 lines up
8h      # move 8 characters to the left

Counts work with almost every movement command in this chapter — not just HJKL. When you see a new command, assume a count prefix will work unless stated otherwise.

Relative line numbers and counts: When :set relativenumber is enabled (introduced in Chapter 1), the number shown next to each line is its distance from the cursor. This makes count-based jumps trivial — you glance at the line you want, read off its number, and type that number followed by j or k.

Word-Level Movement

Moving character by character is slow even with counts. For navigating within a line or across a paragraph, word-level movements are far more efficient.

KeyMovement
wForward to the start of the next word
WForward to the start of the next WORD (whitespace-delimited — ignores punctuation)
eForward to the end of the current (or next) word
EForward to the end of the current WORD
bBackward to the start of the current (or previous) word
BBackward to the start of the current WORD
geBackward to the end of the previous word
word vs WORD: In Vim, a lowercase word is a sequence of letters, digits, and underscores — roughly what a programmer would call an identifier. An uppercase WORD is any sequence of non-whitespace characters. So in the text file-name.txt, w sees three words (file, -, name.txt approximately), whereas W sees one WORD. Use lowercase for precision within punctuation-heavy text; use uppercase for coarser, faster jumps.

Example — word movement on a line

The quick brown fox jumps over the lazy dog

Starting with cursor on "T" in "The":

CommandCursor lands on
wq (start of "quick")
3wf (start of "fox")
ee (end of "The")
2ek (end of "quick"… wait, end of "brown")
bT (back to start of "The")
4wj (start of "jumps")

Line Movement

Moving within a line precisely is something you do constantly. These commands get you to the right position instantly:

KeyMovement
0Jump to the very beginning of the line (column 1)
^Jump to the first non-whitespace character of the line
$Jump to the end of the line
g_Jump to the last non-whitespace character of the line
f{char}Find — jump forward to the next occurrence of char on the line
F{char}Find — jump backward to the previous occurrence of char on the line
t{char}Till — jump forward to just before char on the line
T{char}Till — jump backward to just after char on the line
;Repeat the last f, F, t, or T in the same direction
,Repeat the last f, F, t, or T in the opposite direction

Example — f and t in action

background-color: rgba(255, 128, 0, 0.5);

Starting with cursor at the beginning of the line:

  • f( — jumps to the ( before 255
  • t, — jumps to just before the first , (cursor sits on 5)
  • ; — repeats: jumps to just before the next ,
  • f; — jumps to the ; at the end of the line

This is much faster than pressing l repeatedly or using the arrow keys to navigate within a long line.

The difference between 0 and ^: 0 always goes to column 1, even if the line is indented. ^ goes to the first actual character, skipping any leading whitespace. In code, you almost always want ^ — it puts you at the start of the statement, not at the indentation.

Sentence and Paragraph Movement

When working with prose or structured documents, jumping sentence-by-sentence or paragraph-by-paragraph is faster than any word-level navigation.

KeyMovement
)Forward to the beginning of the next sentence
(Backward to the beginning of the current (or previous) sentence
}Forward to the beginning of the next paragraph (next blank line)
{Backward to the beginning of the previous paragraph
What counts as a sentence? Vim defines a sentence as ending with ., !, or ? followed by either end-of-line or at least two spaces. A paragraph is defined as a block of non-empty lines — it ends and begins at a blank line. In code, } and { are most useful for jumping between functions or blocks separated by blank lines.

As with all movement commands, counts work here too:

3}     # jump 3 paragraphs forward
2{     # jump 2 paragraphs backward
5)     # jump 5 sentences forward

File-Level Navigation

For large files you need to jump across long distances — to the top, the bottom, a specific line, or a screenful at a time.

Top and bottom

KeyMovement
ggJump to the first line of the file
GJump to the last line of the file
{n}GJump to line n (e.g. 42G jumps to line 42)
:{n}Jump to line n via the command line (e.g. :302)
{n}%Jump to n% through the file (e.g. 50% jumps to the midpoint)
{n}G vs :{n}: Both jump to line n. {n}G is faster to type in Normal mode (just the number then G). :{n} is useful when you know the number from an error message — you can type it quickly after pressing :. Both are worth knowing.

Page scrolling

KeyMovement
Ctrl+FScroll forward one full page (keeps a couple of lines for context)
Ctrl+BScroll backward one full page
Ctrl+DScroll down half a page
Ctrl+UScroll up half a page
Ctrl+EScroll the screen down one line without moving the cursor
Ctrl+YScroll the screen up one line without moving the cursor

Cursor positioning on screen

These three commands reposition the view around your cursor, not the cursor itself — useful for reading context around your current position:

KeyAction
zzScroll so the current line is in the centre of the screen
ztScroll so the current line is at the top of the screen
zbScroll so the current line is at the bottom of the screen
zz is one of the most underrated commands in Vim. When you have just jumped to a line and want to read the surrounding context, zz centres it on screen instantly. It is also useful while reading — if the line you care about has drifted to the top or bottom of the screen, zz brings it back to eye level.

Searching

Search is arguably the fastest navigation tool in Vim for reaching a specific location. Instead of scrolling to where you think the text is, you tell Vim exactly what you are looking for and it takes you there instantly.

Forward and backward search

/pattern    # search forward for pattern — press Enter to jump
?pattern    # search backward for pattern — press Enter to jump

After pressing Enter, the cursor jumps to the first match. The matched text is highlighted across the entire file (if :set hlsearch is enabled). To navigate between matches:

KeyAction
nJump to the next match (in the direction of the original search)
NJump to the previous match (opposite direction)

The search wraps around — when you reach the end of the file Vim loops back to the beginning (you will see a message: search hit BOTTOM, continuing at TOP).

Clearing search highlights: After a search, the highlighted matches stay on screen until you search for something else. To clear the highlights without searching, run :nohlsearch (or its abbreviation :noh). Many users map this to a key in their config — for example :nnoremap <Esc><Esc> :nohlsearch<CR>.

Searching for the word under the cursor

Often the word you want to search for is already under your cursor. Rather than typing it out, use:

KeyAction
*Search forward for the exact word under the cursor (whole-word match)
#Search backward for the exact word under the cursor
g*Search forward for the word under the cursor (partial match — finds it inside longer words too)
g#Search backward (partial match)

* is particularly useful when reading code — place your cursor on a variable name and press * to jump through all its usages in the file.

Regular expressions in search

Vim's search supports regular expressions, making it enormously powerful for finding patterns rather than literal strings:

/ch.mb         # . matches any single character: finds "chamber", "chimb", "chombs"...
/^function     # ^ = start of line: finds lines beginning with "function"
/error$        # $ = end of line: finds lines ending with "error"
/\d\+          # \d = digit, \+ = one or more: finds any number
/colou\?r      # \? = zero or one: finds both "color" and "colour"
/\<word\>      # \< and \> = word boundaries: whole-word match (like * does automatically)
Case sensitivity: By default, Vim's search is case-sensitive. You can change this with :set ignorecase. A smarter option is :set smartcase — when enabled, searches are case-insensitive unless your pattern contains an uppercase letter, in which case they become case-sensitive. This is the behaviour most users prefer.

Search settings worth knowing

:set hlsearch      # highlight all matches in the file
:set incsearch     # move cursor as you type the pattern (incremental search)
:set ignorecase    # case-insensitive search
:set smartcase     # case-sensitive only when pattern has uppercase
:noh               # clear current search highlights

Putting It All Together — Practical Examples

Navigation commands become powerful when combined. Here are the worked examples from the course, showing how you think through a multi-step navigation problem.

Navigate to the 4th word of the 2nd-to-last sentence of the 3rd paragraph

3}     # jump 3 paragraphs forward (land at the start of paragraph 3)
2{     # oops — went too far? Step back 2 paragraphs
       # (adjust the count based on where you land)
(     # move back one sentence to the 2nd-to-last
3w     # move forward 3 words — now on the 4th word

The key insight is that you do not need to be perfect on the first jump. Vim navigation is iterative — overshoot, correct, refine.

Find the word immediately before "England"

/England    # jump to "England"
b           # move back one word — the word before "England"

Find the last word on line 302

:302    # jump to line 302
$       # jump to the end of the line — cursor is now on the last character
b       # (optional) back to the start of that last word

Find the word after the 2nd occurrence of the first word on line 14

:14    # jump to line 14
^      # go to the first non-whitespace character (start of first word)
*      # search forward for this word (1st occurrence — which might be itself)
n      # next occurrence (2nd)
w      # move one word forward — now on the word after the 2nd occurrence

Commands Introduced in This Chapter

Chapter 3 — Command Reference

Character movement
h / j / k / lLeft / down / up / right (prefix with count: 5j)
Word movement
wForward to start of next word
WForward to start of next WORD (whitespace-delimited)
eForward to end of current/next word
EForward to end of current/next WORD
bBackward to start of current/previous word
BBackward to start of current/previous WORD
geBackward to end of previous word
Line movement
0Start of line (column 1)
^First non-whitespace character of line
$End of line
g_Last non-whitespace character of line
f{char}Jump forward to next occurrence of char on this line
F{char}Jump backward to previous occurrence of char on this line
t{char}Jump forward to just before char on this line
T{char}Jump backward to just after char on this line
;Repeat last f/F/t/T in the same direction
,Repeat last f/F/t/T in the opposite direction
Sentence & paragraph movement
)Forward to start of next sentence
(Backward to start of current/previous sentence
}Forward to next paragraph (blank line)
{Backward to previous paragraph
File-level navigation
ggJump to first line of file
GJump to last line of file
{n}GJump to line n (e.g. 42G)
:{n}Jump to line n via command line (e.g. :302)
{n}%Jump to n% through the file
Ctrl+FScroll one full page forward
Ctrl+BScroll one full page backward
Ctrl+DScroll half a page down
Ctrl+UScroll half a page up
Ctrl+EScroll screen down one line (cursor stays)
Ctrl+YScroll screen up one line (cursor stays)
zzCentre current line on screen
ztScroll current line to top of screen
zbScroll current line to bottom of screen
Search
/{pattern}Search forward for pattern (Enter to jump)
?{pattern}Search backward for pattern
nNext match (same direction as search)
NPrevious match (opposite direction)
*Search forward for exact word under cursor
#Search backward for exact word under cursor
g*Search forward for word under cursor (partial match)
g#Search backward (partial match)
:nohClear search highlights
:set hlsearchHighlight all search matches
:set incsearchMove cursor as you type the search pattern
:set ignorecaseCase-insensitive search
:set smartcaseCase-sensitive only when pattern contains uppercase