2

I'm working with large xml files that have tags, and some are spread across multiple lines, and some are on a single line. I'd like them to all be on a single line. There are other tags in the files besides the one I want to modify.

<group value="abc">
   <line value="abc" value2="def"/>
   <line value="ghi"
      value2="jkl"/>
</group>

I would like it to look like:

<group value="abc">
   <line value="abc" value2="def"/>
   <line value="ghi" value2="jkl"/>
</group>

I've tried two approaches so far. The first is to try to apply a join to a range for the <line> tag. The second is to try to do a substitution across multiple lines to remove newlines and extra whitespace. Some examples that almost work.

:g/<line/,/\/>/join
:v/\/>/g/<line/,/\/>/join
:g/<line\_.{-}\/>/s/\n\s*/ /g
1

First off, I never knew that you can have a search in the range. Thanks for making me learn something new!

Second off, you were really close with :g/<line/,/\/>/join. But I'm pretty sure that the problem is that the second line i.e.

  <line value="abc" value2="def"/>

Is also matched by <line/,/\/> so this line is joined with the next when it shouldn't be. I guess the search can't match it's own line. This seems to be consistent behavior. For example, try putting this into your buffer:

abc
abc

And doing

:,/abc/join

This does join the two lines together. So to get around this, we'll need to make sure that closed tags are not matched. Originally I had:

:g/<line.*\(\/>\)\@!..$/,/\/>/j

This will match any line containing <line that doesn't end with />.

If this seems like black magic, try mentally breaking it up into 3 parts:

<line.*
\(\/>\)\@!
..$

And read up on the :h /\@! quantifier.

But now as I write up this answer, I realized there's a much simpler solution using negative character ranges:

:g/<line.*[^/][^>]$/,/\/>/j

This is both simpler, and waaaaay easier to understand.

  • This worked! But only after I removed the trailing whitespace on some lines via :%s/\s\+$//e. [vi.stackexchange.com/questions/454/… using] – JHiant Aug 25 '17 at 22:27
  • And yes, the search within a range is powerful. There's some interesting stuff you can do with it, for instance one trick is to search for something at the end of a block, then you can create a backwards range from it as per: g/regex1/?regex2?+1;/regex3/-1d – JHiant Aug 25 '17 at 22:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.