forked from Pephers/Super-Calculator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Super Calculator.py
120 lines (105 loc) · 4.37 KB
/
Super Calculator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from __future__ import division
import sys
import re
import math
import random
from decimal import *
import sublime
import sublime_plugin
class SuperCalculatorCommand(sublime_plugin.TextCommand):
def __init__(self, view):
self.view = view
self.settings = sublime.load_settings("Super Calculator.sublime-settings")
self.callables = {}
self.constants = {}
for lib in (random, math):
for key in dir(lib):
attr = getattr(lib, key)
if key[0] != '_':
if callable(attr):
self.callables[key] = attr
else:
self.constants[key] = attr
self.constants[key.upper()] = attr
def average(nums):
return sum(nums) / len(nums)
self.callables['avg'] = average
self.callables['average'] = average
class Constant(object):
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
return self._func(*args, **kwargs)
def __repr__(self):
return self._func()
def password(length=10):
pwdchrs = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
return ''.join(random.choice(pwdchrs) for _ in range(length))
password = Constant(password)
self.callables['pwd'] = password
self.callables['password'] = password
self.constants['pwd'] = password
self.constants['password'] = password
self.constants['PWD'] = password
self.constants['PASSWORD'] = password
allowed = '|'.join(
[r'[-+*/%%()]'] +
[r'\b[-+]?(\d*\.)?\d+\b'] +
[r'\b%s\b' % c for c in self.constants.keys()] +
[r'\b%s\s*\(' % c for c in self.callables.keys()]
)
self.regex = r'(%s)((%s|[ ])*(%s))?' % (allowed, allowed, allowed)
self.dict = self.callables.copy()
self.dict.update(self.constants)
def run(self, edit):
result_regions = []
exprs = []
for region in reversed(self.view.sel()):
# Do the replacement in reverse order, so the character offsets
# don't get invalidated
exprs.append((region, self.view.substr(region)))
for region, expr in exprs:
if expr:
# calculate expression and replace it with the result
try:
result = str(eval(expr, self.dict, {}))
except Exception as e:
sublime.status_message("Error: %s" % e)
continue
else:
# round result if decimals are found
if '.' in result:
result = round(Decimal(result), self.settings.get("round_decimals"))
result = str(result)
if self.settings.get("trim_zeros") and '.' in result:
result = result.strip('0').rstrip('.')
if result == '':
result = '0'
if result != expr:
self.view.replace(edit, region, result)
sublime.status_message("Calculated result: " + expr + "=" + result)
continue
line_region = self.view.line(region)
match_region = self.find_reverse(self.regex, region)
if match_region:
match = self.view.substr(match_region)
# validate result and check if it is in the current line
if re.match(self.regex, match) and line_region.begin() <= match_region.begin():
result_regions.append(match_region)
sublime.status_message("Calculate: " + match + "?")
if result_regions:
self.view.sel().clear()
for region in result_regions:
self.view.sel().add(region)
def find_reverse(self, string, region):
new_regions = (r for r in reversed(self.view.find_all(string))
if r.begin() < region.end())
try:
if sys.version_info < (3,0,0) :
new_region = new_regions.next()
else :
new_region = next(new_regions)
except StopIteration:
return None
else:
return new_region