-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathfile_scanner.py
154 lines (119 loc) · 4.84 KB
/
file_scanner.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
import sublime, re
from .view_helper import ViewHelper
class FileScanner:
def __init__(self, view, viewhelper):
self.view = view
self.view_helper = viewhelper
def scan(self, direction = 'forward', indent_offset = 0):
self.indent_offset = indent_offset
if direction == 'forward':
indent_match = self.search(self.search_str(), self.next_point()) or 0
possible_matches = [indent_match]
if indent_offset == 0:
block_match = self.find_last_line_of_block()
possible_matches.append(block_match)
return max(possible_matches)
else:
if self.previous_point() < 0:
end = 0
else:
end = self.previous_point()
indent_match = self.reverse_search(self.search_str(), 0, end)
possible_matches = [indent_match]
if indent_offset == 0:
block_match = self.find_first_line_of_block(end)
possible_matches.append(block_match)
return min(possible_matches)
def adapt_indent(self, indent_str):
tab_size = self.view.settings().get("tab_size")
indent = max(0, len(indent_str) + tab_size * self.indent_offset)
return indent
def search_str(self):
settings = sublime.load_settings("jump_along_indent.sublime-settings")
respect_cursor_position = settings.get("respect_cursor_position")
between_leading_spaces = re.match(r"^\s*$", self.str_to_left()) and re.match(r"^\s+\S+", self.str_to_right())
if respect_cursor_position and between_leading_spaces:
indent = self.adapt_indent(self.str_to_left())
search_str = "^ {0," + str(indent) + "}\S+"
else:
indent = self.adapt_indent(self.leading_spaces())
search_str = "^ {" + str(indent) + "}\S"
return search_str
def str_to_left(self):
view = self.view
sel_pt = self.view_helper.initial_cursor_position()
left_pt = view.line(sel_pt).a
left_region = sublime.Region(left_pt, sel_pt)
return view.substr(left_region)
def str_to_right(self):
view = self.view
sel_pt = self.view_helper.initial_cursor_position()
right_pt = view.line(sel_pt).b
right_region = sublime.Region(sel_pt, right_pt)
return view.substr(right_region)
def leading_spaces(self):
spaces = re.match(r"(\s*)", self.current_line())
return spaces.group(0)
def current_line(self):
view = self.view
line_region = view.line(self.view_helper.initial_cursor_position())
return view.substr(line_region)
def previous_point(self, position = None):
position = position or self.view_helper.initial_cursor_position()
return self.view.line(position).a - 1
def previous_line(self, position = None):
return self.view.rowcol(self.previous_point(position))[0]
def next_point(self, position = None):
position = position or self.view_helper.initial_cursor_position()
return self.view.line(position).b + 1
def next_line(self, position = None):
return self.view.rowcol(self.next_point(position))[0]
def search(self, pattern, start = None, flags = 0):
view = self.view
start = start or self.view_helper.initial_cursor_position()
match = view.find(pattern, start, flags)
if not match.a == -1:
matched_row = view.rowcol(match.begin())[0]
return matched_row
def reverse_search(self, pattern, start = 0, end = -1):
view = self.view
if end == -1:
end = view.size()
end = self.view_helper.find_eol(view.line(end).a)
match = self.find_last_match(pattern, start, end)
if match == None:
return self.view_helper.initial_row()
else:
matched_row = view.rowcol(match.begin())[0]
return matched_row
def find_last_match(self, pattern, start, end):
matches = self.view.find_all(pattern)
filtered_matches = [match for match in matches if match.begin() >= start and match.begin() <= end]
if len(filtered_matches) > 0:
return filtered_matches[-1]
def find_last_line_of_block(self):
view = self.view
matched_line = self.search(self.block_pattern())
last_line = view.rowcol(view.size())[0]
if not matched_line or (matched_line == last_line - 1 and self.block_extends_to_bottom()):
return last_line
else:
return matched_line - 1
def find_first_line_of_block(self, end):
pattern = self.block_pattern() + "|(^\A.*$)"
matched_line = self.reverse_search(pattern, 0, end)
if matched_line == 0 and self.block_extends_to_top():
return 0
else:
return matched_line + 1
def block_extends_to_top(self):
return self.view.find("\A" + self.leading_spaces() + "\S", 0)
def block_extends_to_bottom(self):
return self.view.find("^" + self.leading_spaces() + "\S.*\z", self.next_point())
def block_pattern(self):
pattern = "(^\s*$)"
space_count = len(self.leading_spaces())
if space_count > 0:
pattern += "|(^ {0," + str(space_count - 1) + "}\S+)"
pattern += "|(^ {" + str(space_count + 1) + ",}\S+)"
return pattern