Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How g makes loop in Vim ex command script

Tags:

vim

Consider the following Vim ex command,

:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1

It replaces the heading dash with ordered number in selected lines.

I don't understand why this command works as a loop from the first line to the last line of the selected lines. That is, how g can repeat let i+=1 over and over again.

like image 785
George Avatar asked Feb 09 '15 05:02

George


People also ask

What is G in Vim?

The g command first scans all lines in range and marks those that match pattern . It then iterates over the marked lines and executes cmd . (See multi-repeat in vim's documentation). If cmd happens to delete a marked line, the mark disappears.

What is exclamation mark in Vim?

An exclamation mark ( ! ) tells ex to create a shell and to regard what follows as a Unix command: :! command. So if you are editing and you want to check the time or date without exiting vi, you can enter: :!date.


2 Answers

The pattern of a global command is:

:range g[lobal][!]/pattern/cmd

The global commands work by first scanning through the [range] of of the lines and marking each line where a match occurs. In a second scan the [cmd] is executed for each marked line with its line number prepended. If a line is changed or deleted its mark disappears. The default for the [range] is the whole file. (see http://vimregex.com/#global for more details)

Now let's analyse

:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1

step by step.

  1. let i=1 is a single command executed setting the basic number for the loop. We can just execute it alone at the very beginning. Then '<,'>g/^/ s/^\ *-/\=i/ | let i+=1 looks a little more like a global command.
  2. '<,'>g defines the range. '< represents the first line and '> represents the last line of the selected area. (:help '< for more details)
  3. ^ of course matches every line in range.
  4. s/^\ *-/\=i/ | let i+=1 is the [cmd], the number of times it will be executed equals to the number of lines in the selected area, and this is the most important reason why the loop took place.
  5. The part before | is a typical substitute command :range s[ubstitute]/pattern/string/ (see http://vimregex.com/#substitute for more details)
  6. ^\ *- matches 0 or more whitespace followed by a dash at the beginning of a line. We substitute \=i for this pattern. (:help :s\= for more details)
  7. After s/^\ *-/\=i/, let i+=1 is executed. Then the next line, ... , till the last line of selected area.
  8. For better understanding that s/^\ *-/\=i/ | let i+=1 is a [cmd] as a whole, we can change the order of the two [sub-cmd], obtaining let i+=1 | s/^\ *-/\=i/. But for the same effect, let i=0 at the very beginning is essential.
like image 112
fujianjin6471 Avatar answered Oct 21 '22 15:10

fujianjin6471


This is the general pattern of a :global command:

:g/foo/command

Because everything after the second separator is considered as one command, the counter is incremented each time the command is executed: one time for each matching line.

like image 33
romainl Avatar answered Oct 21 '22 16:10

romainl