-
Notifications
You must be signed in to change notification settings - Fork 34
/
filter.py
157 lines (135 loc) · 5.96 KB
/
filter.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
import itertools
import sublime
import sublime_plugin
settings_path = 'Filter Lines.sublime-settings'
class PromptFilterToLinesCommand(sublime_plugin.WindowCommand):
def run(self, search_type='string', invert_search=False):
self._run(search_type, 'filter_to_lines', 'Filter', invert_search)
def _run(self, search_type, filter_command, filter_verb, invert_search):
self.load_settings()
self.filter_command = filter_command
self.search_type = search_type
self.invert_search = invert_search
if search_type == 'string':
prompt = "%s to lines %s: " % (
filter_verb,
'not containing string' if self.invert_search else 'containing string')
else:
prompt = "%s to lines %s: " % (
filter_verb,
'not matching regex' if self.invert_search else 'matching regex')
if not self.search_text:
view = self.window.active_view()
first = view.sel()[0] # first region (or point)
region = first if first.size() else view.word(first.begin())
word = view.substr(region)
self.search_text = word
sublime.active_window().show_input_panel(
prompt, self.search_text, self.on_search_text_entered, None, None)
def on_search_text_entered(self, search_text):
self.search_text = search_text
self.save_settings()
if self.window.active_view():
self.window.active_view().run_command(
self.filter_command, {
"needle": self.search_text,
"search_type": self.search_type,
"invert_search": self.invert_search
}
)
def load_settings(self):
self.settings = sublime.load_settings(settings_path)
self.search_text = ""
if self.settings.get('preserve_search', True):
self.search_text = self.settings.get('latest_search', '')
def save_settings(self):
if self.settings.get('preserve_search', True):
self.settings.set('latest_search', self.search_text)
class FilterToLinesCommand(sublime_plugin.TextCommand):
def run(self, edit, needle, search_type, invert_search):
settings = sublime.load_settings(settings_path)
flags = self.get_search_flags(search_type, settings)
lines = itertools.groupby(
self.view.find_all(needle, flags), self.view.line)
lines = [l for l, _ in lines]
self.line_numbers = settings.get('line_numbers', False)
# exclude title prefix in title name length calculation
self.title_length = settings.get('new_tab_title_max_length', 20) - 16
self.new_tab = settings.get('create_new_tab', True)
self.needle = needle
self.invert_search = invert_search ^ (not self.new_tab)
self.show_filtered_lines(edit, lines)
def get_search_flags(self, search_type, settings):
flags = 0
if search_type == 'string':
flags = sublime.LITERAL
if not settings.get('case_sensitive_string_search', False):
flags = flags | sublime.IGNORECASE
elif search_type == 'regex':
if not settings.get('case_sensitive_regex_search', False):
flags = sublime.IGNORECASE
return flags
def show_filtered_lines(self, edit, lines):
if self.invert_search:
filtered_line_numbers = [
self.view.rowcol(line.begin())[0] for line in lines
]
lines = self.view.lines(sublime.Region(0, self.view.size()))
for line_number in reversed(filtered_line_numbers):
del lines[line_number]
if self.new_tab:
text = '\n'.join(
[self.prepare_output_line(l) for l in lines]
)
self.create_new_tab(text, self.needle)
else:
for line in reversed(lines):
self.view.erase(edit, self.view.full_line(line))
def create_new_tab(self, text, title=''):
results_view = self.view.window().new_file()
title = '%s...' % (title[:self.title_length]) if len(
title) > self.title_length else title
results_view.set_name('Filter Results: %s' % (title))
results_view.set_scratch(True)
results_view.settings().set(
'word_wrap', self.view.settings().get('word_wrap'))
results_view.run_command(
'append',
{'characters': text, 'force': True, 'scroll_to_end': False}
)
results_view.set_syntax_file(self.view.settings().get('syntax'))
def prepare_output_line(self, line):
if self.line_numbers and not self.invert_search:
line_number = self.view.rowcol(line.begin())[0]
return '%5d: %s' % (line_number, self.view.substr(line))
else:
return self.view.substr(line)
class PromptFoldToLinesCommand(PromptFilterToLinesCommand):
def run(self, search_type='string', invert_search=False):
self._run(search_type, "fold_to_lines", "Fold", invert_search)
class FoldToLinesCommand(FilterToLinesCommand):
def show_filtered_lines(self, edit, lines):
source_lines = self.view.lines(sublime.Region(0, self.view.size()))
filtered_line_numbers = {
self.view.rowcol(line.begin())[0] for line in lines
}
regions = []
region = None
for line in source_lines:
matched = (
self.view.rowcol(
line.begin()
)[0] in filtered_line_numbers) ^ self.invert_search
if matched:
if region:
regions.append(region)
region = None
else:
if region:
region = region.cover(line)
else:
region = sublime.Region(line.begin(), line.end())
if region:
regions.append(region)
if regions:
self.view.fold(regions)