I can increment the number under the cursor (by e.g. 10) by doing
10<C-a>
But how do I multiply the number under the cursor with x amount and the replace it in the same way I do when incrementing by a constant amount?
Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It only takes a minute to sign up.
Sign up to join this communityThere's no magic, single operation for arithmetic. You can use substitution with sub-replace expression. This will replace all numbers on the current line with their value multiplied by 5.
s/\d\+/\=submatch(0)*5/
The \=
operator tells Vim to treat everything that follows as an expression, evaluate it, and use the result as the replacement of the final matched text. And when submatch(0)
is evaluated it returns whatever the pattern on the left matched. I'm multiplying that by 5, as I mentioned.
Tune the pattern part as needed for your particular situation. I'm matching any numbers but you could just put 10
there though that's not particularly useful for general needs. If there are other things on the line that you want to ignore you could use \zs
and \ze
to isolate a number. This one will only effect a number that follows a colon at the beginning of the line:
s/^:\zs\d\+\ze/\=submatch(0)*5/
Rather than describe in detail how sub-replace expressions generally work in this space you can look at my answer here: How to run a substitute command on only a certain part of the line
"100" + 0 => 100
, i.e. arithmetic is pretty explicit conversion.
You could use the following to reimplement a mechanism similar to <c-a>
but for multiplication:
function! MultiplyInPlace(count) abort
" Get the word under the cursor
let current_word = expand('<cword>')
" If we don't match a number return
if (current_word !~ '\d\+')
return
endif
" Get cursor position
let col = getcurpos()[2]
" Calculate the result and replace in buffer
let res = current_word * a:count
execute 's/\%' . col . 'c' . current_word . '/' . res
" Move back to the number we changed
normal! g``
endfunction
" Use [count]<leader><c-x> to multiply number under cursor by [count]
nnoremap <silent> <leader><c-x> :<C-U>call MultiplyInPlace(v:count1)<CR>
The idea is to create a function MultiplyInPlace()
which takes a count as argument. The function will use :h <cword>
to get the word under the cursor and match it against /\d\+/
to make sure it's a number (:h expr-=~
).
If it's a number a simple multiplication is done and we use a substitute command forged with execute
to do the replacement. (:h :execute
).
In the substitute command get use the :h /\%c
atom to match the current column of the cursor to avoid the bug described by @Benji in their comment on this answer.
The last line is needed to avoid the cursor jumping at the beginning of the line. (`:h g``)
Then you can create a normal mode mapping (here <leader><c-x>
) which will take a count and call the function with it (and use 1) if no count is provided. (:h v:count
)
You can then adapt the code pretty simply to also handle the divisions (either create a second function DivideInPlace()
or add a new argument to MultiplyInPlace()
to switch between behaviors).
s
is used to replace the word, if there are two of the same numbers on the line, the first will be replaced even if the cursor is over the second.
Feb 5, 2022 at 14:16
getcurpos()
to get the column where the cursor is and using /\%c
in the pattern I think the bug is fixed.