LEARN VIM MOTIONS
Most text editors (Notepad, VS Code, Sublime) treat text as a simple string of characters. You place a cursor and insert/delete characters. This is Modeless editing. It's intuitive, but inefficient.
Learn Vi Motions: The Language of Text Editing
Goal: Wire your brain to speak the “grammar” of Vim. Stop editing character-by-character and start editing by word, sentence, block, and logic.
Why Vim Motions Matter
Most text editors (Notepad, VS Code, Sublime) treat text as a simple string of characters. You place a cursor and insert/delete characters. This is “Modeless” editing. It’s intuitive, but inefficient.
Vim is a Modal editor. It treats text as Structured Objects (words, sentences, paragraphs, code blocks).
To master Vim, you must stop thinking:
- “Move cursor right 5 times, backspace 3 times, type ‘hello’.”
And start thinking:
- “Change inside quotes” (
ci") - “Delete until next comma” (
dt,) - “Copy this paragraph” (
yap)
You are not learning a set of shortcuts (like Ctrl+C). You are learning a Language.
The Grammar of Vim
Vim commands follow a linguistic structure: Verb + Adjective + Noun.
┌─────────────────────────────────────────────────────────────────────────────┐
│ THE VIM COMMAND GRAMMAR │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ COMMAND STRUCTURE │
│ │
│ [COUNT] + OPERATOR + [COUNT] + MOTION/TEXT-OBJECT │
│ │
│ Example: 2 d 3 w │
│ ↓ ↓ ↓ ↓ │
│ "twice" "delete" "three" "words" │
│ │
│ Result: Delete 6 words (2 × 3 = 6) │
└──────────────────────────────────────────────────────────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐
│ OPERATORS │ │ MODIFIERS │ │ MOTIONS/OBJECTS │
│ (Verbs) │ │ (Adjectives) │ │ (Nouns) │
├─────────────────┤ ├─────────────────┤ ├─────────────────────────────┤
│ d = delete │ │ i = inner │ │ w = word W = WORD │
│ c = change │ │ a = around │ │ s = sentence │
│ y = yank (copy) │ │ t = till │ │ p = paragraph │
│ v = visual │ │ f = find │ │ b = back word │
│ > = indent │ │ 2,3,4 = count │ │ e = end of word │
│ < = outdent │ │ │ │ } = next paragraph │
│ = = format │ │ │ │ { = prev paragraph │
│ g~ = toggle case│ │ │ │ " = quotes ' = quotes │
│ gu = lowercase │ │ │ │ ( ) = parentheses │
│ gU = uppercase │ │ │ │ { } = braces [ ] = brackets│
└─────────────────┘ └─────────────────┘ └─────────────────────────────┘
┌───────────────────────────────────────┐
│ EXAMPLE COMBINATIONS │
├───────────────────────────────────────┤
│ d + i + w = delete inner word │
│ c + a + " = change around " │
│ y + 2 + j = yank 2 lines down │
│ > + i + { = indent inner block │
│ gU + a + p = UPPERCASE paragraph │
└───────────────────────────────────────┘

- Verbs (Operators): What you want to do.
d: Deletec: Change (delete + insert)y: Yank (copy)v: Visual select>: Indent
- Adjectives (Counts/Modifiers): How many or which one.
3: Three timesi: Insidea: Around
- Nouns (Motions/Objects): The target.
w: Words: Sentencep: Paragrapht": Tag (HTML/XML)}: Block
Examples:
d2w: Delete (verb) 2 (adjective) words (noun).ci(: Change (verb) inside (adjective) parentheses (noun).
Core Concept Analysis
1. The Modes: Why “Normal” is Normal
In other editors, you are always in “Insert Mode”. In Vim, you spend most of your time in Normal Mode.
┌─────────────────────────────────────────────────────────────────────────────┐
│ VIM'S MODAL EDITING │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ NORMAL MODE │ ← You spend 80% of time here
│ (Default) │
│ │
│ • Navigate │
│ • Delete │
│ • Copy/Paste │
│ • Undo/Redo │
└──────┬───────┘
│
┌─────────────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ INSERT MODE │ │ VISUAL MODE │ │ COMMAND MODE │
│ │ │ │ │ │
│ Enter: i,a,o │ │ Enter: v,V │ │ Enter: : │
│ Exit: <Esc> │ │ Ctrl+v │ │ Exit: Enter │
│ │ │ Exit: <Esc> │ │ or Esc │
│ • Type text │ │ │ │ │
│ • Add content│ │ • Select │ │ • Save :w │
│ │ │ • Highlight │ │ • Quit :q │
│ │ │ • Block ops │ │ • Search / │
└──────────────┘ └──────────────┘ └──────────────┘
┌─────────────────────────────────────┐
│ MODE TRANSITIONS │
├─────────────────────────────────────┤
│ Normal → Insert: i, I, a, A, o, O │
│ Normal → Visual: v, V, Ctrl+v │
│ Normal → Command: : │
│ Any → Normal: <Esc> or Ctrl+[ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ INSERT MODE VARIANTS │
├─────────────────────────────────────┤
│ i = insert before cursor │
│ I = insert at start of line │
│ a = append after cursor │
│ A = append at end of line │
│ o = open line below │
│ O = open line above │
└─────────────────────────────────────┘

- Normal Mode: For navigation and manipulation. Keys are commands, not characters.
- Insert Mode: For typing new text.
- Visual Mode: For selecting blocks of text.
- Command Mode: For global operations (save, quit, search-replace).
Insight: You only enter Insert Mode to add content. As soon as you stop typing, you hit Esc to return to Normal Mode. This keeps your hands on the home row and your brain in “command” mode.
2. The Dot Command (.)
The . key repeats the last change. This is the single most powerful feature in Vim.
If you structure your edits correctly (e.g., using dw instead of repeating backspace), the . key allows you to replay complex edits with a single keystroke. It turns you into a macro machine.
┌─────────────────────────────────────────────────────────────────────────────┐
│ THE DOT COMMAND │
│ Vim's "Do It Again" Button │
└─────────────────────────────────────────────────────────────────────────────┘
THE DOT FORMULA
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ONE CHANGE + ONE MOTION + REPEAT (.) = PRODUCTIVITY │
│ │
│ ↓ ↓ ↓ │
│ Make edit Navigate Press "." │
│ once to next spot to replay │
│ │
└─────────────────────────────────────────────────────────────────┘
WHAT DOES "." REPEAT?
┌─────────────────────────────────────────────────────────────────┐
│ The dot command repeats the last CHANGE, which includes: │
│ │
│ • Everything from entering Insert mode to leaving it │
│ (i...text...<Esc> is ONE change) │
│ │
│ • Operators with motions (dw, cit, yap, etc.) │
│ │
│ • Single-key changes (x, r, ~, etc.) │
└─────────────────────────────────────────────────────────────────┘
PRACTICAL EXAMPLE
Task: Change "foo" to "bar" in multiple places
┌──────────────────────────────────────────────────────────────────┐
│ Step 1: Search for pattern │
│ /foo<Enter> │
│ Cursor jumps to first "foo" │
│ │
│ Step 2: Make the change ONCE │
│ cw bar <Esc> │
│ "change word" + type "bar" + exit insert mode │
│ │
│ Step 3: Navigate and repeat │
│ n → jump to next "foo" │
│ . → replay "change word to bar" │
│ n → jump to next "foo" │
│ . → replay again │
│ (repeat n . n . n . as needed) │
└──────────────────────────────────────────────────────────────────┘
GOOD vs BAD REPEATABILITY
┌─────────────────────┬────────────────────────────────────────────┐
│ BAD (. = ?) │ GOOD (. = whole action) │
├─────────────────────┼────────────────────────────────────────────┤
│ x x x x x │ dw (delete word) │
│ (. repeats "x") │ (. repeats "delete word") │
├─────────────────────┼────────────────────────────────────────────┤
│ $a text <Esc> │ A text <Esc> │
│ (. repeats "a") │ (. repeats "append at EOL + text") │
├─────────────────────┼────────────────────────────────────────────┤
│ F( x f) x │ df) (delete to and including ')') │
│ (. repeats "x") │ (. repeats the entire deletion) │
└─────────────────────┴────────────────────────────────────────────┘
KEY INSIGHT
┌─────────────────────────────────────────────────────────────────┐
│ Think BEFORE you edit: │
│ "How can I make this change repeatable with a single '.'?" │
│ │
│ Use COMPOUND commands: │
│ • A instead of $a (append at end of line) │
│ • I instead of ^i (insert at start of line) │
│ • C instead of c$ (change to end of line) │
│ • S instead of cc (substitute entire line) │
│ • O instead of ko (open line above) │
└─────────────────────────────────────────────────────────────────┘

3. Text Objects: Editing Semantics
Instead of highlighting text with a mouse, you address the “object” you want to edit.
┌─────────────────────────────────────────────────────────────────────────────┐
│ VIM TEXT OBJECTS │
│ "Inner" vs "Around" Explained │
└─────────────────────────────────────────────────────────────────────────────┘
THE GIFT BOX MENTAL MODEL
┌─────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ The Gift │ │ ← AROUND │
│ │ │ (i = inner) │ │ (a) │
│ │ │ │ │ │
│ │ └─────────────────────────────────┘ │ │
│ │ The Box │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
EXAMPLE: The string "hello world"
Original: "hello world"
↑ ↑
│ └─ closing quote (delimiter)
└─ opening quote (delimiter)
di" (delete inner quotes):
Before: "hello world" After: ""
^^^^^^^^^^^^ ↑↑
deleted quotes remain
da" (delete around quotes):
Before: "hello world" After: (nothing)
^^^^^^^^^^^^^
entire thing deleted including quotes
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMMON TEXT OBJECTS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ WORDS BLOCKS/DELIMITERS │
│ ────── ───────────────── │
│ iw = inner word i" = inner quotes "..." │
│ aw = around word a" = around quotes │
│ iW = inner WORD (whitespace) i' = inner single quotes '...' │
│ aW = around WORD i` = inner backticks `...` │
│ i( or ib = inner parentheses (...) │
│ SENTENCES & PARAGRAPHS i{ or iB = inner braces {...} │
│ ────────────────────── i[ = inner brackets [...] │
│ is = inner sentence i< = inner angle brackets <...> │
│ as = around sentence it = inner HTML tag <div>...</div> │
│ ip = inner paragraph at = around HTML tag │
│ ap = around paragraph │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
VISUAL COMPARISON
┌────────────────────────────────────────────────────────┐
│ Code: function(arg1, arg2) │
│ ^^^^^^^^^^^ │
│ └─ cursor here │
│ │
│ di( → function() (deletes args only) │
│ da( → function (deletes args + parens) │
│ ci( → function(█) (change: cursor inside) │
└────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────┐
│ Code: <div class="box">content</div> │
│ ^^^^^^^ │
│ └─ cursor here │
│ │
│ dit → <div class="box"></div> (delete inner tag) │
│ dat → (nothing) (delete entire tag) │
│ cit → <div class="box">█</div> (change inner tag) │
└────────────────────────────────────────────────────────┘

iw: Inner Word (the word under cursor).it: Inner Tag (content inside<div>...</div>).i": Inner Quotes (content inside"...").ap: Around Paragraph (the whole paragraph + whitespace).
4. Navigation: Search > Scroll
Beginners scroll with j and k. Masters search.
f{char}: Jump to the next occurrence of {char} on this line./pattern: Jump to the next occurrence of “pattern” in the file.*: Jump to the next occurrence of the word under the cursor.
Concept Summary Table
| Concept Cluster | What You Need to Internalize |
|---|---|
| Modal Editing | You are not always writing. You are mostly navigating and refactoring. Use Normal mode by default. |
| The Grammar | Operator + Motion = Action. Think in sentences (d2w), not keystrokes. |
| Text Objects | Don’t select manually. Tell Vim what to select (i", at, iw). |
| The Dot Formula | One change + One motion + . = Infinite productivity. |
| Search Navigation | Don’t move your eyes to the cursor; move the cursor to your eyes (using /, f, L). |
| Registers | “Copy/Paste” is actually “Yank/Put” into named slots ("ay, "bp). |
Deep Dive Reading By Concept
Read these chapters to build a strong mental model before or during the projects.
The Philosophy & Basics
| Concept | Book & Chapter | | :— | :— | | The Vim Way (Dot Formula) | Practical Vim by Drew Neil — Ch. 1: “The Vim Way” | | Normal Mode | Practical Vim — Ch. 2: “Normal Mode” | | Insert Mode | Practical Vim — Ch. 3: “Insert Mode” |
Navigation (Moving)
| Concept | Book & Chapter | | :— | :— | | File Navigation | Practical Vim — Ch. 8: “Navigate Inside Files with Motions” | | Search Navigation | Learning the vi and Vim Editors — Ch. 3: “Moving Around in a Hurry” | | Jumps & Marks | Practical Vim — Ch. 9: “Navigate Between Files with Jumps” |
Operations (Editing)
| Concept | Book & Chapter | | :— | :— | | Visual Mode | Practical Vim — Ch. 4: “Visual Mode” | | Copy & Paste (Registers) | Practical Vim — Ch. 10: “Copy and Paste” | | Macros | Practical Vim — Ch. 11: “Macros” | | Patterns & Regex | Practical Vim — Ch. 12: “Matching Patterns and Literals” |
Project 1: The “No-HJKL” Navigator
- File:
navigation_practice.txt(Create a large text file) - Difficulty: Level 1: Beginner
- Knowledge Area: Horizontal/Vertical Navigation
- Main Book: Practical Vim (Chapter 8)
Real World Outcome
You will navigate a 1000-line log file or code file without touching your mouse or the arrow keys, and without repeatedly mashing j or l. You will be able to look at a specific semicolon on the screen and teleport your cursor to it instantly.
Here’s what you’ll actually see in your terminal when you open a large file:
$ vim server_error.log
Initial screen (cursor at top):
█2025-01-15 10:23:45 ERROR: Database connection failed
2025-01-15 10:23:46 INFO: Retrying connection...
2025-01-15 10:23:47 ERROR: Timeout after 30s
...
[Line 1/1247]
Scenario: You need to jump to the word “Timeout” visible on line 3.
Without Vim motions, you’d press: j j w w w w w w (8+ keystrokes)
With Vim motions, you type: /Timeout Enter (2 keystrokes)
After /Timeout + Enter:
2025-01-15 10:23:45 ERROR: Database connection failed
2025-01-15 10:23:46 INFO: Retrying connection...
2025-01-15 10:23:47 ERROR: █imeout after 30s
...
[Line 3/1247]
Scenario: Now you want to jump to the semicolon after “30s” on the same line.
You can see it 8 characters to the right. Instead of pressing l 8 times, you type: f;
After f;:
2025-01-15 10:23:47 ERROR: Timeout after 30s█
Scenario: There are 4 more semicolons visible on screen. You want to jump to the 3rd one.
You type: 3; (repeat the last f search 3 times)
Cursor teleports across multiple lines instantly.
Using screen-relative motions - Before M: (viewing lines 100-150 of the file)
█00: function initialize() {
101: setupDatabase();
102: loadConfig();
...
125: startServer(); ← Middle of visible screen
...
148: return true;
149: }
150: // End of init
After typing M:
100: function initialize() {
101: setupDatabase();
102: loadConfig();
...
█25: startServer(); ← Cursor teleported here
...
148: return true;
149: }
150: // End of init
The power: Your eyes see a target, your brain thinks the motion, your cursor teleports. No holding down keys. No mouse reaching. Pure thought-to-action.
The Core Question You’re Answering
“How do I move my cursor at the speed of my eyes?” Most people look at a target, then hold an arrow key until the cursor arrives. This disconnect breaks flow. You want to think “Go to ‘function’” and have the cursor arrive there.
Concepts You Must Understand First
Stop and research these before coding:
- Line Search (
f/t): How to jump to a character on the current line. - Word Motion (
w/b/e): The difference between a “word” and a “WORD” (whitespace delimited). - Screen Motion (
H/M/L): Moving relative to the visible screen (High, Middle, Low). - The Search Command (
/): Using search as a motion operator.
Questions to Guide Your Design
Before practicing, think through these:
- Precision: If I want to delete from here to the next comma, how do I do it? (
df,ordt,) - Efficiency: If I need to go down 50 lines, is
50jthe best way? Or is50Gor/targetbetter? - Repetition: If I used
f;to jump to a semicolon, how do I go to the next one without typingf;again? (Hint:;)
Thinking Exercise
The “Gaze” Test Open a file. Look at a random word.
- Don’t touch the keyboard.
- Formulate the exact keystrokes to get there in your head.
- Example target: The word “Return” inside a function 10 lines down.
- Plan:
/Return-> Enter. - Now execute.
The Interview Questions They’ll Ask
- “What is the difference between
fandt?” - “How do you center the current line on the screen?” (
zz) - “How do you move to the matching closing brace?” (
%)
Hints in Layers
Hint 1: Line Jumps
To delete everything from cursor to the next period: dt. (delete til period).
Hint 2: Screen Jumps
Use H to go to the top of the window, M to middle, L to bottom. Combine with relative numbers.
Hint 3: Search as Motion
d/foo deletes from cursor until the next occurrence of “foo”.
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Motions | “Practical Vim” | Ch. 8 | | Basic Movement | “Learning the vi and Vim Editors” | Ch. 3 |
Project 2: The JSON Surgeon (Text Objects)
- File:
messy_config.json - Difficulty: Level 2: Intermediate
- Knowledge Area: Text Objects / Selection
- Main Book: Practical Vim (Visual Mode / Text Objects)
Real World Outcome
You will take a minified or messy JSON/Config file and restructure it. You will change values, delete keys, and copy entire objects without ever manually highlighting text (clicking and dragging). You will edit purely by structure.
Here’s what you’ll see when editing a JSON configuration file. You open config.json:
BEFORE:
{"database":{"host":"old-server.com","port":5432,"credentials":{"username":"admin","password":"temp123"}},"cache":{"enabled":true}}
The cursor is somewhere in the middle of this mess. You want to:
- Change the password value
- Delete the entire credentials object
- Change “enabled” from true to false
Task 1: Change the password - Cursor is anywhere inside "temp123". You type: ci"
After ci" (change inner quote):
{"database":{"host":"old-server.com","port":5432,"credentials":{"username":"admin","password":"█"}},"cache":{"enabled":true}}
Vim deleted temp123 and put you in insert mode inside the quotes. You type the new password secure_pass_2024 and press Esc:
{"database":{"host":"old-server.com","port":5432,"credentials":{"username":"admin","password":"secure_pass_2024█"}},"cache":{"enabled":true}}
Task 2: Delete the entire credentials object - You navigate your cursor anywhere inside the credentials block. You type: da{ (delete around braces)
After da{:
{"database":{"host":"old-server.com","port":5432,"credentials":█},"cache":{"enabled":true}}
Wait, that kept the word “credentials”:. Let’s include it. Put cursor on the “c” of “credentials” and type: df} (delete forward to })
Result:
{"database":{"host":"old-server.com","port":5432█},"cache":{"enabled":true}}
Task 3: Change true to false - Navigate to “true” (you can use /true then Enter). Cursor on the “t”. You type: cw (change word), then type false, then Esc:
After cwfalseEsc:
{"database":{"host":"old-server.com","port":5432},"cache":{"enabled":false█}}
The magic: You never touched your mouse. You never manually selected text by dragging. You told Vim “change the thing inside these quotes” (ci"), “delete the thing around these braces” (da{), “change this word” (cw). You edited by STRUCTURE, not by position.
The Core Question You’re Answering
“How do I edit the meaning of the code, not just the characters?” Code is hierarchical. You have blocks inside blocks. You want to operate on “the inner block” or “the outer block” regardless of how many lines it spans.
Concepts You Must Understand First
- Text Objects (
ivsa): Inner (content only) vs Around (content + surrounding delimiters). - Delimiters: How Vim understands
( ),{ },[ ]," ",' '. - The Change Operator (
c): Deleting text and immediately entering Insert Mode.
Questions to Guide Your Design
- Selection: How do I select everything inside these braces? (
vi{) - Replacement: How do I replace the string inside these quotes? (
ci") - Deletion: How do I delete this whole function block? (
da{)
Thinking Exercise
The “Inside/Around” Mental Model Imagine a gift box.
- Inner (
i): The gift inside the box. - Around (
a): The gift + the box itself. - If you
di{(delete inner brace), you keep the braces but empty the contents. - If you
da{(delete around brace), you remove the braces and the contents.
The Interview Questions They’ll Ask
- “What is the command to change the text inside the current paragraph?” (
cip) - “How do you copy a function including its braces?” (
ya{) - “Why use
ci"instead ofdi"theni?” (Because.will repeat the whole change action).
Hints in Layers
Hint 1: Changing Values
Cursor anywhere inside "value". Type ci" -> type new value -> Esc.
Hint 2: Deleting Blocks
Cursor anywhere inside a { ... } block. Type da{ to delete the whole object.
Hint 3: Hierarchy
If you are inside nested braces {{ }}. di{ works on the closest pair. 2di{ works on the next one out.
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Text Objects | “Practical Vim” | Ch. 8 (Tip 52 approx) | | Visual Mode | “Practical Vim” | Ch. 4 |
Project 3: The Code Refactor (Change Operator)
- File:
legacy_script.py - Difficulty: Level 2: Intermediate
- Knowledge Area: Operators / Dot Command
- Main Book: Practical Vim (Chapter 1 & 2)
Real World Outcome
You will rename a variable that appears 20 times in a file, but not every occurrence (so a global find/replace won’t work). You will do this by making the change once, and then hopping to specific spots and pressing . to repeat it instantly.
Here’s what you’ll see when refactoring code. You open calculate.py:
BEFORE:
def process_data(data):
temp = data.filter()
temp = temp.transform()
result = temp.validate()
temp = load_cache() # Different 'temp' - don't rename this one
cache_result = temp.get()
temp = result.finalize() # Back to original 'temp' - rename this
return temp
You want to rename the first set of temp variables to filtered_data, but NOT the one used for cache (line 5). A global find/replace would break the code.
Step 1: Position cursor on first temp (line 2) and press *
After *:
Vim highlights all occurrences of temp in the file and jumps to the next one:
def process_data(data):
temp = data.filter()
█emp = temp.transform() ← Cursor here, all 'temp' highlighted
result = temp.validate()
temp = load_cache()
cache_result = temp.get()
temp = result.finalize()
return temp
Step 2: Go back to first temp - Press N (previous match) or gg to go to top, then navigate to line 2. Put cursor on the “t” of “temp”.
Step 3: Make the change once - Type: cw filtered_data Esc
After cwfiltered_dataEsc:
def process_data(data):
filtered_data█ = data.filter()
temp = temp.transform()
result = temp.validate()
temp = load_cache()
cache_result = temp.get()
temp = result.finalize()
return temp
Step 4: Jump to next occurrence - Press n (next match)
After n:
def process_data(data):
filtered_data = data.filter()
█emp = temp.transform() ← Cursor here
result = temp.validate()
...
Step 5: Repeat the change - Press . (dot command)
After .:
def process_data(data):
filtered_data = data.filter()
filtered_data█ = temp.transform()
result = temp.validate()
...
It changed “temp” to “filtered_data” instantly! The dot command replayed your entire edit: “delete word, type ‘filtered_data’, exit insert mode.”
Step 6: Continue the pattern - Press n to jump to next occurrence (line 3), press . to rename it. Press n again (line 4), press . to rename it.
Step 7: Skip the cache temp - Press n to jump to line 5 temp = load_cache(). You DON’T want to rename this one. Just press n again to skip it. Press n again to skip line 6.
Step 8: Rename the last ones - Press n to get to line 8, press . to rename. Press n to get to line 9, press . to rename.
FINAL RESULT:
def process_data(data):
filtered_data = data.filter()
filtered_data = filtered_data.transform()
result = filtered_data.validate()
temp = load_cache() # Unchanged - correct!
cache_result = temp.get()
filtered_data = result.finalize()
return filtered_data█
The workflow: One change (cwfiltered_dataEsc) + Navigation (n) + Repeat (.) = Selective refactoring at superhuman speed. You made 1 manual edit and pressed . 5 times. No copy-paste, no retyping, no manual selection.
The Core Question You’re Answering
“How do I make repetitive editing tasks feel like magic?” The Dot Command is Vim’s “Do it again” button. It captures your last edit (from entering Insert mode to leaving it).
Concepts You Must Understand First
- The Dot Formula: One Motion + One Change.
- Repeatable Changes: Why
dwis better thanxxx. - Search Navigation: Using
*to find the word under the cursor.
Questions to Guide Your Design
- Optimization: I want to change “foo” to “bar”.
- Bad way:
llxxxibarEsc. (The.will only repeat the last typed char). - Good way:
cwbarEsc. (The.will repeat “Change word to bar”).
- Bad way:
- Navigation: How do I find the next “foo”? (
*orn).
Thinking Exercise
The Repeat Trace Write a sentence: “The cat sat on the cat.”
- Cursor on first “cat”.
cw“dog”Esc. (Sentence: “The dog sat on the cat.”)- Move to next “cat” (
wor*). - Press
.. - Result: “The dog sat on the dog.”
- Notice how
.replayed “Delete word + Insert ‘dog’ + Escape”.
The Interview Questions They’ll Ask
- “What exactly does the dot command repeat?”
- “Why is
A(append at end of line) more repeatable than$a?” - “How do you search for the word under the cursor?” (
*)
Hints in Layers
Hint 1: The Setup
Place cursor on start of variable. cw -> type new name -> Esc.
Hint 2: The Loop
Press n to go to next match.
Press . to repeat change.
Press n again.
If you want to skip one, just press n again without pressing ..
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | The Dot Command | “Practical Vim” | Ch. 1 & 2 | | Pattern Search | “Practical Vim” | Ch. 12 |
Project 4: The Markdown Re-Sequencer (Line Operations)
- File:
todo_list.md - Difficulty: Level 1: Beginner
- Knowledge Area: Visual Block / Line Motions
- Main Book: Practical Vim (Chapter 4)
Real World Outcome
You will turn a messy dump of text into a structured TODO list. You will move paragraphs around, add checkboxes to 50 lines at once, and indent blocks of code—all without tedious copy-pasting or line-by-line typing.
Here’s exactly what you’ll see in your Vim editor. Start with this plain text in todo_list.md:
Buy groceries
Call dentist
Fix bug in login form
Review PR #234
Update documentation
Step 1: Add checkboxes using Visual Block mode
Place cursor at the start of line 1 (on “B” of “Buy”). Press Ctrl+v to enter Visual Block mode. Press 4j to select down 4 lines. You’ll see a vertical column selection:
█Buy groceries
│Call dentist
│Fix bug in login form
│Review PR #234
│Update documentation
Now press I (Shift+i) to insert at the start of all lines. Type - [ ] (dash, space, open bracket, space, close bracket, space). Press Esc. Wait a moment and watch the magic—all lines update simultaneously:
- [ ] Buy groceries
- [ ] Call dentist
- [ ] Fix bug in login form
- [ ] Review PR #234
- [ ] Update documentation
Step 2: Move lines around
Need to prioritize “Fix bug in login form”? Place cursor on that line. Press dd to cut it. Move cursor to line 1. Press P (uppercase p) to paste above. Your screen now shows:
- [ ] Fix bug in login form
- [ ] Buy groceries
- [ ] Call dentist
- [ ] Review PR #234
- [ ] Update documentation
Step 3: Indent a section
Want to indent the last two items as sub-tasks? Place cursor on “Review PR #234”. Press V (uppercase v) for Visual Line mode. Press j to select both lines:
- [ ] Fix bug in login form
- [ ] Buy groceries
- [ ] Call dentist
█- [ ] Review PR #234
█- [ ] Update documentation
Press > to indent. Result:
- [ ] Fix bug in login form
- [ ] Buy groceries
- [ ] Call dentist
- [ ] Review PR #234
- [ ] Update documentation
You just restructured a 5-line TODO list in under 15 keystrokes, no mouse required.
The Core Question You’re Answering
“How do I manipulate rectangular blocks of text and whole lines?” Standard editors select in streams (lines wrapping). Vim can select columns (Visual Block).
Concepts You Must Understand First
- Line Operations:
dd(delete line),yy(copy line),p(paste line). - Visual Block Mode:
Ctrl+v. How to select a vertical column of text. - Range Operations: How to apply a command to multiple lines.
Questions to Guide Your Design
- Column Editing: How do I add “- [ ] “ to the start of 10 lines simultaneously?
- Reordering: How do I swap this paragraph with the one above it? (
ddthenkthenp). - Indentation: How do I indent these 5 lines? (
V(visual line) ->5j->>).
Thinking Exercise
The Column Mental Model Imagine your text file is a grid (like Excel).
- Switch to Visual Block Mode (
Ctrl+v). - Move down. You are now selecting a column, not lines.
- Press
I(Shift+i). You are now inserting on all selected lines. - Type text.
- Press Esc. Watch it propagate to all lines.
The Interview Questions They’ll Ask
- “How do you paste a line before the current line?” (
P) - “How do you insert text on multiple lines at once?” (Visual Block
I) - “How do you join two lines together?” (
J)
Hints in Layers
Hint 1: Moving Lines
dd cuts the line. Move cursor. p pastes it below. P pastes it above.
Hint 2: Visual Block Insert
Ctrl+v -> Select column -> I (Shift+i) -> Type text -> Esc. Wait a second for it to apply.
Hint 3: Paragraph Jumps
Use { and } to jump over blocks of text to move faster.
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Visual Mode | “Practical Vim” | Ch. 4 | | Copy and Paste | “Practical Vim” | Ch. 10 |
Project 5: The Log Parser (Macros)
- File:
server_log.txt(Messy data) - Difficulty: Level 3: Advanced
- Knowledge Area: Macros / Registers
- Main Book: Practical Vim (Chapter 11)
Real World Outcome
You will clean up a CSV file where the columns are misaligned, dates are wrong, and quotes are missing. You will record your actions on the first line, and then tell Vim to “do that 1000 times,” cleaning the entire file in milliseconds.
Here’s exactly what you’ll see. Open server_log.txt with messy CSV data:
John,Smith,john@email.com,2024-01-15
Sarah,Jones,sarah@email.com,2024-02-20
Michael,Brown,michael@email.com,2024-03-10
But wait—the data has issues. Some names need quotes, emails are inconsistent. Let’s say you need to wrap each name field in quotes and add a column number at the start.
Target transformation: Each line should become 1,"John","Smith",john@email.com,2024-01-15
Step 1: Position cursor and start recording
Place cursor on line 1, column 1 (the “J” in “John”). Press qa to start recording a macro into register a. You’ll see recording @a in the status line at the bottom of Vim.
Step 2: Record the transformation on one line Now perform these keystrokes (Vim will remember them):
0 " Go to start of line (cursor: █John,Smith...)
i1,"<Esc> " Insert '1,"' at start (now: 1,"█John,Smith...)
f, " Find first comma (cursor: 1,"John█,Smith...)
i"<Esc> " Insert quote before comma (now: 1,"John"█,Smith...)
l " Move right one char (cursor: 1,"John",█Smith...)
i"<Esc> " Insert quote (now: 1,"John","█Smith...)
f, " Find next comma (cursor: 1,"John","Smith█,john@...)
i"<Esc> " Insert quote (now: 1,"John","Smith"█,john@...)
j0 " Down to next line, go to start
q " Stop recording
Step 3: Watch it in action
Your first line now looks like: 1,"John","Smith",john@email.com,2024-01-15
The cursor is on line 2. Now press @a to replay the macro once. Line 2 transforms instantly:
1,"John","Smith",john@email.com,2024-01-15
2,"Sarah","Jones",sarah@email.com,2024-02-20
Michael,Brown,michael@email.com,2024-03-10
Step 4: Apply to remaining lines
You have 100 more lines? No problem. Press 100@a and watch Vim blaze through the file. In under a second, you’ll see in your status bar something like:
102 lines processed
Your CSV file in Vim now shows 100 perfectly formatted rows, all transformed by the exact same sequence of keystrokes you recorded once.
Alternative: Apply to all remaining lines
Or use Visual mode: Place cursor on line 3. Press V (Visual Line), then G to select to end of file. Type :normal @a and press Enter. Every selected line runs your macro. Done.
The Core Question You’re Answering
“How do I automate complex edits without learning a scripting language?” Macros allow you to record a sequence of keystrokes into a register (variable) and replay them.
Concepts You Must Understand First
- Registers: Storage slots in Vim (
"a,"b, …). - Recording:
qa(Record to register ‘a’). - Playback:
@a(Play register ‘a’). - Atomic Motions: Using motions that work regardless of line content (e.g.,
0instead of<<<<).
Questions to Guide Your Design
- Repeatability: Does your macro work if the name “John” is 4 letters and “Christopher” is 11? (Use
f,instead oflllll). - Reset: Does your macro end by moving to the start of the next line? (Crucial for loops).
- Counters: Can you use a macro to number lines? (Using Ctrl+A to increment numbers).
Thinking Exercise
The Robot Script Write down your steps for one line:
- Go to start of line (
0). - Find first comma (
f,). - Delete char (
x). - Go down one line (
j). Now record exactly that.
The Interview Questions They’ll Ask
- “How do you edit an existing macro?” (Paste register, edit, yank back).
- “How do you run a macro on all selected lines?” (Visual selection ->
:normal @a). - “What happens if a macro fails (beep) in the middle of a loop?” (It stops).
Hints in Layers
Hint 1: Start Clean
Always start recording from a known position (usually start of line 0).
Hint 2: The End Move
Ensure your macro ends by positioning the cursor ready for the next run (e.g., j0).
Hint 3: Recursive Macros You can record a macro that calls itself (dangerous but powerful).
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Macros | “Practical Vim” | Ch. 11 | | Registers | “Practical Vim” | Ch. 10 |
Project 6: The HTML Tag Wrapper (Surround Simulation)
- File:
index.html - Difficulty: Level 3: Advanced
- Knowledge Area: Marks / Registers / Insert-Normal
- Main Book: Practical Vim
Real World Outcome
You will take a plain text list and wrap items in <li> tags, wrap the list in <ul>, and modify attributes. You will do this manually to understand how plugins like vim-surround work under the hood.
Here’s exactly what you’ll see in your Vim editor. Start with this plain text list in index.html:
Apples
Oranges
Bananas
Grapes
Your goal: Convert this into a proper HTML unordered list with <li> tags around each item, and a <ul> wrapper around everything.
Step 1: Wrap first item in <li> tags using marks
Place cursor on “Apples”. Press ma to set mark a at the start of the word. Your screen looks like:
█Apples
Oranges
Bananas
Grapes
Now move to the end of “Apples” by pressing e (cursor is now on the “s”). Press yiw to yank (copy) the inner word into the default register. Now type I<li> (capital I to insert at line start) and you’ll see:
<li>█Apples
Oranges
Bananas
Grapes
Press Esc to exit insert mode. Now append the closing tag: Press A</li> (capital A to append at line end):
<li>Apples</li>█
Oranges
Bananas
Grapes
Press Esc. First item done!
Step 2: Use a register to save and reuse the wrapper
Let’s be smarter for the remaining items. Place cursor on line 2 (“Oranges”). Press yiw to yank the word into the default register. Now press S (capital S to substitute entire line), and type:
<li><Ctrl+r>"</li>
When you press Ctrl+r then " (the double-quote key) while in insert mode, Vim pastes the contents of the default register (the word “Oranges”). Your line becomes:
<li>Apples</li>
<li>Oranges</li>█
Bananas
Grapes
Press Esc. Line 2 is done!
Step 3: Repeat for remaining items
For line 3, place cursor on “Bananas”. Press yiw, then S, then type <li><Ctrl+r>"</li>, press Esc. Repeat for “Grapes”. Your editor now shows:
<li>Apples</li>
<li>Oranges</li>
<li>Bananas</li>
<li>Grapes</li>█
Step 4: Add the <ul> wrapper using marks
Now wrap everything in <ul> tags. Place cursor on line 1. Press O (capital O to open line above) and type <ul>, press Esc. Your screen shows:
<ul>
<li>Apples</li>█
<li>Oranges</li>
<li>Bananas</li>
<li>Grapes</li>
Press G to jump to the last line, then press o (lowercase o to open line below) and type </ul>, press Esc. Final result:
<ul>
<li>Apples</li>
<li>Oranges</li>
<li>Bananas</li>
<li>Grapes</li>
</ul>█
Step 5: Using marks to jump around
Let’s say you need to copy an attribute from line 1 and paste it on line 5. Place cursor on line 1, press ma to set mark a. Navigate to line 5 with 5G. Do your work. Now press `a (backtick + a) to jump back to the exact cursor position where mark a was set. Press 'a (single quote + a) to jump to the beginning of that line instead.
Using named registers
Want to save “Apples” for later use? Place cursor on the word and press "ayiw (yank inner word into register a). Navigate anywhere in the file. Press "ap to paste from register a. The word “Apples” appears at cursor position, no matter how much you’ve copied/pasted in between.
The Core Question You’re Answering
“How do I save my place in a file and move text between locations?” You will learn to use Marks (
ma) to drop an anchor, move away, and pull text back to the anchor.
Concepts You Must Understand First
- Marks:
m{char}to set,`{char}to jump. - Named Registers:
"aY(Yank line into register ‘a’). - Insert-Normal Mode:
Ctrl+oallows one normal command while in insert mode.
Questions to Guide Your Design
- Anchoring: How do I remember where “here” is so I can jump back after copying something? (
ma). - Transplanting: How do I move a line from the bottom of the file to here? (Delete to register, move, paste).
Thinking Exercise
The Teleporter
- Set mark
aat top (ma). - Go to bottom (
G). - Set mark
b(mb). - Jump to
a(`a). - Jump to
b(`b).
The Interview Questions They’ll Ask
- “What is the difference between
dand"ad?” (The second saves to register ‘a’). - “How do you jump to the exact location of a mark vs just the line?” (
`avs'a). - “How do you paste from register ‘a’?” (
"ap).
Hints in Layers
Hint 1: Wrapping Text
Yank the word. Type <div>. Press Ctrl+r then " (default register) to paste while in insert mode. Type </div>.
Hint 2: Marks
Use capital marks (mA) for file-global marks (jump between files).
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Marks | “Learning the vi and Vim Editors” | Ch. 3 (Marking Your Place) | | Registers | “Practical Vim” | Ch. 10 |
Project 7: The “Vim Golf” Challenge
- File:
golf_challenge.txt - Difficulty: Level 4: Expert
- Knowledge Area: Optimization
- Main Book: Online Resources / VimGolf
Real World Outcome
You will look at a text transformation and solve it in the fewest keystrokes possible. This turns editing into a game and forces you to learn obscure, high-efficiency commands.
Example Challenge: Convert “hello world” to “HELLO WORLD” and add exclamation marks.
Starting state:
hello world█
Naive Solution (16 keystrokes):
v$ " Visual select to end of line (cursor: hello world█)
y " Yank
gU " Uppercase (now: HELLO WORLD█)
A " Append at end
! " Type exclamation
Esc " Back to normal
Score: 16 keystrokes
Vim Golf Solution (4 keystrokes):
gU$ " Uppercase to end of line (now: HELLO WORLD█)
A! " Append exclamation and enter insert mode
Esc " Back to normal (now: HELLO WORLD!█)
Score: 4 keystrokes
You’ll practice on VimGolf.com where you submit your solution and see how you rank against others globally. The site shows you the winning solution and you’ll discover commands like ~ (toggle case), Ctrl+a (increment number), and xp (swap characters) that you never knew existed.
The Core Question You’re Answering
“Is there a faster way?” This project trains your lateral thinking. Instead of “Delete line, type new line”, you might find “Increment number” (
Ctrl+a) or “Swap case” (~).
Concepts You Must Understand First
- Obscure Operators:
~(case swap),gu(lowercase),gU(uppercase). - Increment/Decrement:
Ctrl+a,Ctrl+x. - Compound Commands:
xp(transpose characters),ddp(swap lines).
Questions to Guide Your Design
- Math: Can I turn “10px” into “12px” without typing? (
2Ctrl+a). - Case: Can I turn “HELLO” to “hello” without retyping? (
gUaw). - Swaps: “teh” -> “the”. How? (
xp).
Thinking Exercise
The Keystroke Audit
Record yourself editing. Count the keys.
“I pressed l 5 times.” -> Score: 5.
“I pressed f,.” -> Score: 2.
Winner: f,.
The Interview Questions They’ll Ask
- “How do you swap two characters?” (
xp). - “How do you increment the first number on the line?” (
Ctrl+a). - “How do you toggle case?” (
~).
Hints in Layers
Hint 1: Don’t Enter Insert Mode
Vim Golf challenges are often solved without ever hitting i.
Hint 2: Visual Selection Counts
Avoid visual selection if a direct operator (d3w) works. v adds a keystroke.
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Efficiency | “Practical Vim” | Ch. 1 (The Vim Way) |
Project 8: The Config Builder (Vanilla Vimrc)
- File:
.vimrc - Difficulty: Level 3: Advanced
- Knowledge Area: Vim Configuration / Vimscript
- Main Book: Modern Vim / Learning vi and Vim
Real World Outcome
You will create a configuration file that makes Vim behave exactly how you want. You will enable line numbers, relative numbering, syntax highlighting, and custom key maps. You will own your tools.
BEFORE (Stock Vim with no .vimrc): When you open a Python file, you see this bare-bones view:
def calculate_total(items):
total = 0
for item in items:
total += item.price
return total
█
- No line numbers
- No syntax highlighting (all text is one color)
- Need to hit
Esckey (far from home row) - When you search with
/total, the matches stay highlighted forever
AFTER (With your custom ~/.vimrc):
" Your .vimrc configuration
set number " Show absolute line numbers
set relativenumber " Show relative numbers (makes 5j easier)
syntax on " Enable syntax highlighting
set hlsearch " Highlight search results
set incsearch " Show matches as you type
set ignorecase " Case insensitive search
set smartcase " Case sensitive if uppercase present
inoremap jj <Esc> " Map jj to Escape in insert mode
nnoremap <leader>h :nohl<CR> " Clear search highlighting with Space+h
let mapleader = " " " Set spacebar as leader key
Now when you open the same file you see:
4│def calculate_total(items):
3│ total = 0
2│ for item in items:
1│ total += item.price
1│ return total
2│█
- Line numbers appear (absolute on left: 345, 346, 347…)
- Relative numbers show distance (1, 2, 3 above and below cursor)
- Python keywords like
def,for,returnare colored differently - When in insert mode, you can type
jjto escape (no reaching for Esc) - After searching, you press
Space+hto clear highlighting
You’ll test your config by opening code files and verifying each setting works as expected.
The Core Question You’re Answering
“How do I make the editor work for me, instead of me working for the editor?”
Concepts You Must Understand First
- Set Options:
set number,set hlsearch. - Mappings:
map,nmap(normal mode map),imap(insert mode map). - The Leader Key: Creating your own namespace for commands.
Questions to Guide Your Design
- Relative Numbers: Why do many Vimmers use
set relativenumber? (It makes10jeasier to calculate). - Escaping:
Escis far away. Can you mapjjtoEsc? - Search: How do I stop the highlighting after a search? (
:nohl).
Thinking Exercise
The Pain Audit
What annoys you?
“I hate reaching for Ctrl+w to switch windows.”
Fix it: nnoremap <C-h> <C-w>h.
The Interview Questions They’ll Ask
- “What is the difference between
mapandnoremap?” (Recursive vs Non-recursive. Always usenoremapunless you know why not). - “What is the Leader key?”
- “How do you reload .vimrc without restarting?” (
:source %).
Hints in Layers
Hint 1: The Leader
let mapleader = " " (Space bar). Now you can map Space + w to save file.
Hint 2: Comments
In .vimrc, comments start with ".
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Configuration | “Modern Vim” | Tips 26-28 | | Settings | “Learning the vi and Vim Editors” | Appendix B |
Project 9: The Code Navigator (CTags & Jumps)
- File: Large Source Code Repository (e.g., Redis source)
- Difficulty: Level 3: Advanced
- Knowledge Area: Jump List / Tags
- Main Book: Practical Vim (Chapter 16)
Real World Outcome
You will explore a massive unfamiliar codebase. You will jump from a function call to its definition in another file, and jump back, without a file explorer.
The Experience: You’ve cloned the Redis source code (thousands of files). You open src/server.c and see:
/* src/server.c - line 5234 */
int main(int argc, char **argv) {
initServerConfig();
loadServerConfig(config_file);
initServer();
█
You wonder “What does initServerConfig() actually do?” Place your cursor on the function name and press Ctrl+]:
JUMP! Vim instantly opens src/config.c at line 892:
/* src/config.c - line 892 */
void initServerConfig(void) {
server.port = CONFIG_DEFAULT_SERVER_PORT;
server.tcp_backlog = CONFIG_DEFAULT_TCP_BACKLOG;
█
You explore a bit, then see it calls createSharedObjects(). Cursor on that name, press Ctrl+] again:
JUMP! Now you’re in src/object.c at line 445:
/* src/object.c - line 445 */
void createSharedObjects(void) {
shared.crlf = createObject(OBJ_STRING, sdsnew("\r\n"));
█
You’ve gone 3 levels deep (main -> initServerConfig -> createSharedObjects). To get back, you press Ctrl+o (jump back):
JUMP BACK! You’re back in src/config.c at line 892.
Press Ctrl+o again:
JUMP BACK! You’re back in src/server.c at line 5234 in main().
Your jump list looks like this (visible with :jumps):
jump line col file/text
3 5234 4 src/server.c
2 892 10 src/config.c
1 445 8 src/object.c
>
You’ve navigated a complex codebase without opening a single file browser or typing a file path. The tags file (generated by ctags -R .) maps every function, variable, and struct definition, letting you traverse code like a graph.
The Core Question You’re Answering
“How do I build a mental map of code structure?”
Concepts You Must Understand First
- The Jump List: Vim remembers where you’ve been.
Ctrl+o(back),Ctrl+i(forward). - Tags: Using
ctagsto index code. - Definition Jumps:
Ctrl+](jump to tag/definition).
Questions to Guide Your Design
- Traversal: I’m inside
main(). It callsinit(). How do I seeinit()? - Return: I’m done with
init(). How do I go back tomain()? (Ctrl+torCtrl+o). - Local vs Global:
gdgoes to local declaration.
Thinking Exercise
The Breadcrumb Trail
Navigate 5 levels deep into function calls.
Now try to get back to the start pressing only one key combination repeatedly. (Ctrl+o).
The Interview Questions They’ll Ask
- “How do you jump to the definition of the word under cursor?”
- “What is the difference between
Ctrl+oandu?” (Jump list vs Undo list). - “How do you generate a tags file?” (
ctags -R .).
Hints in Layers
Hint 1: Ctags
You need to install ctags on your system for Ctrl+] to work efficiently in C/C++ projects.
Hint 2: The Change List
g; goes to the last place you made a change.
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Ctags | “Practical Vim” | Ch. 16 | | Jumps | “Practical Vim” | Ch. 9 |
Project 10: The Search-Replace Master (Substitutions)
- File:
database_dump.sql - Difficulty: Level 4: Expert
- Knowledge Area: Ex Commands / Regex
- Main Book: Practical Vim (Chapter 14)
Real World Outcome
You will take a text file of names “Last, First” and convert them to SQL INSERT INTO users (name) VALUES ('First Last'); using a single complex command.
BEFORE - You have a file names.txt with this content:
Doe, John
Smith, Jane
Johnson, Bob
Williams, Alice
You need to convert this to SQL INSERT statements with the names in “First Last” format.
The Command: You type this one Ex command in Vim:
:%s/\v(\w+), (\w+)/INSERT INTO users (name) VALUES ('\2 \1');/g
Breaking it down:
%= apply to all lines in the files= substitute command/\v= “very magic” mode (less escaping needed)(\w+)= capture group 1: one or more word characters (the last name),= match the literal comma and space(\w+)= capture group 2: one or more word characters (the first name)/= separator between find and replaceINSERT INTO users (name) VALUES ('= literal text\2 \1= reference to captured groups (first name, then last name)');= literal text to close the SQL statement/g= global flag (all occurrences on each line)
You press Enter.
AFTER - The file instantly transforms:
INSERT INTO users (name) VALUES ('John Doe');
INSERT INTO users (name) VALUES ('Jane Smith');
INSERT INTO users (name) VALUES ('Bob Johnson');
INSERT INTO users (name) VALUES ('Alice Williams');
All four lines transformed in milliseconds. You see the confirmation at the bottom of Vim:
4 substitutions on 4 lines
Advanced variation: If you only wanted to transform lines containing “Smith”, you’d use:
:g/Smith/s/\v(\w+), (\w+)/INSERT INTO users (name) VALUES ('\2 \1');/
This combines the :g (global) command with :s (substitute) - only lines matching “Smith” get transformed.
The Core Question You’re Answering
“How do I perform programmatic text processing?” Vim’s command line (
:) exposes the power ofsedand Regex.
Concepts You Must Understand First
- The Substitute Command:
:%s/find/replace/g. - Capture Groups:
\( ... \)and\1,\2. - Ranges:
:1,10s/...(Lines 1-10 only).
Questions to Guide Your Design
- Pattern Matching: How do I match “Word, Word”? (
\w\+, \w\+). - Swapping: How do I swap them? (
\2 \1). - Context: How do I execute this only on lines containing “User”? (
:g/User/s/...).
Thinking Exercise
Regex Construction Don’t write the whole command at once.
- Search first:
/pattern. Check matches. - Start substitute:
:%s//(uses last search).
The Interview Questions They’ll Ask
- “What does
%mean in:%s?” (The whole file range). - “How do you make search case-insensitive?” (
\c). - “How do you reuse the matched pattern in the replacement?” (
&).
Hints in Layers
Hint 1: Very Magic
Use \v at the start of regex to avoid escaping parentheses. /\v(group) instead of /\(group\).
Hint 2: Confirmation
Add c at the end (:%s/.../.../gc) to confirm each replacement.
Books That Will Help
| Topic | Book | Chapter | | :— | :— | :— | | Substitution | “Practical Vim” | Ch. 14 | | Global Commands | “Practical Vim” | Ch. 15 |
Project Comparison Table
| Project | Difficulty | Time | Depth of Understanding | Fun Factor |
|---|---|---|---|---|
| 1. No-HJKL Navigator | Beginner | Weekend | ⭐⭐⭐ | ⭐⭐ |
| 2. JSON Surgeon | Intermediate | Weekend | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 3. Code Refactor | Intermediate | Weekend | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 4. Markdown Sequencer | Beginner | Weekend | ⭐⭐ | ⭐⭐⭐ |
| 5. Log Parser | Advanced | 1 Week | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 6. HTML Wrapper | Advanced | Weekend | ⭐⭐⭐ | ⭐⭐ |
| 7. Vim Golf | Expert | Ongoing | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 8. Config Builder | Intermediate | Weekend | ⭐⭐⭐ | ⭐⭐⭐ |
| 9. Code Navigator | Advanced | 1 Week | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| 10. Search-Replace | Expert | Weekend | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |