6

I can't find out how to join all lines till a next condition happens (a line with only 1 or more numbers) p.e.

input:

1    
text text text text (with numbers)   
text text text text (with numbers)    
2
this text   
text text text text (with numbers)  
text text text  
3  
text text text text (with numbers)  
4  
etc  

desidered output:

1 text text text text (with numbers) text text text text (with numbers)    
2 this text text text text text (with numbers) text text text  
3 text text text text (with numbers)  
4  
etc

I normally use global/^/,+2 join but the number of lines to join are not always 3 in my example above.

Reman
  • 7,931
  • 11
  • 55
  • 97
  • vim only supports POSIX style regexes which don't support lookaround - so if it has to be vim, then it can't be a regex alone. If you can use other editors, it's easy: Replace `\s*\n(?!^\d)` with a single space. – Tim Pietzcker May 27 '14 at 14:09
  • 1
    Nice question, with example data, desired output, and attempted steps. That deserves an upvote! – Ingo Karkat May 27 '14 at 14:20
  • 3
    @tim you assume that the only way to solve this problem involves lookaround and then that vim doesn't have lookaround regexp. Both assumptions are wrong. – mb14 May 27 '14 at 14:20
  • Interesting. The regex docs for vim my Google search turned up only covered POSIX. But yes, it's also possible without lookarounds. I was too pessimistic there. – Tim Pietzcker May 27 '14 at 15:16

3 Answers3

6

Instead of the static +2 end of the range for the :join command, just specify a search range for the next line that only contains a number (/^\d\+$/), and then join until the line before (-1):

:global/^/,/^\d\+$/-1 join
Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
  • Should be noted this does not join the very last section correctly. Please look @mb14 solution – Peter Rincker May 27 '14 at 14:33
  • Works fine. It returns an error (E16: Invalid Range) but it still does what I want. Tnx. (and thanks for the upvote) – Reman May 27 '14 at 14:49
  • 1
    When there's no following number-only line, the error is thrown. This could be avoided, but would make the command more complex. If this suits your data (and you could always temporarily add another number at the end), just ignore it. – Ingo Karkat May 27 '14 at 15:15
5

v/^\d\+/-j will do the trick.

v execute the function for each not matching the condition ^\d\+ your condition : Line starting with a number. -j go one line backward an join. Or if you prefer join the current line with the previous line.

So basically we join every lines not matching your condition with the previous line.

mb14
  • 22,276
  • 7
  • 60
  • 102
3

Just because of the comment by Tim that it couldn't be done with only a regular expression search and replace using Vim, I present this: how to do it with only a regular expression search and replace, using Vim:

:%s#\s*\n\(\d\+\s*\n\)\@!# #

If you're not fond of backslashes, it can be simplified using "very magic" \v:

:%s#\v\s*\n(\d+\s*\n)@!# #

This is adapted from Tim's Perl-style regular expression given in the same comment, improved to make sure the "stop line" only has numbers (and maybe trailing whitespace).

See :help perl-patterns if you're comfortable with Perl and find yourself having trouble with the Vim regular expression dialect.

Ben
  • 8,725
  • 1
  • 30
  • 48