diff vim/bundle/math/plugin/vmath.vim @ 22:b2c16cd71d95

Add math plugin
author zegervdv <zegervdv@me.com>
date Sun, 23 Mar 2014 18:08:34 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vim/bundle/math/plugin/vmath.vim	Sun Mar 23 18:08:34 2014 +0100
@@ -0,0 +1,140 @@
+" Vim global plugin for math on visual regions
+" Maintainer:	Damian Conway
+" License:	This file is placed in the public domain.
+
+"######################################################################
+"##                                                                  ##
+"##  To use:                                                         ##
+"##                                                                  ##
+"##     vmap <expr>  ++  VMATH_YankAndAnalyse()                      ##
+"##     nmap         ++  vip++                                       ##
+"##                                                                  ##
+"##  (or whatever keys you prefer to remap these actions to)         ##
+"##                                                                  ##
+"######################################################################
+
+
+" If already loaded, we're done...
+if exists("loaded_vmath")
+    finish
+endif
+let loaded_vmath = 1
+
+" Preserve external compatibility options, then enable full vim compatibility...
+let s:save_cpo = &cpo
+set cpo&vim
+
+" Grab visual selection and do simple math on it...
+function! VMATH_YankAndAnalyse ()
+    return "y:call VMATH_Analyse()\<CR>gv"
+endfunction
+
+" What to consider a number...
+let s:NUM_PAT = '^[+-]\?\d\+\%([.]\d\+\)\?\([eE][+-]\?\d\+\)\?$'
+
+" How widely to space the report components...
+let s:REPORT_GAP = 5  "spaces between components
+
+" Do simple math on current yank buffer...
+function! VMATH_Analyse ()
+    " Extract data from selection...
+    let selection = getreg('')
+    let raw_numbers = filter(split(selection), 'v:val =~ s:NUM_PAT')
+    let numbers = map(copy(raw_numbers), 'str2float(v:val)')
+
+    " Results include a newline if original selection did...
+    let newline = selection =~ "\n" ? "\n" : ""
+
+    " Calculate and en-register various interesting metrics...
+    let summation = len(numbers) ? join( numbers, ' + ') : '0'
+    call setreg('s', s:tidy( eval( summation )      ))   " Sum     --> register s
+    call setreg('a',         s:average(raw_numbers)  )   " Average --> register a
+    call setreg('x', s:tidy( s:max(numbers)         ))   " Max     --> register x
+    call setreg('n', s:tidy( s:min(numbers)         ))   " Min     --> register n
+    call setreg('r',         @n . ' to ' . @x        )   " Range   --> register r
+
+    " Default paste buffer should depend on original contents (TODO)
+    call setreg('', @s )
+
+    " Report...
+    let gap = repeat(" ", s:REPORT_GAP)
+    highlight NormalUnderlined term=underline cterm=underline gui=underline
+    echohl NormalUnderlined
+    echo  's'
+    echohl NONE
+    echon  'um: ' . @s . gap
+    echohl NormalUnderlined
+    echon 'a'
+    echohl NONE
+    echon  'vg: ' . @a . gap
+    echon 'mi'
+    echohl NormalUnderlined
+    echon   'n'
+    echohl NONE
+    echon    ': ' . @n . gap
+    echon 'ma'
+    echohl NormalUnderlined
+    echon   'x'
+    echohl NONE
+    echon    ': ' . @x . gap
+
+endfunction
+
+" Prettify numbers...
+function! s:tidy (number)
+    let tidied = printf('%g', a:number)
+    return substitute(tidied, '[.]0\+$', '', '')
+endfunction
+
+" Compute average with meaningful number of decimal places...
+function! s:average (numbers)
+    " Compute average...
+    let summation = eval( len(a:numbers) ? join( a:numbers, ' + ') : '0' )
+    let avg = 1.0 * summation / s:max([len(a:numbers), 1])
+
+    " Determine significant figures...
+    let min_decimals = 15
+    for num in a:numbers
+        let decimals = strlen(matchstr(num, '[.]\d\+$')) - 1
+        if decimals < min_decimals
+            let min_decimals = decimals
+        endif
+    endfor
+
+    " Adjust answer...
+    return min_decimals > 0 ? printf('%0.'.min_decimals.'f', avg)
+    \                       : string(avg)
+endfunction
+
+" Reimplement these because the builtins don't handle floats (!!!)
+function! s:max (numbers)
+    if !len(a:numbers)
+        return 0
+    endif
+    let numbers = copy(a:numbers)
+    let maxnum = numbers[0]
+    for nextnum in numbers[1:]
+        if nextnum > maxnum
+            let maxnum = nextnum
+        endif
+    endfor
+    return maxnum
+endfunction
+
+function! s:min (numbers)
+    if !len(a:numbers)
+        return 0
+    endif
+    let numbers = copy(a:numbers)
+    let minnum = numbers[0]
+    for nextnum in numbers[1:]
+        if nextnum < minnum
+            let minnum = nextnum
+        endif
+    endfor
+    return minnum
+endfunction
+
+
+" Restore previous external compatibility options
+let &cpo = s:save_cpo