You could try the following command:
:let c=0 | g/^* /let c+=1 | s//\=c.'. '
First it initializes the variable c
(let c=0
), then it executes the global command g
which looks for the pattern ^*
(a beginning of line, followed by an asterisk and a space).
Whenever a line containing this pattern is found, the global command executes the command:
let c+=1 | s//\=c.'. '
It increments the variable c
(let c+=1
), then (|
) it substitutes (s
) the previous searched pattern (//
) with the evaluation of an expression (\=
):
the contents of variable c
concatenated (.
) with the string '. '
If you don't want to modify all the lines from your buffer, but only a specific paragraph, you can pass a range to the global command.
For example, to modify only the lines whose number is between 5 and 10:
:let c=0 | 5,10g/^* /let c+=1 | s//\=c.'. '
If you have a file containing several similar lists which you want to convert, for example something like this:
some random stuff some random stuff
* foo 1. foo
* bar 2. bar
* baz 3. baz
some other random stuff some other random stuff
==>
some random stuff some random stuff
* foo 1. foo
* bar 2. bar
* baz 3. baz
* qux 4. qux
some other random stuff some other random stuff
You can do it with the following command:
:let [c,d]=[0,0] | g/^* /let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')] | s//\=c.'. '
It's just a variant of the previous command, which resets the variable c
when you switch to another list. To detect whether you are in another list, the variable d
is used to store the number of the last line where a substitution was made.
The global command compares the current line number (line('.')
) with d+1
. If they are the same, it means we are in the same list as before so c
is incremented (c+1
), otherwise it means we are in a different list, so c
is reset (1
).
Inside a function, the command let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')]
could be rewritten like this:
let c = line('.') == d+1 ? c+1 : 1
let d = line('.')
Or like this:
if line('.') == d+1
let c = c+1
else
let c = 1
endif
let d = line('.')
To save some keystrokes, you could also define the custom command :NumberedLists
, which accepts a range whose default value is 1,$
(-range=%
):
command! -range=% NumberedLists let [c,d]=[0,0] | <line1>,<line2>g/^* /let [c,d]=[line('.')==d+1 ? c+1 : 1, line('.')] | s//\=c.'. '
When :NumberedLists
will be executed, <line1>
and <line2>
will be automatically replaced with the range you used.
So, to convert all the lists in the buffer, you would type: :NumberedLists
Only the lists between line 10 and 20: :10,20NumberedLists
Only the visual selection: :'<,'>NumberedLists
For more information, see:
:help :range
:help :global
:help :substitute
:help sub-replace-expression
:help list-identity (section list unpack)
:help expr1
:help :command
1.
? So:%s/^* /1. /
would do it. That seems like much less work.