-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
indentation.py
179 lines (121 loc) · 4.51 KB
/
indentation.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
import sublime
import sublime_plugin
def get_tab_size(view):
return int(view.settings().get('tab_size', 8))
def normed_indentation_pt(view, sel, non_space=False):
"""
Calculates tab normed `visual` position of sel.begin() relative "
to start of line
\n\t\t\t => normed_indentation_pt => 12
\n \t\t\t => normed_indentation_pt => 12
Different amount of characters, same visual indentation.
"""
tab_size = get_tab_size(view)
pos = 0
ln = view.line(sel)
for pt in range(ln.begin(), ln.end() if non_space else sel.begin()):
ch = view.substr(pt)
if ch == '\t':
pos += tab_size - (pos % tab_size)
elif ch.isspace():
pos += 1
elif non_space:
break
else:
pos += 1
return pos
def compress_column(column):
# "SS\T"
if all(c.isspace() for c in column):
column = '\t'
# "CCSS"
elif column[-1] == ' ':
while column and column[-1] == ' ':
column.pop()
column.append('\t')
# "CC\T"
return column
def line_and_normed_pt(view, pt):
return (view.rowcol(pt)[0], normed_indentation_pt(view, sublime.Region(pt)))
def pt_from_line_and_normed_pt(view, p):
ln, pt = p
i = start_pt = view.text_point(ln, 0)
tab_size = get_tab_size(view)
pos = 0
for i in range(start_pt, start_pt + pt):
ch = view.substr(i)
if ch == '\t':
pos += tab_size - (pos % tab_size)
else:
pos += 1
i += 1
if pos == pt:
break
return i
def save_selections(view, selections=None):
return [[line_and_normed_pt(view, p) for p in (sel.a, sel.b)] for sel in selections or view.sel()]
def region_from_stored_selection(view, stored):
return sublime.Region(*[pt_from_line_and_normed_pt(view, p) for p in stored])
def restore_selections(view, lines_and_pts):
view.sel().clear()
for stored in lines_and_pts:
view.sel().add(region_from_stored_selection(view, stored))
def unexpand(the_string, tab_size, first_line_offset=0, only_leading=True):
lines = the_string.split('\n')
compressed = []
for li, line in enumerate(lines):
pos = 0
if not li:
pos += first_line_offset
rebuilt_line = []
column = []
for i, char in enumerate(line):
if only_leading and not char.isspace():
column.extend(list(line[i:]))
break
column.append(char)
pos += 1
if char == '\t':
pos += tab_size - (pos % tab_size)
if pos % tab_size == 0:
rebuilt_line.extend(compress_column(column))
column = []
rebuilt_line.extend(column)
compressed.append(''.join(rebuilt_line))
return '\n'.join(compressed)
class TabCommand(sublime_plugin.TextCommand):
translate = False
def run(self, edit, set_translate_tabs=False, whole_buffer=True, **kw):
view = self.view
if set_translate_tabs or not self.translate:
view.settings().set('translate_tabs_to_spaces', self.translate)
if whole_buffer or not view.has_non_empty_selection_region():
self.operation_regions = [sublime.Region(0, view.size())]
else:
self.operation_regions = view.sel()
sels = save_selections(view)
visible, = save_selections(view, [view.visible_region()])
self.do(edit, **kw)
restore_selections(view, sels)
visible = region_from_stored_selection(view, visible)
view.show(visible, False)
view.run_command("scroll_lines", {"amount": 1.0})
def is_enabled(self):
return not self.view.settings().get('is_widget')
class ExpandTabs(TabCommand):
translate = True
def do(self, edit, **kw):
view = self.view
tab_size = get_tab_size(view)
for sel in self.operation_regions:
sel = view.line(sel) # TODO: expand tabs with non regular offsets
view.replace(edit, sel, view.substr(sel).expandtabs(tab_size))
class UnexpandTabs(TabCommand):
def do(self, edit, only_leading=True, **kw):
view = self.view
tab_size = get_tab_size(view)
for sel in self.operation_regions:
the_string = view.substr(sel)
first_line_off_set = normed_indentation_pt(view, sel) % tab_size
compressed = unexpand(the_string, tab_size, first_line_off_set, only_leading=only_leading)
view.replace(edit, sel, compressed)