There is a problem with your search motion approach. If "foo" doesn't appear on a line or multiple consecutive lines, you will end up deleting entire lines up until the next instance of "foo"!
Instead of using a search motion, you could simply use a global :substitute
and leverage the zero-width atom \ze
:
:%s/^.\{-}\zefoo//
This will delete any and all text from the beginning of a line up to, but not including "foo", for every line in the buffer. The \{-}
atom is a non-greedy version of *
, meaning it will match as few times as possible, deleting only up to the first "foo" on the line (Thanks for catching this, wmmso).
Since you didn't specify in your question, if you want to match only on the whole word "foo" and not substrings, then you must wrap "foo" in \<
and \>
like so:
:%s/^.\{-}\ze\<foo\>//
How it works
\ze
is a zero-width atom that explicitly marks the end of the match. In other words, it overrides where the actual end of a match is, even if there is more matching text after the \ze
, like in the command above.
Here is how the help page describes it, from :h \ze
:
Matches at any position, and sets the end of the match there: The previous char is the last char of the whole match. Can be used multiple times, the last one encountered in a matching branch is used.
Example: "end\ze(if\|for)" matches the "end" in "endif" and "endfor".
This is a pretty good explanation, but I think an interactive, visual example speaks for itself. Let's look at this example buffer:
cats dogs birds foo fish
cats foo dogs birds fish
If you don't have it enabled already, :set hlsearch incsearch
. Now, begin a search like so: /^.\{-}foo
. Hitting Enter is unnecessary. The buffer should visually highlight the exact matched portion like so (assume the brackets denote what is highlighted):
[cats dogs birds foo] fish
cats foo dogs birds fish
As expected, this matches everything from the beginning of the line, stopping with "foo". But we don't want to include "foo" when we delete, so how do we keep "foo" out of the match, while still requiring its presence? \ze
to the rescue.
Assuming you still have the search open, modify it by inserting a \ze
between the *
and foo
, just like in the pattern in the top of this answer: /^.\{-}\zefoo
. You should see the highlighted portion shrink back to just before "foo":
[cats dogs birds ]foo fish
cats foo dogs birds fish
It is important to remember that "foo" is still an important part of this match in that it must be there for anything to match at all. If we replace "foo" with "bar" in the example buffer, the same pattern would return not return any matches:
cats dogs birds bar fish
cats bar dogs birds fish
Bonus Information
Just as an extra bit of info, \zs
is the counterpart to \ze
, and sets the start of the match. This can come in handy, for example, when what you'd like to match is preceded by whitespace that you don't want in the match. /\s*\zsfoo
would match only "foo", even if it had an arbitrary amount of whitespace before it.