← back to latest

Replace matches one at a time and skip the ones you want to keep

NORMALcgnReplace matches one at a time and skip the ones you want to keep

anatomy

c
Change. Deletes whatever the following motion or object covers and drops you into insert mode.
gn
Search the forward direction for the last used pattern and treat the match as a text object. If the cursor is already on a match, that match is the target. The search wraps around the end of the file when 'wrapscan' is on, which is the default.

In the buffer

Before:

total = price * qty
^
tax = total * 0.08
print("total:", total)
return total + tax

After typing *N to load the word as the search pattern and come back, then cgn, subtotal, <Esc>, and finally .nn..:

subtotal = price * qty
tax = subtotal * 0.08
print("total:", subtotal)
return subtotal + tax
              ^

The first cgn changes the match under the cursor. Each . finds the next match and changes it. The nn steps onto the label inside the string and then past it, so that one stays total.

When you would reach for it

You are renaming something that appears a dozen times, but not every occurrence should change. One is inside a string, one is in a comment that refers to the old name on purpose, and one belongs to a different variable that happens to share the word. :%s///gc works, but it takes over the screen and asks y/n for every match, including the ones you already know about. cgn lets you make the change once, then work down the file with . to accept and n to move on, looking at the real code around each match while you go.

Gotchas

  • n does not skip a match in one press. gn targets the match under the cursor if there is one, so after one n the cursor sits on the match you wanted to keep and the next . changes it. Press n a second time to move past it, then ..
  • If the new text contains the pattern, . can land on text you typed a moment ago. With a pattern of total (no word boundaries), replacing total with subtotal and pressing . turns it into subsubtotal. Search with * or write /\<total\> so the pattern only matches the whole word.
  • * jumps forward before you change anything, so *cgn changes the second occurrence and leaves the one you started on for last, reached only after the search wraps. Type *N to return to where you started first.
  • The pattern comes from the last search, not from what you changed. Running any other search between presses of . changes the target, and . then changes matches for the new pattern.

Variants

dgn

Deletes the next match instead of changing it. Set the pattern to , total and dgn removes the trailing argument from the print call. . repeats the delete.

gn

Without an operator, gn visually selects the next match. Useful for checking what cgn is about to touch before you commit to it.

cgN

The same change, searching backward. Handy when the cursor is below the matches you care about.

lineage

gn and gN arrived in Vim 7.4 in 2013, contributed by Christian Brabandt. The feature is small, but it closed an old gap: search and change had always been separate steps, so the dot command could repeat the change but never the search. Neovim inherited the feature when it forked from the 7.4 codebase.

-- INSERT --vim-hacks.com019-019-change-the-next-match-and-repeat.html
utf-81,1All