-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathalign_cursor.py
More file actions
91 lines (65 loc) · 2.48 KB
/
align_cursor.py
File metadata and controls
91 lines (65 loc) · 2.48 KB
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
"""
This is hereby released completely and irrevocably into the Public Domain.
- Joshua Landau <joshua@landau.ws>
"""
import sublime, sublime_plugin
class AlignCursorsCommand(sublime_plugin.TextCommand):
"""
Align the cursors to the first cursor, using the characters in the selection
if possible, otherwise using the characters on either side of the cursor.
It will extend if any cursor is a selection, otherwise it will move.
"""
def run(self, edit):
selections = self.view.sel()
if not selections:
return
first_selection, *rest = selections
selections.clear()
selections.add(first_selection)
first_selection_line = self.view.line(first_selection.b)
first_selection_line_chars = self.view.substr(first_selection_line)
row, column = self.view.rowcol(first_selection.b)
# Get where each cursor aims to be
if first_selection:
str_end = first_selection.end() - first_selection_line.begin()
target = self.view.substr(first_selection)
# I can't handle this!
# What's a good, easy-to understand behaviour?
# Exactly - there isn't one.
if "\n" in target:
return
side = "left" if first_selection.a > first_selection.b else "right"
else:
if column == 0:
target = first_selection_line_chars[:1]
side = "left"
elif column == len(first_selection_line):
target = first_selection_line_chars[-1:]
side = "right"
else:
target = first_selection_line_chars[column-1:column+1]
side = "center"
str_end = column + 1
# str.startswith(sub, start) == str[start:].startswith(sub)
# This method accepts overlapping substrings so is better than str.count
possible_indexes = range(len(first_selection_line_chars) - len(target) + 1)
count = sum(first_selection_line_chars.startswith(target, i, str_end) for i in possible_indexes)
# Put them there
for selection in rest:
line = self.view.line(selection.b)
line_chars = self.view.substr(line)
# Just like with generating count, except a filtered list rather than a boolean generator
possible_indexes = range(len(line_chars) - len(target) + 1)
positions = [i for i in possible_indexes if line_chars.startswith(target, i)]
try:
# Get "count-1"th item, defauling to last
[start] = positions[count-1:count] or positions[-1:]
except ValueError:
continue
start += line.begin()
end = start + len(target)
if side == "left":
start, end = end, start
elif side == "center":
start = end = (start + end) / 2
selections.add(sublime.Region(start, end))