Similar to the answer at https://vi.stackexchange.com/a/818/227, you can use the global command.
With it you can instruct vim to search for lines matching a pattern, and then perform commands on it.
In your case, you wish to prepend text to lines starting with "Level N:", so our global command could be
:g/^Level \d:/{COMMANDS}
Using the substitute command (regular expression replacement) for the command
The commands is more fun. I usually like to do a regular expression replacement for stuff like this, since it is easy to use variables.
Example for your question
:let i = 1 | g/^Level \d:/s/^/\=printf("%02d ", i)/ | let i = i+1
How it works
In the replacement section of a substitution command can be an expression.
The first thing we'll do is set a variable i
to be the starting number. I chose 1, but any number will do. let i = 1
Then we run our global command, which sets us up to do an action on matched lines. g/^Level \d:/
We'll have our global command insert the value and increment our counter using the substitution command and the let command. s/^/\=printf("%02d ", i)/ | let i = i+1
The substitution command's regular expression finds the beginning of the line ^
and replaces it with an expression, and our expression will be the result of a formatted print. Like in the C language, vim's printf takes formatting parameters. %02d
means convert an argument as though it was a decimal number d
, occupying at least 2 spaces 2
and pad with 0 0
. For details and other conversion options (including floating point formatting), see :help printf
. We give printf our counting variable i
and it gives us 01
the first time, 02
the second time, etc. This gets used by the substitution command to replace the beginning of the line, effectively inserting the result of the printf at the beginning.
Note that I put a space after the d: "%02d "
. You didn't ask for it in the question (and I didn't see example output), but I suspected you wanted to separate the number from the word "Level". Remove the space from the string given to printf to have the inserted number right next to the L in Level.
Finally, that let i = i + 1
increments our counter after each substitution.
This can be applied generally for replacing parts of lines that are matched by other criteria with arbitrary functional data.
Using combined normal commands
This is good for simple insertions or complex editing. Like with substitute, we'll use global to match, but instead of regular expression substitution, we'll execute a series of operations as if typed by the user.
Example for your question
:let i = 1 | g/^Level \d:/execute "normal! I" . printf("%02d ", i) | let i = i+1
How it works
The values used are very similar to the substitute (we're still using printf to format our number to make it 0 padded with 2 digits), but the operation is different.
Here we use the execute command, which takes a string and runs the string as an ex command (:help :exe
). We construct a string that combines "normal! I" with our data, which will be "normal! I01 " the first time and "normal! I02 " the second time, etc.
The normal
command performs operations as if in normal mode. In this example, our normal command is I
, which inserts at the beginning of the line. If we had used dd
it would delete the line, o
would open a new line after the matched line. It is as if you typed I
(or any other operations) yourself in normal mode. we use the !
after normal
to make sure no mappings get in our way. See :help :normal
.
What is inserted then is the value of our printf, as in the first example of using substitute.
This method can be fancier than regex, because you can do things like execute "normal! ^2wy" . i . "th$p"
, which will go to the beginning of the text ^
, move forward 2 words 2w
, yank until the ith 'h' character y" . i . "th
, move to the end of the line $
, and paste p
.
This is almost like running a macro, but doesn't actually use up a register and can combine strings from any expressions. I find this to be very powerful.
Approach where each level has its own counter
You might want each level to get its own counter. If you know the maximum number of levels ahead of time, you can do the following (adding extra code to find the largest level might not be too difficult, but would make this answer too long. This is getting long as it is).
First, lets free i, in case we already used it as an integer. We can't convert i to a list, we have to create it that way.
:unlet! i
Next, lets set i to be a list containing the number of levels. You showed 2 in your question, but lets assume 10 for the fun of it. Since list indexing is 0 based, and I don't want to bother correcting for 1 based like your list, we'll just create enough elements (11) and never use the 0 index.
:let j = 0
:let i = []
:while j < 11 | let i += [1] | let j += 1 | endwhile
Next, we need a way to get the level number. Fortunately, substitute is available as a function as well, so we'll give it our line and extract the level number substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")
Since i is now a list of 11 1
s (each index is the counter for our level), we can now adjust either of the above examples to use the result of this substitution:
Via substitute command:
:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | s/^/\=printf("%02d ", i[ind])/ | let i[ind] += 1
Via normal command:
:unlet! i | unlet! j | let j = 0 | let i = [] | while j < 11 | let i += [1] | let j += 1 | endwhile
:g/^Level \d:/let ind=str2nr(substitute(getline("."), "^Level \\(\\d\\):.*", "\\=submatch(1)", "")) | execute "normal! I" . printf("%02d ", i[ind]) | let i[ind] += 1
Example input:
Level 1: stuff
Level 1: Stuff
Some text
Level 3: Other
Level 1: Meh
Level 2: More
Example output:
01 Level 1: stuff
02 Level 1: Stuff
Some text
01 Level 3: Other
03 Level 1: Meh
01 Level 2: More