Learn Practice Daily golf Reference
The Vim Way·Lesson 4 of 7·8 min·0/8 exercises

Find and replace by hand

* lines up every copy of the word under the cursor; fix one, then n . the rest, or let cgn bring each match to you.

Scene

4:05 p.m. The photo of the specials board has forty likes and one comment: "tortila?" It's misspelled nine times across the menu files. Two of those sit inside quoted five-star reviews, and marketing wants those word for word, typo and all.

So no blind find-and-replace. You want to visit each one, decide, and fix it in two keys.

From memory

Warm-up from earlier chapters

Two from earlier chapters, from memory, before the new idea. Two wrong tries show the answer.

Throw the whole line away (keep the indent) and retype it.
press the keys…
1 of 2
Throw the whole line away (keep the indent) and retype it.

More like this

Put the cursor on a word and press *: Vim reads it as more like this and jumps to the next copy of that word. n slides on to the copy after that, and at the end of the file it wraps round to the top. It's the third slide for your packing line, after j and ;.

*n
tortila.size > 0 && wrap(tortila, tortila.filling);
2 hops
* from the first tortila jumps to the next copy, and n to the one after.
1const tortila = warm(tortila);
1wrap(tortila);
2serve(tortila);
NORMALLoading the editor…1:7
Check yourself

Which copies count?

1count = 0;
1counter = 1;
2log(count);
NORMAL
Keys pressed: *

The cursor is on count. You press *. Where does it land?

Pick an answer (or press its number), then watch the engine prove it.

Change once, then slide and stamp

ciw changes the word under the cursor, from anywhere in it. Take it as a recipe for now: it reads change inner word, and the next track takes it apart. So: * to line up the copies, ciw and the right spelling to ink the stamp, then n ., n .. The dot formula, with n as the slide.

Before
const qty = item.qty;
return price * qty;
*ciwquantityEsc
const qty = item.quantity;
return price * qty;
n.
const qty = item.quantity;
return price * quantity;
n.
const quantity = item.quantity;
return price * quantity;
* jumps to the next qty, ciw renames it, and each n . renames the next one, wrapping back to the first.
Try it

Fix the spelling

One name is misspelled everywhere it appears. Get onto one copy, *, fix it with ciw, then n . until they all match.

1 of 2Fix the marked spots · 5 left
1export function happyHour(item: Item, hour: number) {
1 const bsae = item.price * item.qty;
2 const off = hour >= 17 && hour < 19 ? bsae * 0.2 : 0;
3 const tax = (bsae - off) * TAX_RATE;
4 const total = bsae - off + tax + item.tip;
5 log("happy hour", item.id, bsae, off, total);
6 return Math.round(total * 100) / 100;
7}
8
9export const LIMITS = { maxQty: 12, maxTip: 20, rushMinutes: 45 };
NORMALLoading the editor…1/20.0s1:1

This exercise needs a keyboard. Read on, and play it at your computer.

Let one go by

On the packing line you can let a bag slide past unstamped. Here that's one more n: n lands on the copy you want to keep, n again moves past it, and you stamp the next. You decide at every stop.

Try it

Keep the review

Fix every tortila except the one in the quoted review. The cursor starts on the first one.

Fix the marked spots · 2 left
1const sides = ["tortila chips", "flour tortilatortilla chips", "flour tortilla"];
1// review: "best tortila in town" (quote it exactly)
2const wrap = makeWrap("tortila");
NORMALLoading the editor…0.0s1:17

This exercise needs a keyboard. Read on, and play it at your computer.

The conveyor

cgn is a second recipe: it changes the match under the cursor, or the next one if you're not on a match, so the slide is built into the change. Ink it once, and from then on . alone brings the next copy to you and stamps it, like a conveyor belt. Want to keep one? n lands on it, n again slides past, then ..

1const tortila = warm(tortila);
1wrap(tortila);
2serve(tortila);
NORMALLoading the editor…1:7
Try it

Rename with the conveyor

The new menu service calls it quantity. The cursor is on qty: *, cgn once, then let . do the rest.

Fix the marked spots · 2 left
1function lineTotal(item: Item) {
2 const qty = item.qty ?? 1;
1 return item.price * qty;
2}
NORMALLoading the editor…0.0s2:9

This exercise needs a keyboard. Read on, and play it at your computer.

From memory

More like this, from memory

Read the situation, press the keys.

Search for the word under the cursor.
scroll here, then press the keys
1 of 4
Search for the word under the cursor.
Challenge

Spellcheck sprint

Three files, one misspelled name in each. Every copy of it should match the spelling the rest of the file expects.

Stuck? Show a hinton a copy: *, ciw and the right name, <Esc>, then n. for each copy (or cgn and .)
1 of 3Fix the marked spots · 6 left
1function averageRating(reivews: Review[]) {
1 if (reivews.length === 0) return 0;
2 const sum = reivews.reduce((acc, r) => acc + r.stars, 0);
3 const avg = sum / reivews.length;
4 return Math.round(avg * 10) / 10;
5}
6
7const best = reivews.filter((r) => r.stars === 5);
8console.log("five-star reivews:", best.length);
NORMALLoading the editor…1/30.0s1:1

This exercise needs a keyboard. Read on, and play it at your computer.

Boss round

Rename the special

sides.ts: seven copies of tortila. The five outside the two quoted reviews become tortilla; the reviews stay exactly as written. The cursor starts on the copy in chips. Pick your slide.

Stuck? Show a hint*, cgntortilla<Esc>, then . for each copy · n n lets a review go by (or ciw and n . to look at each first)
Fix the marked spots · 5 left
1// review: "the tortila was perfect" (quote it exactly)
1export const sides = {
2 chips: "tortila chips",
3 wrap: "flour tortila",
4 bowl: "no tortila",
5};
6// review: "best tortila in town" (quote it exactly)
7export function wrapIn(tortila: string) {
8 return "warm " + tortila;
9}
NORMALLoading the editor…0.0s1:1

This exercise needs a keyboard. Read on, and play it at your computer.

Extra practice optional

The chapter is done. These rounds keep going as long as you like: stop whenever it stops being useful.

Endless

Proofreader on call

Endless repairs, each wanting a different slide: a misspelled name (n .), a run of lost semicolons (j .), squeezed operators (; .). Look first, then pick the slide.

Level 1Change the red text to ·/·
1function averageRating(reviews: Review[]) {
1 if (reviews.length === 0) return 0;
2 const sum = reviews.reduce((acc, r) => acc + r.stars, 0);
3 const avg = sum/reviews.length;
4 return Math.round(avg * 10) / 10;
5}
6
7const best = reviews.filter((r) => r.stars === 5);
8console.log("five-star reviews:", best.length);
NORMALLoading the editor…lv 1 · 0 done0.0s1:1

This exercise needs a keyboard. Read on, and play it at your computer.