Vim Tips Wiki
Register
Advertisement
Tip 1070 Printable Monobook Previous Next

created 2005 · complexity basic · author Bryce Wagner · version 5.7


I do a lot of mathematical operations on data in text files, and I wanted a way to do it inline instead of having to copy it to a spreadsheet, perform the operations, and copy it back. The following map the four keypad operators (plus, minus, multiply, divide), to their equivalent operation. There are two versions, one for normal mode and one for visual mode.

noremap <kMinus> <C-X>
vnoremap <silent><kMinus> :<C-U>'<,'>s%\(-\?\d\+\)%\=submatch(1) - v:count1%g<CR>:noh<CR>gv
noremap <kPlus> <C-A>
vnoremap <silent><kPlus> :<C-U>'<,'>s%\(-\?\d\+\)%\=submatch(1) + v:count1%g<CR>:noh<CR>gv
noremap <silent><kMultiply> :<C-U>s%\(-\?\d*\%#\d\+\)%\=submatch(1) * v:count1%<CR><C-O>h/\d\+/e<CR>:noh<CR>
vnoremap <silent><kMultiply> :<C-U>'<,'>s%\(-\?\d\+\)%\=submatch(1) * v:count1%g<CR>:noh<CR>gv
noremap <silent><kDivide> :<C-U>s%\(-\?\d*\%#\d\+\)%\=submatch(1) / v:count1%<CR><C-O> ?\d\+?e<CR>:noh<CR>
vnoremap <silent><kDivide> :<C-U>'<,'>s%\(-\?\d\+\)%\=submatch(1) / v:count1%g<CR>:noh<CR>gv

Problems I tried hard to get the cursor location to act the same for the multiply and divide as the built in add and subtract, but there are a few conditions where it doesn't. It also clobbers your last search in the process.

Comments[]

Using submatch(0), you wouldn't need the grouping parentheses.


If you use a function, I think you can avoid the change of the search pattern and maybe other anomalies:

function! MultiplyCursor(x)
  let p = @/
  s%\d*\%#\d\+%\=submatch(0) * a:x%
  exe "normal \<C-O>"
  let @/ = p
endfunction
noremap <silent><kMultiply> :<C-U>call MultiplyCursor(v:count1)<CR>

And the same for the other commands.

The minus sign in the search pattern is not needed, because multiplication and division don't change the sign.


If you're going to use a function, might as well do it right. This will catch "failed search" errors and prevent the control-O from being executed. The stuff inside the inner try isn't necessary, but it makes it act more like the built in add and subtract, finding the next number after the cursor on the same line if the cursor isn't already on a number.

function! MultiplyCursor(x)
  let p = @/
  try
    silent s%-\?\d*\%#\d\+%\=submatch(0) * a:x%
    exe "normal \<C-O>"
  catch /^Vim\%((\a\+)\)\=:E486/
    try
      silent exe "normal /\\%#.\\{-}\\zs\\d\\+/b\<CR>"
      s%-\?\d*\%#\d\+%\=submatch(0) * a:x%
      exe "normal \<C-O>"
    catch /^Vim\%((\a\+)\)\=:E486/
    endtry
  finally
    let @/ = p
  endtry
endfunction

function! DivideCursor(x)
  let p = @/
  try
    s%-\?\d*\%#\d\+%\=submatch(0) / a:x%
    exe "normal \<C-O>"
  catch /^Vim\%((\a\+)\)\=:E486/
    try
      silent exe "normal /\\%#.\\{-}\\zs\\d\\+/b\<CR>"
      s%-\?\d*\%#\d\+%\=submatch(0) / a:x%
      exe "normal \<C-O>"
    catch /^Vim\%((\a\+)\)\=:E486/
    endtry
  finally
    let @/ = p
  endtry
endfunction

A minor addition just for the sake of reducing duplications:

function! CalculateCursor(x, operator)
  let p = @/
  try
    silent exe "s%-\\?\d*\\%#\\d\\+%\\=submatch(0) " . a:operator . " a:x%"
    exe "normal \<C-O>"
  catch /^Vim\%((\a\+)\)\=:E486/
    try
      silent exe "normal /\\%#.\\{-}\\zs\\d\\+/b\<CR>"
      exe "s%-\\?\d*\\%#\\d\\+%\\=submatch(0) " . a:operator . " a:x%"
      exe "normal \<C-O>"
    catch /^Vim\%((\a\+)\)\=:E486/
    endtry
  finally
    let @/ = p
  endtry
endfunction

noremap <kMinus> <C-X>
vnoremap <silent><kMinus> :<C-U>'<,'>call CalculateCursor(v:count1, "-")<CR>:noh<CR>gv
noremap <kPlus> <C-A>
vnoremap <silent><kPlus> :<C-U>'<,'>call CalculateCursor(v:count1, "+")<CR>:noh<CR>gv
noremap <silent><kMultiply> :<C-U>call CalculateCursor(v:count1, "*")<CR>
vnoremap <silent><kMultiply> :<C-U>'<,'>call CalculateCursor(v:count1, "*")<CR>:noh<CR>gv
noremap <silent><kDivide> :<C-U>call CalculateCursor(v:count1, "/")<CR>
vnoremap <silent><kDivide> :<C-U>'<,'>call CalculateCursor(v:count1, "/")<CR>:noh<CR>gv

Advertisement