zdd
zdd

Reputation: 8747

How to let Vim process my command backforward?

Suppose I have 5 lines of text, if I input some commands to let vim process each line, Vim will process the text one by one, first line, second line, ... the last line. what I want is to let Vim process my text in reverse order. that is the last line first, then the 4th line, and at last the first line.

Why I need this? I have the following text

1234567890
abc
123
def
1234567890
123456789

I want to remove the newline symbol(\n) from lines which contains 3 characters. so after processing,I will get the following text

1234567890
abc123def1234567890
123456789

It seems a piece of cake, I use the following command

:%s/\v(^\w{3})\n/\1/

But what i got is

1234567890
abc123
def1234567890
1234567890

Why? I guess vim first remove \n from the second line, then this line has text abc123, now vim will not remove \n after 123, since it's not 3 characters now, so vim process the next line def, and remove \n from it, that's the result i got.

If vim can process from back to front, this will not happen and I can got the result I want.

BTW, I can get the expected result in other ways, I just want to know whether this is possible.

Upvotes: 2

Views: 125

Answers (4)

Kent
Kent

Reputation: 195229

this line should do what you want:

:%s/\v(\_^\w{3}\n)+/\=substitute(submatch(0),"\n","","g")/

if you want to do it simpler with external command, e.g. awk, you could:

%!awk '{printf "\%s", length($0)==3? $0:$0"\n"}'

Upvotes: 1

Prince Goulash
Prince Goulash

Reputation: 15735

In this case it is easier to use the :global command to join the lines.

:g/^\w\{3}$/normal! gJ

The command gJ joins the current line with the following line without inserting any spaces. The global command above calls gJ on each line containing only three characters. It works by marking all the matches first, before performing the operation, so the problem of looping is avoided.

Upvotes: 1

Ingo Karkat
Ingo Karkat

Reputation: 172738

Explicitly loop over the range of lines (e.g. the visually selected ones) backwards, then execute the command on each line. I've used :join here instead of the :substitute with a newline:

:for i in range(line("'>"), line("'<"), -1)| silent execute i . 'g/^\w\{3}$/join!' | endfor

Upvotes: 2

devnull
devnull

Reputation: 123608

Can be achieved using perl:

while (<>) {
  chomp if (/^...$/);
  print;
}

Upvotes: 2

Related Questions