forked from hayaku/hayaku
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hayaku.py
191 lines (160 loc) · 7.94 KB
/
hayaku.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# -*- coding: utf-8 -*-
import re
import operator
from functools import partial
import sublime
import sublime_plugin
from core.probe import extract
from core.templates import make_template
__all__ = [
'HayakuCommand',
'HayakuChangeNumberCommand',
]
# максимальный размер css properties
MAX_SIZE_CSS = len('-webkit-transition-timing-function')
ABBR_REGEX = re.compile(r'[\s|;|{]([\.:%#a-z-,\d]+!?)$', re.IGNORECASE)
GUESS_REGEX = re.compile(r'selector(\s+)?(\{)?(\s+)?property(:)?(\s+)?value(;)?(\s+)?(\})?(\s+)?', re.IGNORECASE)
def get_hayaku_options(self):
# Autoguessing the options
settings = self.view.settings()
options = {}
match = {}
if settings.get("hayaku_CSS_syntax_autoguess"):
autoguess = settings.get("hayaku_CSS_syntax_autoguess")
offset = len(autoguess[0]) - len(autoguess[0].lstrip())
autoguess = [ s[offset:].rstrip() for s in autoguess]
# 1 2 3 4 5 6 7 8 9
match = GUESS_REGEX.search('\n'.join(autoguess))
options["CSS_whitespace_block_start_before"] = settings.get("hayaku_CSS_whitespace_block_start_before", match and match.group(1) or "")
options["CSS_whitespace_block_start_after"] = settings.get("hayaku_CSS_whitespace_block_start_after", match and match.group(3) or "\n\t")
options["CSS_whitespace_block_end_before"] = settings.get("hayaku_CSS_whitespace_block_end_before", match and match.group(7) or "\n\t")
options["CSS_whitespace_block_end_after"] = settings.get("hayaku_CSS_whitespace_block_end_after", match and match.group(9) or "")
options["CSS_whitespace_after_colon"] = settings.get("hayaku_CSS_whitespace_after_colon", match and match.group(5) or "")
options["CSS_syntax_no_curly_braces"] = settings.get("hayaku_CSS_syntax_no_curly_braces", match and not (match.group(2) and match.group(8)) or False)
options["CSS_syntax_no_colons"] = settings.get("hayaku_CSS_syntax_no_colons", match and not match.group(4) or False)
options["CSS_syntax_no_semicolons"] = settings.get("hayaku_CSS_syntax_no_semicolons", match and not match.group(6) or False)
options["CSS_prefixes_disable"] = settings.get("hayaku_CSS_prefixes_disable", False)
options["CSS_prefixes_align"] = settings.get("hayaku_CSS_prefixes_align", True)
options["CSS_prefixes_only"] = settings.get("hayaku_CSS_prefixes_only", [])
options["CSS_prefixes_no_unprefixed"] = settings.get("hayaku_CSS_prefixes_no_unprefixed", False)
return options
class HayakuCommand(sublime_plugin.TextCommand):
def run(self, edit):
regions = self.view.sel()
if len(regions) > 1:
# разобраться с многооконными выборками
# пока что работаем только с одним регионом
for r in regions:
self.view.insert(edit, r, '\t')
return
region = regions[0]
if not region.empty():
# сделать работы с выделенным словом
self.view.insert(edit, region, '\t')
return
cur_pos = region.begin()
start_pos = cur_pos - MAX_SIZE_CSS
if start_pos < 0:
start_pos = 0
probably_abbr = self.view.substr(sublime.Region(start_pos, cur_pos))
match = ABBR_REGEX.search(probably_abbr)
if match is None:
self.view.insert(edit, cur_pos, '\t')
return
abbr = match.group(1)
args = extract(abbr)
if not args:
return
get_hayaku_options(self)
options = get_hayaku_options(self)
template = make_template(args, options)
if template is None:
return
new_cur_pos = cur_pos-len(abbr)
assert cur_pos-len(abbr) >= 0
self.view.erase(edit, sublime.Region(new_cur_pos, cur_pos))
self.view.run_command("insert_snippet", {"contents": template})
WHITE_SPACE_FINDER = re.compile(r'^(\s*)[-\w].*')
class HayakuAddLineCommand(sublime_plugin.TextCommand):
def run(self, edit):
regions = self.view.sel()
if len(regions) > 1:
align_regions = (self.view.line(r) for r in regions)
strings = (self.view.substr(r) for r in align_regions)
finders = (WHITE_SPACE_FINDER.search(s) for s in strings)
min_size = min(len(g.group(1)) for g in finders if g is not None)
max_pos = max(r.end() for r in regions)
self.view.sel().clear()
self.view.sel().add(sublime.Region(max_pos, max_pos))
self.view.run_command('insert', {"characters": "\n"})
erase_region = self.view.line(self.view.sel()[0])
reg = sublime.Region(erase_region.a + min_size, erase_region.b)
self.view.erase(edit, reg)
else:
self.view.run_command('insert', {"characters": "\n"})
OPERATION_TABLE = {
"up": partial(operator.add, 1),
"down": partial(operator.add, -1),
"shift_up": partial(operator.add, 10),
"shift_down": partial(operator.add, -10),
"alt_up": partial(operator.add, 0.1),
"alt_down": partial(operator.add, -0.1),
}
# Изменяет число по сочетаниям ctrl/alt/shift + up/down
class HayakuChangeNumberCommand(sublime_plugin.TextCommand):
def run(self, edit, key):
# поиск текущей позиции в файле
regions = self.view.sel()
if len(regions) > 1:
# разобраться с многооконными выборками
# пока что работаем только с одним регионом
for r in regions:
self.view.insert(edit, r, '\t')
return
region = regions[0]
if not region.empty():
# сделать работы с выделенным словом
self.view.insert(edit, region, '\t')
return
cur_pos = region.begin()
# Буферы для чисел до и после курсора
before_buf = []
after_buf = []
# считывает линию и текущую позицию в куросора в строке
line_region = self.view.line(cur_pos)
line = self.view.substr(line_region)
row, col = self.view.rowcol(cur_pos)
for i in range(col-1, 0-1, -1):
if line[i].isdigit() or line[i] in ('.', '-'):
before_buf.append(line[i])
else:
break
before_buf = before_buf[::-1]
for i in range(col, len(line)):
if line[i].isdigit() or line[i] in ('.', '-'):
after_buf.append(line[i])
else:
break
start_pos_offset = len(before_buf)
end_pos_offset = len(after_buf)
# прочитать число
total_buf = before_buf + after_buf
buf = u''.join(total_buf)
value = None
try:
value = float(buf)
value = int(buf)
except ValueError:
if value is None:
return
# Расчёт нового значения
operation = OPERATION_TABLE[key]
new_value = operation(value)
# Замена региона с числом
start_pos = cur_pos - start_pos_offset
end_pos = cur_pos + end_pos_offset
replace_region = sublime.Region(start_pos, end_pos)
self.view.replace(edit, replace_region, str(new_value))
# установить курсор на место
self.view.sel().clear()
self.view.sel().add(sublime.Region(cur_pos, cur_pos))