-
Notifications
You must be signed in to change notification settings - Fork 5
/
add_selections.py
181 lines (127 loc) · 4.42 KB
/
add_selections.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
"""
BetterFindAllUnder is an implementation of the same command from
Sublime Selection Tools, https://github.com/simonrad/sublime-selection-tools,
but I didn't use any code from there so licensing issues are void.
This is hereby released completely and irrevocably into the Public Domain.
- Joshua Landau <[email protected]>
"""
import sublime, sublime_plugin
class AddNextCharacterCommand(sublime_plugin.TextCommand):
"""
Select then next character.
This is an additive version of "left" and "right".
"""
def run(self, edit, forward):
selections = self.view.sel()
for selection in list(selections):
if forward:
new = selection.end() + 1
else:
new = selection.begin() - 1
selections.add(sublime.Region(new, new))
class AddNextWordCommand(sublime_plugin.TextCommand):
"""
Select the next word.
This is an additive version of "ctrl+left" and
"ctrl+right".
"""
def run(self, edit, forward):
selections = self.view.sel()
for selection in list(selections):
if forward:
new = selection.end() + 1
while not self.view.classify(new) & sublime.CLASS_WORD_END:
if new >= self.view.size():
break
new += 1
else:
new = selection.begin() - 1
while not self.view.classify(new) & sublime.CLASS_WORD_START:
if new <= 0:
break
new -= 1
selections.add(sublime.Region(new, new))
class AddEndOfLineCommand(sublime_plugin.TextCommand):
"""
Select the end of the line.
This is an additive version of "home" and "end", except
it will always select the very end of the line, never the
indented start as with "home".
Additionally, this will select the line before or after if
the cursor is already on the end of the line.
"""
def run(self, edit, forward):
selections = self.view.sel()
for selection in list(selections):
if forward:
new = selection.end() + 1
while not self.view.classify(new) & sublime.CLASS_LINE_END:
if new >= self.view.size():
break
new += 1
else:
new = selection.begin() - 1
while not self.view.classify(new) & sublime.CLASS_LINE_START:
if new <= 0:
break
new -= 1
selections.add(sublime.Region(new, new))
class AddNextLineCommand(sublime_plugin.TextCommand):
"""
Add the next line above or below.
This is much better than the default implementation, as it doesn't result
in so many clashes. The only cursors to move are those at the very top or
bottom, so you don't get "build-up" when you hit a differently-sized line.
Additionally, blank lines are skipped when there are multiple moving
selections, which prevents them "blending" together on a blank line.
Doesn't work for word-wrap because it's hard yet pointless.
"""
def run(self, edit, forward):
selections = self.view.sel()
row_number = None
moving_selections = []
# Find all selections with maximum or minimum row (depending on the forward argument)
for selection in selections:
row, col = self.view.rowcol(selection.end() if forward else selection.begin())
if row == row_number:
moving_selections.append(selection)
elif row_number is None or (row > row_number) == forward:
moving_selections = [selection]
row_number = row
# Add the selections
for selection in moving_selections:
position = selection.end() if forward else selection.begin()
row, col = self.view.rowcol(position)
# Move up or down, skipping if need be
while True:
row += 1 if forward else -1
line = self.view.line(self.view.text_point(row, 0))
if line or len(moving_selections) == 1:
break
# Set xpos
target_xpos = selection.xpos
if target_xpos == -1:
target_xpos = self.view.text_to_layout(position)[0]
# Move to place closest to xpos
for new in range(line.begin(), line.end()+1):
xpos = self.view.text_to_layout(new)[0]
if xpos >= target_xpos:
prev_xpos = self.view.text_to_layout(new-1)[0]
new -= abs(xpos-target_xpos) > abs(prev_xpos-target_xpos)
break
# Yeah!
selections.add(sublime.Region(new, new, target_xpos))
class BetterFindAllUnderCommand(sublime_plugin.TextCommand):
"""
Works like find_all_under, except works with initial
multiple cursors.
"""
def run(self, edit):
[*old_selections] = selections = self.view.sel()
new_selections = []
for selection in old_selections:
selections.clear()
selections.add(selection)
self.view.window().run_command("find_all_under")
new_selections.extend(self.view.sel())
selections.add_all(new_selections)