Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Imagine I have the following text:

some random stuff
* asdf
* foo
* bar
some other random stuff

I want to replace the asterisk bullets with numbers, like so:

some random stuff
1. asdf
2. foo
3. bar
some other random stuff

How can this be done in vim ?

share|improve this question
    
Why don't you go for plugins? A similar one is increment.vim in Github – SibiCoder Apr 16 at 11:01
up vote 11 down vote accepted

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
share|improve this answer

This only works with a recent Vim version (that has :h v_g_CTRL-A):

  1. Block-select the list bullets (*) and replace them with 0 (cursor is on first *): Ctrl-v j j r 0.
  2. Reselect previous block and increment with counter: gv g Ctrl-a

... and that's it :)


(If you want to have a dot after each number, change 1st step to: Ctrl-v j j s 0 . Esc)

share|improve this answer

Visually select the lines and execute this substitution command:

:'<,'>s/*/\=line('.') - line("'<") + 1 . '.'

See :help sub-replace-expression, :help line(), and :help '<.

To avoid having to select the lines, backward and forward searches with offsets can be used to specify the substitution range like this:

:?^[^*]?+1,/^[^*]/-1s/*/\=line('.') - search('^[^[:digit:]]', 'bn') . '.'

See :help cmdline-ranges

share|improve this answer

Another way:

:let n = 1 | g/^* /s//\=printf('%d. ', n)/g | let n = n + 1
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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