0

I often have to write XML where text-width has been manually set by another editor:

<context>
    <p>Here is a paragraph about some stuff that happened
       and someone did a nice printing that makes the text
       break and stuff, which is nice unless you have to
       it it, and now its a PITA.</p>
    <p>Here is another paragraph. I suppose I could have
       Googled to get some lorem ipsum text to copy paste
       for this example, but that's not very fun and I wanted
       an excuse to use the then how could I use the word
       floccinaucinihilipilification?</p>
    <p>Ignoring this paragraph would be absolute
       floccinaucinihilipilification!</p>
</context>

This is really hard to work with when I need to re-write parts. What I'd rather have is this:

<context>
    <p>Here is a paragraph about some stuff that happened and someone did a nice printing that makes the text break and stuff, which is nice unless you have to it it, and now its a PITA.</p>
    <p>Here is another paragraph. I suppose I could have Googled to get some lorem ipsum text to copy paste for this example, but that's not very fun and I wanted an excuse to use the then how could I use the word floccinaucinihilipilification?</p>
    <p>Ignoring this paragraph would be absolute floccinaucinihilipilification!</p>
</context>

Is there a vim command that can do this for me?

Edit: While the example above uses <p>, there's a lot of possible block level elements. The content in question is written in DITA.

3

Here is a solution which is not perfect but not that bad:

First add these lines to your .vimrc:

function! ReJoinTags() range
    set nowrapscan
    let @/='<\(\k\+\)>\([^<]\|\n\)\{-}<\/\1>'

    let initialLine = line(".")
    normal ngnJ

    if initialLine != line(".")
        call ReJoinTags()
    endif
endfunction

Put your cursor on the root tag of your xml and call the function with :call ReJoinTags()

The function does several things:

  • First set nowrapscan so that a research will not wrap around the file.
  • Set the search register to a regex which match only the content of tags which doesn't contains subtags (i.e. only the deepest tags). I'll detail the regex later.
  • Get the current position of the cursor
  • Go to the next match, select it and join the selection.
  • Check if the cursor has moved and if so it means that a new tag has been joined so call the function again to try and join the next match.

This is not perfect but on my system from this file:

<context>
    <p>Here is a paragraph about some stuff that happened
       and someone did a nice printing that makes the text
       break and stuff, which is nice unless you have to
       it it, and now its a PITA.</p>
    <p>Here is another paragraph. I suppose I could have
       Googled to get some lorem ipsum text to copy paste
       for this example, but that's not very fun and I wanted
       an excuse to use the then how could I use the word
       floccinaucinihilipilification?</p>
    <p>Ignoring this paragraph would be absolute
       floccinaucinihilipilification!</p>
    <a>
        <b>ldkfn sqkldfj lskjdfh
            sdfkljh lsdkfjh</b>
        <b>sdf sflj zelrfkj erfh
            zer sdf sdf</b>
        <b>a zerlfkj ezrlfk erf</b>
    </a>
    <a>
        <b>
            <c> sdf zerkj zer df
                zeroi ferioj</c>
        </b>
        <b>ldkfn sqkldfj lskjdfh
            sdfkljh lsdkfjh</b>
        <b>sdf sflj zelrfkj erfh
            zer sdf sdf</b>
    </a>
</context>

I get this:

<context>
    <p>Here is a paragraph about some stuff that happened and someone did a nice printing that makes the text break and stuff, which is nice unless you have to it it, and now its a PITA.</p>
    <p>Here is another paragraph. I suppose I could have Googled to get some lorem ipsum text to copy paste for this example, but that's not very fun and I wanted an excuse to use the then how could I use the word floccinaucinihilipilification?</p>
    <p>Ignoring this paragraph would be absolute floccinaucinihilipilification!</p>
    <a>
        <b>ldkfn sqkldfj lskjdfh sdfkljh lsdkfjh</b>
        <b>sdf sflj zelrfkj erfh zer sdf sdf</b>
        <b>a zerlfkj ezrlfk erf</b> </a>
    <a>
        <b>
            <c> sdf zerkj zer df zeroi ferioj</c>
        </b>
        <b>ldkfn sqkldfj lskjdfh sdfkljh lsdkfjh</b>
        <b>sdf sflj zelrfkj erfh zer sdf sdf</b> </a>
</context>

There seems to be a problem with some tags but it's still a beginning.


For the ones interested in the regex part, first feel free to tell me how to improve it: I'm far from being a regex guru so I'll be happy to learn more.

Now for the details:

<\(\k\+\)>\([^<]\|\n\)\{-}<\/\1>
<  \k\+  > The name of a tag
<\(\k\+\)> Captures the name of a tag

          \([^<]\|\n\)\{-}      
          \(        \)\{-} Capture as few as possible occurences of the group
            [^<]\|\n       A group containing every character which is not '<' or is a new line      

                          <\/\1> The closing tag
  • Interesting approach! I'll give this a try. – Scribblemacher Apr 16 '16 at 0:53
0

You can try this:

:v/^<p>/-1j!

Inspired from this answer here

https://superuser.com/a/565566

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.