You need substitution with a state. I remember having provided a (/several?) complete solution for this kind of problems on SO.
Here is another way to proceed (1).
Now, I'll proceed into 2 steps:
- a dummy list variable I need for the dirty and convoluted trick I'll employ
- a substitution where I insert the len of this dummy array I'm filling on each matching occurrence.
Which gives:
:let t=[]
:%s/#\zs\d\+\(\.\d\+\)\=\ze/\=len(add(t,1))/g
If you're not used to vim regexes, I use :h /\zs
and \ze
to specify which sub-pattern I'm matching, then I match a series of digits possibly followed by a dot and other digits. This is not perfect for any floating point number, but this is enough here.
Note: You'll have to wrap it up in a couple function + command for a simple interface. Again, there are examples on SO/vim (here, here, here)
Nowadays I know enough of vim to not care about wrapping this trick into a command. Indeed I'll be able to write this command on the first try, while I'll take several minutes to remember the name of the command.
(1) The objective is to be able to maintain a state in-between substitutions, and to replace the current occurrence with something that depends on the current state.
Thanks to :s\=
we are able to insert something resulting from a computation.
Remains the problem of the state. Either we define a function that's managing an external state, or we update ourselves an external state. In C (and related languages), we could have used something like length++
or length+=1
. Unfortunately, in vim scripts, +=
cannot be used out of the box. It needs to be used either with :set
or with :let
. This means that :let length+=1
increments a number, but doesn't return anything. We cannot write :s/pattern/\=(length+=1)
. We need something else.
We need mutating functions. i.e. functions that mutates their inputs. We have setreg()
, map()
, add()
and probably more. Let's start with them.
setreg()
mutates a register. Perfect. We can write setreg('a',@a+1)
as in @Doktor OSwaldo's solution. And yet, this is not enough. setreg()
is more of a procedure than a function (for those among us who know Pascal, Ada...). This means it doesn't return anything. Actually, it does return something. Nominal exit (i.e. non-exceptionnal exits) always return something. By default, when we forgot to return something, 0 is returned -- it also applies with built-in functions. That's why in his solution the replacement expression is actually \=@a+setreg(...)
. Tricky, isn't it?
map()
could also be used. If we start from a list with a single 0 (:let single_length=[0]
), we could increment it thanks to map(single_length, 'v:val + 1')
. Then we need to return the new length. Unlike setreg()
, map()
returns its mutated input. That's perfect, the length is stored at the first (and unique, and thus last as well) position of the list. The replacement expression could be \=map(...)[0]
.
add()
is the one I often use out of habit (I've just though about map()
actually, and I haven't benched their respective performances yet). The idea with add()
is to use a list as the current state, and to append something at the end before each substitution. I often store the new value at the end of the list, and use it for the replacement. As add()
also returns its mutated input list, we can use: \=add(state, Func(state[-1], submatch(0)))[-1]
. In OP's case, we only need to remember how many matches have been detected so far. Returning the length of this state list is enough. Hence my \=len(add(state, whatever))
.
perldo
, you can use:%perldo s/#\K\d+(\.\d+)?/++$i/ge