8

Let's say that I have the following very simple file

a
b
c
d
e

and I've decided that I want to add empty lines after each line. Several different methods immediately jump to my mind. We might just do it (and thus embrace failure). We might record a macro like qqo<ESC>jq and repeat it several times.

Two other methods seemed more obvious to me at the time.

Firstly, I thought I would issue the :norm command o on each line. So I run :%norm o. But what actually happens is that we get 5 blank lines, followed by the un-separated lines as above. I interpret this to mean that by %norm, vim actually picks up the message issue the following normal command on the first five lines of this five line file. The o command creates a new line and vim is "dumb" in the sense that it references by line number and not actually by some other identifier.

Well, I was embarrassed. Sure. I tried a few other things to see if I could make the above method work, but alas, I couldn't. Out of curiosity, I tried my other favorite mass-apply method. This led me to try :g/^/norm o. To my surprise, this works just fine! So to my eyes, it seems that vim isn't "dumb" here in the same way as above and references lines by more than just line number.

What exactly is going on?

1
  • 2
    On a side note: you can also do it like this: :%s/$/\r/, or like this: :%s/\n/\r\r/. The takeaway is that newlines can be matched with \n, but have to be written as \r in replacement values.
    – lcd047
    Jul 17, 2015 at 5:07

1 Answer 1

10

Well, % is shorthand for 1,$ (a range from the first line to the last). From :he :%:

Line numbers may be specified with:             :range E14 {address}
        {number}        an absolute line number
        .               the current line                          :.
        $               the last line in the file                 :$
        %               equal to 1,$ (the entire file)            :%

And for :global:

The global commands work by first scanning through the [range] lines and
marking each line where a match occurs (for a multi-line pattern, only the
start of the match matters).
In a second scan the [cmd] is executed for each marked line with its line
number prepended.  For ":v" and ":g!" the command is executed for each not
marked line.  If a line is deleted its mark disappears.

So, the first case is like traversing a list while modifying it, so a counter on list elements becomes invalid. In the second case, we mark the elements we want to target in one pass, so that even if the list is modified in the second pass, we still know which elements we want to work on.

2
  • Ah, it's even in the help file for global. How silly I am. Thank you Jul 17, 2015 at 2:30
  • man - the g is overkill crazy useful. i gotta spend more time to go from average vi user to power user Feb 25, 2018 at 21:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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