vimtricks.wiki Concise Vim tricks, one at a time.

How do I use \zs and \ze to define the exact boundaries of a regex match in Vim?

Answer

\zs and \ze in a pattern

Explanation

Vim's \zs ("match start") and \ze ("match end") atoms let you narrow the actual match region within a broader pattern context. Everything before \zs must be present but is excluded from the match, and everything after \ze must be present but is also excluded. This makes them the Vim equivalent of look-around assertions, and they work in search patterns, :substitute, :global, and anywhere else a Vim regex is accepted.

How it works

  • \zs — sets the start of the match at this position; text before it is required context but not captured
  • \ze — sets the end of the match at this position; text after it is required context but not captured
  • Both atoms are zero-width: they consume no characters themselves
  • When omitted, the match spans the entire pattern as usual

Example

Replace every function name in calls like call foo(...) without touching the word call:

:%s/call \zs\w\+/renamed_fn/g

Before:

call foo(x)
call foo(y)

After:

call renamed_fn(x)
call renamed_fn(y)

Or highlight only the value inside double-quoted strings:

/"\zs[^"]*\ze"

This positions the cursor on the value, not the opening quote.

Tips

  • \zs and \ze can both appear in the same pattern; only the region between them is highlighted or substituted
  • Useful for renaming identifiers that share a prefix: /class \zsMyClass\ze/ matches only MyClass while requiring class before it
  • Combine with \< and \> word boundaries for precise whole-word matches with context
  • These are Vim-specific; POSIX grep and PCRE lookaheads ((?<=...) / (?=...)) use different syntax

Next

How do I jump to a tag in Vim and automatically choose if there is only one match?