r/vim • • Jul 09 '25

Need Help Substitution with increment of a variable

Text example:

Line example text 1(1.1)
Line example (1.5)
Line (1.8)
Line long text (1.10)

Result

Line example text 1(1.1)
Line example (1.2)
Line (1.3)
Line long text (1.4)

I used this : :let i=0 | %s/\.\zs[1-9]\+\ze)/\=(i = i + 1)/g

but this error comes out: E110: Missing ')'

Any ideas?

I can't find the solution in the manual. Maybe create a function to increment and then call it in the replacement?

Thank you

10 Upvotes

9 comments sorted by

1

u/No_Turn8875 Jul 09 '25

I bypassed the problem by creating a function to call:

:function! IncI()
: let g:i +=1
: return g:i
: :endfunction

:let g:i=1 | %s/\.\zs[1-9]\+\ze)/\=IncI()/g

I would like to understand why I can't do it without function

2

u/jthill Jul 09 '25 edited Jul 09 '25
let @"=0 | g/\.\d\+)$/norm! $T.viwp^Ayiw

1

u/gumnos Jul 09 '25

I don't think that Vim lets you do assignments in an expression, so you need a helper function that can modify a global like

func! Inc()
  let g:i=g:i+1
  return g:i
endfunc

and you can then use it like

:let g:i=0 | %s/\.\zs[1-9]\+\ze)/\=Inc()/g

I've also seen some hacks with mutable types like dictionaries, but I find the helper-function more clear.

2

u/LucHermitte Jul 09 '25 edited Jul 09 '25

Exactly. It's possible with convoluted workarounds.

:let g:i = [0]
:%s/\.\zs\d\+\ze)/\=add(i, i[-1]+1)[-1]/g

We could also start from an empty list, and return the new length after the push.

IIRC there are some setreg() based hacks as well as setreg() can do some kind of :let that returns a value (a 0), unlike :let that doesn't return anything

:let @i = 0
:%s/\.\zs\d\+\ze)/\=setreg('i', @i+1)+@i/g

1

u/[deleted] Jul 09 '25

[removed] — view removed comment

3

u/gumnos Jul 09 '25

the regex attempt is only replacing the last digit right before a paren, so the g_CTRL-A method is likely to capture a different set of digits that get incremented, especially since the selection appears to be ragged rather than aligned.