22
|
1 " Vim global plugin for math on visual regions
|
|
2 " Maintainer: Damian Conway
|
|
3 " License: This file is placed in the public domain.
|
|
4
|
|
5 "######################################################################
|
|
6 "## ##
|
|
7 "## To use: ##
|
|
8 "## ##
|
|
9 "## vmap <expr> ++ VMATH_YankAndAnalyse() ##
|
|
10 "## nmap ++ vip++ ##
|
|
11 "## ##
|
|
12 "## (or whatever keys you prefer to remap these actions to) ##
|
|
13 "## ##
|
|
14 "######################################################################
|
|
15
|
|
16
|
|
17 " If already loaded, we're done...
|
|
18 if exists("loaded_vmath")
|
|
19 finish
|
|
20 endif
|
|
21 let loaded_vmath = 1
|
|
22
|
|
23 " Preserve external compatibility options, then enable full vim compatibility...
|
|
24 let s:save_cpo = &cpo
|
|
25 set cpo&vim
|
|
26
|
|
27 " Grab visual selection and do simple math on it...
|
|
28 function! VMATH_YankAndAnalyse ()
|
|
29 return "y:call VMATH_Analyse()\<CR>gv"
|
|
30 endfunction
|
|
31
|
|
32 " What to consider a number...
|
|
33 let s:NUM_PAT = '^[+-]\?\d\+\%([.]\d\+\)\?\([eE][+-]\?\d\+\)\?$'
|
|
34
|
|
35 " How widely to space the report components...
|
|
36 let s:REPORT_GAP = 5 "spaces between components
|
|
37
|
|
38 " Do simple math on current yank buffer...
|
|
39 function! VMATH_Analyse ()
|
|
40 " Extract data from selection...
|
|
41 let selection = getreg('')
|
|
42 let raw_numbers = filter(split(selection), 'v:val =~ s:NUM_PAT')
|
|
43 let numbers = map(copy(raw_numbers), 'str2float(v:val)')
|
|
44
|
|
45 " Results include a newline if original selection did...
|
|
46 let newline = selection =~ "\n" ? "\n" : ""
|
|
47
|
|
48 " Calculate and en-register various interesting metrics...
|
|
49 let summation = len(numbers) ? join( numbers, ' + ') : '0'
|
|
50 call setreg('s', s:tidy( eval( summation ) )) " Sum --> register s
|
|
51 call setreg('a', s:average(raw_numbers) ) " Average --> register a
|
|
52 call setreg('x', s:tidy( s:max(numbers) )) " Max --> register x
|
|
53 call setreg('n', s:tidy( s:min(numbers) )) " Min --> register n
|
|
54 call setreg('r', @n . ' to ' . @x ) " Range --> register r
|
|
55
|
|
56 " Default paste buffer should depend on original contents (TODO)
|
|
57 call setreg('', @s )
|
|
58
|
|
59 " Report...
|
|
60 let gap = repeat(" ", s:REPORT_GAP)
|
|
61 highlight NormalUnderlined term=underline cterm=underline gui=underline
|
|
62 echohl NormalUnderlined
|
|
63 echo 's'
|
|
64 echohl NONE
|
|
65 echon 'um: ' . @s . gap
|
|
66 echohl NormalUnderlined
|
|
67 echon 'a'
|
|
68 echohl NONE
|
|
69 echon 'vg: ' . @a . gap
|
|
70 echon 'mi'
|
|
71 echohl NormalUnderlined
|
|
72 echon 'n'
|
|
73 echohl NONE
|
|
74 echon ': ' . @n . gap
|
|
75 echon 'ma'
|
|
76 echohl NormalUnderlined
|
|
77 echon 'x'
|
|
78 echohl NONE
|
|
79 echon ': ' . @x . gap
|
|
80
|
|
81 endfunction
|
|
82
|
|
83 " Prettify numbers...
|
|
84 function! s:tidy (number)
|
|
85 let tidied = printf('%g', a:number)
|
|
86 return substitute(tidied, '[.]0\+$', '', '')
|
|
87 endfunction
|
|
88
|
|
89 " Compute average with meaningful number of decimal places...
|
|
90 function! s:average (numbers)
|
|
91 " Compute average...
|
|
92 let summation = eval( len(a:numbers) ? join( a:numbers, ' + ') : '0' )
|
|
93 let avg = 1.0 * summation / s:max([len(a:numbers), 1])
|
|
94
|
|
95 " Determine significant figures...
|
|
96 let min_decimals = 15
|
|
97 for num in a:numbers
|
|
98 let decimals = strlen(matchstr(num, '[.]\d\+$')) - 1
|
|
99 if decimals < min_decimals
|
|
100 let min_decimals = decimals
|
|
101 endif
|
|
102 endfor
|
|
103
|
|
104 " Adjust answer...
|
|
105 return min_decimals > 0 ? printf('%0.'.min_decimals.'f', avg)
|
|
106 \ : string(avg)
|
|
107 endfunction
|
|
108
|
|
109 " Reimplement these because the builtins don't handle floats (!!!)
|
|
110 function! s:max (numbers)
|
|
111 if !len(a:numbers)
|
|
112 return 0
|
|
113 endif
|
|
114 let numbers = copy(a:numbers)
|
|
115 let maxnum = numbers[0]
|
|
116 for nextnum in numbers[1:]
|
|
117 if nextnum > maxnum
|
|
118 let maxnum = nextnum
|
|
119 endif
|
|
120 endfor
|
|
121 return maxnum
|
|
122 endfunction
|
|
123
|
|
124 function! s:min (numbers)
|
|
125 if !len(a:numbers)
|
|
126 return 0
|
|
127 endif
|
|
128 let numbers = copy(a:numbers)
|
|
129 let minnum = numbers[0]
|
|
130 for nextnum in numbers[1:]
|
|
131 if nextnum < minnum
|
|
132 let minnum = nextnum
|
|
133 endif
|
|
134 endfor
|
|
135 return minnum
|
|
136 endfunction
|
|
137
|
|
138
|
|
139 " Restore previous external compatibility options
|
|
140 let &cpo = s:save_cpo
|