itchmyback
itchmyback

Reputation: 549

Match pattern and then replace using the pattern

I would like to find a good way in Vim to do this search and replace for the following -

name_1
end
name_2
end

name_3
end

where I take name_"x" and append it to it's following "end" statement. So it would look like this -

name_1
end_name_1
name_2
end_name_2
name_3

end_name_3

Note - the empty line before name_3 ruins a constant line spacing between "name_x" and "end". So I was thinking of something like

:g/\(name_x\)/,/end/ s/end/end_\1/

Now that doesn't work trying to store the first pattern like that but hopefully you get the idea. Any tricks here?

Thanks

Upvotes: 0

Views: 327

Answers (2)

Alan Gómez
Alan Gómez

Reputation: 378

A regex based solution is:

:%s/\(name.*\)\(\n\+.*\)/\1\2_\1

This group every line which contains name followed by anything, another group which could have any number of return followed bay anything, then append first group to the second one.

Upvotes: 0

Peter Rincker
Peter Rincker

Reputation: 45177

The issue you are having is that \1 refers to the current substitution's match group not the :g command's pattern. Sadly you can not access previous match groups from previous searches. There are however ways to accomplish your goal. The first I would suggest is a macro.

First off start on a a non matching line. This can be done by inserting a blank line at the top of the file if needed (just remove it afterwards).

  1. start the macro search for your pattern: /name_\d\+
  2. yank until the end of your pattern: y//e
  3. find the end pattern: /end/e
  4. insert a underscore a_
  5. paste with p
  6. end your macro q
  7. execute the macro with a large number. e.g. 999@q

Here is the macro I used:

:let @q = "/name_\\d\\+\<cr>y//e\<cr>/end/e\<cr>a_\<esc>p"

The other way is to use the :global command in a similar fashion to your attempt.

:g/name_\d\+/let @@=matchstr(getline('.'),'name_\d\+')|/end/s//\=submatch(0).'_'.@@/

I personally this is much too complicated, but it is nice to have options. The command can be broken down into the following parts:

  • :g/name_\d\+ execute a command on each line matching name_ and some number
  • getline('.') will return a string of the current line
  • matchstr(getline('.'), 'name_\d\+') will return only the matching part of the current line
  • let @@ = matchstr(...) will set the matched portion to the default register
  • /end/s/ will execute a :s on the following line that matches end
  • s//\=.../ matches the last used pattern and replaces it with an express.
  • submatch(0).'_'.@@ create a string with the whole matched pattern followed by an underscore and the contents of the unnamed register.

For more information on the following topics see:

:h q
:h search-offset
:h :g
:h :let-@
:h registers
:h matchstr(
:h getline(
:h range
:h submatch(

Upvotes: 3

Related Questions