ruby-zoom 3.5.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/z +189 -147
- data/bin/zc +189 -147
- data/bin/zf +189 -147
- data/bin/zg +189 -147
- data/bin/zl +189 -147
- data/bin/zr +189 -147
- data/lib/zoom.rb +48 -604
- data/lib/zoom/cache.rb +253 -0
- data/lib/zoom/cache/result.rb +49 -0
- data/lib/zoom/config.rb +156 -0
- data/lib/zoom/editor.rb +121 -0
- data/lib/zoom/error.rb +6 -6
- data/lib/zoom/error/{executable_not_found_error.rb → executable_not_found.rb} +1 -3
- data/lib/zoom/error/invalid_color.rb +5 -0
- data/lib/zoom/error/{invalid_tag_error.rb → invalid_tag.rb} +1 -3
- data/lib/zoom/error/{profile_class_unknown_error.rb → profile_class_unknown.rb} +1 -3
- data/lib/zoom/error/{profile_does_not_exist_error.rb → profile_does_not_exist.rb} +1 -3
- data/lib/zoom/error/profile_not_named.rb +7 -0
- data/lib/zoom/profile.rb +162 -66
- data/lib/zoom/profile/ack.rb +5 -30
- data/lib/zoom/profile/ag.rb +2 -37
- data/lib/zoom/profile/find.rb +2 -25
- data/lib/zoom/profile/grep.rb +2 -44
- data/lib/zoom/profile/passwords.rb +27 -59
- data/lib/zoom/profile/pt.rb +2 -26
- data/lib/zoom/profile_manager.rb +49 -0
- data/lib/zoom/wish/add_wish.rb +58 -0
- data/lib/zoom/wish/color_wish.rb +147 -0
- data/lib/zoom/wish/copy_wish.rb +57 -0
- data/lib/zoom/wish/delete_wish.rb +51 -0
- data/lib/zoom/wish/edit_wish.rb +130 -0
- data/lib/zoom/wish/editor_wish.rb +31 -0
- data/lib/zoom/wish/list_wish.rb +58 -0
- data/lib/zoom/wish/rename_wish.rb +69 -0
- data/lib/zoom/wish/reset_wish.rb +40 -0
- data/lib/zoom/wish/use_wish.rb +61 -0
- metadata +118 -24
- data/lib/string.rb +0 -48
- data/lib/zoom/error/profile_already_exists_error.rb +0 -8
- data/lib/zoom/error/profile_can_not_be_modified_error.rb +0 -8
data/bin/zr
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require "djinni"
|
4
|
+
require "fileutils"
|
5
|
+
require "hilighter"
|
3
6
|
require "optparse"
|
4
7
|
require "zoom"
|
5
8
|
|
@@ -8,7 +11,9 @@ class ZoomExit
|
|
8
11
|
INVALID_OPTION = 1
|
9
12
|
INVALID_ARGUMENT = 2
|
10
13
|
MISSING_ARGUMENT = 3
|
11
|
-
|
14
|
+
EXTRA_ARGUMENTS = 4
|
15
|
+
EXCEPTION = 5
|
16
|
+
AMBIGUOUS_ARGUMENT = 6
|
12
17
|
end
|
13
18
|
|
14
19
|
def parse(args)
|
@@ -16,76 +21,50 @@ def parse(args)
|
|
16
21
|
options["action"] = "exec"
|
17
22
|
options["cache_file"] = nil
|
18
23
|
options["use"] = nil
|
24
|
+
options["verbose"] = false
|
19
25
|
|
20
|
-
info =
|
21
|
-
"
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
25
|
-
"
|
26
|
-
"
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"one terminal and jump to a
|
31
|
-
"any directory!"
|
26
|
+
info = [
|
27
|
+
"Do you like to search through code using ag, ack, grep, or",
|
28
|
+
"pt? Good! This tool is for you! Zoom adds some convenience",
|
29
|
+
"to ag/ack/grep/pt by allowing you to quickly open your",
|
30
|
+
"search results in your editor of choice. When looking at",
|
31
|
+
"large code-bases, it can be a pain to have to scroll to",
|
32
|
+
"find the filename of each result. Zoom prints a tag number",
|
33
|
+
"in front of each result that ag/ack/grep/pt outputs. Then",
|
34
|
+
"you can quickly open that tag number with Zoom to jump",
|
35
|
+
"straight to the source. Zoom is even persistent across all",
|
36
|
+
"your sessions! You can search in one terminal and jump to a",
|
37
|
+
"tag in another terminal from any directory!"
|
38
|
+
].join(" ")
|
32
39
|
|
33
40
|
parser = OptionParser.new do |opts|
|
41
|
+
opts.summary_width = 25
|
42
|
+
|
34
43
|
opts.banner =
|
35
|
-
"Usage: #{File.basename($0)} [OPTIONS]
|
44
|
+
"Usage: #{File.basename($0)} [OPTIONS] [pattern]"
|
36
45
|
|
37
|
-
opts.on(
|
38
|
-
|
39
|
-
|
40
|
-
"
|
41
|
-
) do |name|
|
42
|
-
options["action"] = "add"
|
43
|
-
options["use"] = name
|
46
|
+
opts.on("", "DESCRIPTION")
|
47
|
+
|
48
|
+
info.scan(/\S.{0,76}\S(?=\s|$)|\S+/).each do |line|
|
49
|
+
opts.on(" #{line}")
|
44
50
|
end
|
45
51
|
|
52
|
+
opts.on("", "OPTIONS")
|
53
|
+
|
46
54
|
opts.on("-c", "--cache", "Show previous results") do
|
47
55
|
options["action"] = "cache"
|
48
56
|
end
|
49
57
|
|
50
58
|
opts.on(
|
59
|
+
"-o",
|
51
60
|
"--cache-file=FILE",
|
52
|
-
"Use
|
61
|
+
"Use alternate cache"
|
53
62
|
) do |file|
|
54
63
|
options["cache_file"] = file
|
55
64
|
end
|
56
65
|
|
57
|
-
opts.on(
|
58
|
-
"
|
59
|
-
"--delete=NAME",
|
60
|
-
"Delete profile with specified name"
|
61
|
-
) do |name|
|
62
|
-
options["action"] = "delete"
|
63
|
-
options["use"] = name
|
64
|
-
end
|
65
|
-
|
66
|
-
opts.on(
|
67
|
-
"-e",
|
68
|
-
"--edit=NAME",
|
69
|
-
"Edit profile with specified name"
|
70
|
-
) do |name|
|
71
|
-
options["action"] = "edit"
|
72
|
-
options["use"] = name
|
73
|
-
end
|
74
|
-
|
75
|
-
opts.on(
|
76
|
-
"--editor=EDITOR",
|
77
|
-
"Use the specified editor"
|
78
|
-
) do |editor|
|
79
|
-
options["action"] = "editor"
|
80
|
-
options["use"] = editor
|
81
|
-
end
|
82
|
-
|
83
|
-
opts.on("--examples", "Show some examples") do
|
84
|
-
options["action"] = "examples"
|
85
|
-
end
|
86
|
-
|
87
|
-
opts.on("--find", "Use the zoom_find profile") do
|
88
|
-
options["use"] = "zoom_find"
|
66
|
+
opts.on("--find", "Use built-in find profile") do
|
67
|
+
options["use"] = "find"
|
89
68
|
end
|
90
69
|
|
91
70
|
opts.on(
|
@@ -106,65 +85,78 @@ def parse(args)
|
|
106
85
|
options["action"] = "list_profiles"
|
107
86
|
end
|
108
87
|
|
109
|
-
opts.on(
|
110
|
-
"
|
111
|
-
"List profile names for completion functions"
|
112
|
-
) do
|
113
|
-
options["action"] = "list_profile_names"
|
88
|
+
opts.on("-r", "--repeat", "Repeat last Zoom command") do
|
89
|
+
options["action"] = "repeat"
|
114
90
|
end
|
115
91
|
|
116
|
-
opts.on(
|
117
|
-
"
|
118
|
-
"List tags for completion functions"
|
119
|
-
) do
|
120
|
-
options["action"] = "list_tags"
|
92
|
+
opts.on("-u", "--use=NAME", "Run specified profile") do |name|
|
93
|
+
options["use"] = name
|
121
94
|
end
|
122
95
|
|
123
|
-
opts.on(
|
124
|
-
"
|
125
|
-
"Treat Zoom as a pager (internal use only)"
|
126
|
-
) do
|
127
|
-
options["action"] = "pager"
|
96
|
+
opts.on("-w", "--which", "Display current profile") do
|
97
|
+
options["action"] = "which"
|
128
98
|
end
|
129
99
|
|
130
|
-
opts.on("
|
131
|
-
|
100
|
+
opts.on("", "CONFIGURE_OPTIONS")
|
101
|
+
|
102
|
+
opts.on(
|
103
|
+
"--configure",
|
104
|
+
"Open prompt to edit profiles"
|
105
|
+
) do
|
106
|
+
options["action"] = "configure"
|
132
107
|
end
|
133
108
|
|
134
|
-
opts.on("--rc", "Create default
|
109
|
+
opts.on("--rc", "Create default config file") do
|
135
110
|
options["action"] = "rc"
|
136
111
|
end
|
137
112
|
|
113
|
+
opts.on("", "MISC_OPTIONS")
|
114
|
+
|
138
115
|
opts.on(
|
139
|
-
"--
|
140
|
-
"
|
141
|
-
) do
|
142
|
-
options["action"] = "
|
143
|
-
options["rename"] = name
|
116
|
+
"--list-profile-names",
|
117
|
+
"List profile names for completion functions"
|
118
|
+
) do
|
119
|
+
options["action"] = "list_profile_names"
|
144
120
|
end
|
145
121
|
|
146
122
|
opts.on(
|
147
|
-
"-
|
148
|
-
"
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
123
|
+
"--list-tags",
|
124
|
+
"List tags for completion functions"
|
125
|
+
) do
|
126
|
+
options["action"] = "list_tags"
|
127
|
+
end
|
128
|
+
|
129
|
+
opts.on("--nocolor", "Disable colorized output") do
|
130
|
+
Hilighter.disable
|
153
131
|
end
|
154
132
|
|
155
133
|
opts.on(
|
156
|
-
"-
|
157
|
-
"--
|
158
|
-
"
|
159
|
-
) do
|
160
|
-
options["
|
134
|
+
"-v",
|
135
|
+
"--verbose",
|
136
|
+
"Show backtrace when error occurs"
|
137
|
+
) do
|
138
|
+
options["verbose"] = true
|
161
139
|
end
|
162
140
|
|
163
|
-
opts.on("
|
164
|
-
options["action"] = "
|
141
|
+
opts.on("--version", "Show version") do
|
142
|
+
options["action"] = "version"
|
165
143
|
end
|
166
144
|
|
167
|
-
opts.on(
|
145
|
+
opts.on(
|
146
|
+
"",
|
147
|
+
"EXAMPLES",
|
148
|
+
" Execute default profile:",
|
149
|
+
" $ z PATTERN",
|
150
|
+
"",
|
151
|
+
" Execute specified profile:",
|
152
|
+
" $ z -u grep PATTERN",
|
153
|
+
"",
|
154
|
+
" Pass additional flags to default profile:",
|
155
|
+
" $ z -- -A 3 PATTERN",
|
156
|
+
"",
|
157
|
+
" Open tags:",
|
158
|
+
" $ zg 10,20,30-40"
|
159
|
+
)
|
168
160
|
end
|
169
161
|
|
170
162
|
begin
|
@@ -181,13 +173,17 @@ def parse(args)
|
|
181
173
|
puts e.message
|
182
174
|
puts parser
|
183
175
|
exit ZoomExit::MISSING_ARGUMENT
|
176
|
+
rescue OptionParser::AmbiguousOption => e
|
177
|
+
puts e.message
|
178
|
+
puts parser
|
179
|
+
exit ZoomExit::AMBIGUOUS_ARGUMENT
|
184
180
|
end
|
185
181
|
|
186
182
|
case File.basename($0)
|
187
183
|
when "zc"
|
188
184
|
options["action"] = "cache"
|
189
185
|
when "zf"
|
190
|
-
options["use"] = "
|
186
|
+
options["use"] = "find"
|
191
187
|
when "zg"
|
192
188
|
options["action"] = "go"
|
193
189
|
options["use"] = args[0]
|
@@ -196,7 +192,7 @@ def parse(args)
|
|
196
192
|
when "zr"
|
197
193
|
options["action"] = "repeat"
|
198
194
|
when "z"
|
199
|
-
#
|
195
|
+
# Do nothing, this is the normal usage
|
200
196
|
else
|
201
197
|
options["use"] = File.basename($0)
|
202
198
|
end
|
@@ -208,80 +204,126 @@ end
|
|
208
204
|
|
209
205
|
options = parse(ARGV)
|
210
206
|
|
211
|
-
zoom = Zoom.new(options["cache_file"])
|
212
207
|
begin
|
208
|
+
if (options["action"] == "rc")
|
209
|
+
FileUtils.rm_f(Pathname.new("~/.zoomrc").expand_path)
|
210
|
+
end
|
211
|
+
|
212
|
+
zoom = Zoom.new(options["cache_file"], nil, !Hilighter.disable?)
|
213
|
+
|
213
214
|
case options["action"]
|
214
|
-
when "add"
|
215
|
-
zoom.interactive_add_profile(options["use"])
|
216
215
|
when "cache"
|
217
|
-
zoom.
|
218
|
-
when "
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
"
|
232
|
-
|
233
|
-
"",
|
234
|
-
"Execute the current profile:",
|
235
|
-
" $ z PATTERN",
|
236
|
-
"",
|
237
|
-
"Repeat the previous Zoom command:",
|
238
|
-
" $ z --repeat",
|
239
|
-
"",
|
240
|
-
"Pass additional flags to the choosen operator:",
|
241
|
-
" $ z -- -A 3 PATTERN",
|
242
|
-
"",
|
243
|
-
"Open a tag:",
|
244
|
-
" $ z --go 10",
|
245
|
-
"",
|
246
|
-
"Open multiple tags:",
|
247
|
-
" $ z --go 10,20,30-40"
|
248
|
-
].join("\n")
|
216
|
+
zoom.cache.shortcut(zoom.config)
|
217
|
+
when "configure"
|
218
|
+
Zoom.hilight(false)
|
219
|
+
djinni = Djinni.new
|
220
|
+
djinni.load_wishes(
|
221
|
+
"#{File.dirname(__FILE__)}/../lib/zoom/wish"
|
222
|
+
)
|
223
|
+
djinni.prompt(
|
224
|
+
{
|
225
|
+
"cache" => zoom.cache,
|
226
|
+
"config" => zoom.config,
|
227
|
+
"prompt_color" => "white",
|
228
|
+
"zoom" => zoom
|
229
|
+
},
|
230
|
+
"zoom(#{zoom.config.current_profile_name})> ".white
|
231
|
+
)
|
249
232
|
when "go"
|
250
|
-
|
233
|
+
if (options["use"])
|
234
|
+
results = zoom.cache.get_results(options["use"])
|
235
|
+
zoom.open(results)
|
236
|
+
end
|
251
237
|
when "list_profiles"
|
252
|
-
zoom.
|
238
|
+
profiles = zoom.config.get_profiles
|
239
|
+
profiles.keys.sort do |a, b|
|
240
|
+
a.downcase <=> b.downcase
|
241
|
+
end.each do |name|
|
242
|
+
if (name == zoom.config.current_profile_name)
|
243
|
+
print "*".red if (Zoom.hilight?)
|
244
|
+
print "*" if (!Zoom.hilight?)
|
245
|
+
end
|
246
|
+
|
247
|
+
lines = profiles[name].to_s.scan(
|
248
|
+
/\S.{0,76}\S(?=\s|$)|\S+/
|
249
|
+
)
|
250
|
+
puts lines.delete_at(0)
|
251
|
+
lines.each do |line|
|
252
|
+
puts " #{line}"
|
253
|
+
end
|
254
|
+
end
|
253
255
|
when "list_profile_names"
|
254
|
-
zoom.
|
256
|
+
puts zoom.config.get_profile_names
|
255
257
|
when "list_tags"
|
256
|
-
zoom.
|
257
|
-
when "pager"
|
258
|
-
zoom.pager
|
258
|
+
puts zoom.cache.available_tags
|
259
259
|
when "repeat"
|
260
260
|
zoom.repeat
|
261
261
|
when "rc"
|
262
|
-
zoom.
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
when "switch"
|
269
|
-
zoom.switch_profile(options["use"])
|
262
|
+
zoom.config.default_config
|
263
|
+
zoom.cache.clear
|
264
|
+
when "version"
|
265
|
+
__FILE__.match(/ruby-zoom-(\d+\.\d+\.\d+)/) do |m|
|
266
|
+
puts m[1]
|
267
|
+
end
|
270
268
|
when "which"
|
271
|
-
zoom.
|
269
|
+
name = zoom.config.current_profile_name
|
270
|
+
profile = zoom.config.get_profiles[name]
|
271
|
+
|
272
|
+
print "*".red if (Zoom.hilight?)
|
273
|
+
print "*" if (!Zoom.hilight?)
|
274
|
+
|
275
|
+
lines = profile.to_s.scan(/\S.{0,76}\S(?=\s|$)|\S+/)
|
276
|
+
puts lines.delete_at(0)
|
277
|
+
lines.each do |line|
|
278
|
+
puts " #{line}"
|
279
|
+
end
|
272
280
|
else
|
273
|
-
# Search and
|
274
|
-
zoom.
|
281
|
+
# Search and cache results
|
282
|
+
zoom.run(
|
275
283
|
options["use"],
|
276
284
|
options["subargs"],
|
277
285
|
options["pattern"]
|
278
286
|
)
|
279
287
|
end
|
288
|
+
rescue SystemExit
|
289
|
+
# Quit from djinni
|
290
|
+
# Exit gracefully
|
280
291
|
rescue Zoom::Error => e
|
281
|
-
puts e.message
|
292
|
+
$stderr.puts e.message
|
293
|
+
if (options["verbose"])
|
294
|
+
e.backtrace.each do |line|
|
295
|
+
$stderr.puts line.yellow
|
296
|
+
end
|
297
|
+
end
|
282
298
|
exit ZoomExit::EXCEPTION
|
299
|
+
rescue Interrupt
|
300
|
+
# ^C
|
301
|
+
# Exit gracefully
|
302
|
+
rescue Errno::EPIPE
|
303
|
+
# Do nothing. This can happen if piping to another program such as
|
304
|
+
# less. Usually if less is closed before Zoom is done with STDOUT.
|
283
305
|
rescue Exception => e
|
284
|
-
puts
|
306
|
+
$stderr.puts
|
307
|
+
$stderr.puts "Oops! Looks like an error has occured! Try " \
|
308
|
+
"resetting your configs with the"
|
309
|
+
$stderr.puts "following comand:"
|
310
|
+
$stderr.puts
|
311
|
+
$stderr.puts " z --rc"
|
312
|
+
$stderr.puts
|
313
|
+
$stderr.puts "If the error persists, file a bug at:"
|
314
|
+
$stderr.puts
|
315
|
+
$stderr.puts " https://gitlab.com/mjwhitta/zoom/issues"
|
316
|
+
$stderr.puts
|
317
|
+
$stderr.puts "Maybe the message below will help. If not, you " \
|
318
|
+
"can use the --verbose flag to get"
|
319
|
+
$stderr.puts "a backtrace."
|
320
|
+
|
321
|
+
$stderr.puts e.message.white.on_red
|
322
|
+
if (options["verbose"])
|
323
|
+
e.backtrace.each do |line|
|
324
|
+
$stderr.puts line.yellow
|
325
|
+
end
|
326
|
+
end
|
285
327
|
exit ZoomExit::EXCEPTION
|
286
328
|
end
|
287
329
|
exit ZoomExit::GOOD
|
data/lib/zoom.rb
CHANGED
@@ -1,637 +1,81 @@
|
|
1
|
-
require "io/wait"
|
2
|
-
require "json"
|
3
1
|
require "pathname"
|
4
|
-
require "string"
|
5
2
|
|
6
3
|
class Zoom
|
7
|
-
|
8
|
-
|
9
|
-
clas,
|
10
|
-
operator = nil,
|
11
|
-
flags = nil,
|
12
|
-
envprepend = nil,
|
13
|
-
append = nil
|
14
|
-
)
|
15
|
-
if (@profiles.has_key?(name))
|
16
|
-
raise Zoom::Error::ProfileAlreadyExistsError.new(name)
|
17
|
-
end
|
18
|
-
|
19
|
-
default_class = nil
|
20
|
-
begin
|
21
|
-
default_class = Zoom::Profile.profile_by_name(clas).new
|
22
|
-
rescue NameError => e
|
23
|
-
raise Zoom::Error::ProfileClassUnknownError.new(clas)
|
24
|
-
end
|
4
|
+
attr_reader :cache
|
5
|
+
attr_reader :config
|
25
6
|
|
26
|
-
|
27
|
-
|
28
|
-
default_class,
|
29
|
-
operator,
|
30
|
-
flags,
|
31
|
-
envprepend,
|
32
|
-
append
|
33
|
-
)
|
7
|
+
def self.hilight(hilight = true)
|
8
|
+
@@hilight = hilight
|
34
9
|
end
|
35
10
|
|
36
|
-
def
|
37
|
-
|
11
|
+
def self.hilight?
|
12
|
+
@@hilight ||= false
|
13
|
+
return @@hilight
|
38
14
|
end
|
39
15
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
16
|
+
def initialize(cache = nil, rc = nil, hilight = false)
|
17
|
+
@@hilight = hilight
|
18
|
+
@cache = Zoom::Cache.new(cache)
|
19
|
+
@config = Zoom::Config.new(rc)
|
45
20
|
|
46
|
-
|
47
|
-
|
21
|
+
# Prioritize false, so only reassign if true
|
22
|
+
@@hilight = @config.hilight if (hilight)
|
48
23
|
end
|
49
24
|
|
50
|
-
def
|
51
|
-
|
52
|
-
default_zoomrc
|
53
|
-
end
|
54
|
-
|
55
|
-
def default_zoominfo
|
56
|
-
info = Hash.new
|
57
|
-
info["profile"] = "default"
|
58
|
-
|
59
|
-
# Reset last command to be empty
|
60
|
-
info["last_command"] = Hash.new
|
25
|
+
def open(results)
|
26
|
+
return if (results.nil? || results.empty?)
|
61
27
|
|
62
|
-
|
63
|
-
|
64
|
-
|
28
|
+
# All results should be from the same profile
|
29
|
+
profile = @config.get_profile(results[0].profile_name)
|
30
|
+
profile.go(@config.editor, results)
|
65
31
|
end
|
66
32
|
|
67
|
-
def
|
68
|
-
|
69
|
-
profiles = Hash.new
|
70
|
-
|
71
|
-
all = nil
|
72
|
-
ag = nil
|
73
|
-
pt = nil
|
74
|
-
ack = nil
|
75
|
-
|
76
|
-
# Default ag profiles
|
77
|
-
if (ScoobyDoo.where_are_you("ag"))
|
78
|
-
ag = Zoom::Profile::Ag.new
|
79
|
-
all = Zoom::Profile::Ag.new(nil, "-u")
|
80
|
-
end
|
81
|
-
|
82
|
-
# Default pt profiles
|
83
|
-
if (ScoobyDoo.where_are_you("pt"))
|
84
|
-
pt = Zoom::Profile::Pt.new
|
85
|
-
all ||= Zoom::Profile::Pt.new(nil, "--color -SeU")
|
86
|
-
end
|
33
|
+
def repeat
|
34
|
+
return if (@cache.empty?)
|
87
35
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
36
|
+
run(
|
37
|
+
@cache.profile_name,
|
38
|
+
@cache.args,
|
39
|
+
@cache.pattern
|
92
40
|
)
|
93
|
-
ack = Zoom::Profile::Ack.new
|
94
|
-
end
|
95
|
-
|
96
|
-
# Default grep profile (emulate ag/ack as much as possible)
|
97
|
-
grep = Zoom::Profile::Grep.new
|
98
|
-
all ||= Zoom::Profile::Grep.new(nil, "--color=always -EHinR")
|
99
|
-
|
100
|
-
# Create default profile
|
101
|
-
if (ag)
|
102
|
-
default = ag
|
103
|
-
elsif (pt)
|
104
|
-
default = pt
|
105
|
-
elsif (ack)
|
106
|
-
default = ack
|
107
|
-
else
|
108
|
-
default = grep
|
109
|
-
end
|
110
|
-
|
111
|
-
# Create find profile
|
112
|
-
find = Zoom::Profile::Find.new
|
113
|
-
|
114
|
-
# Put profiles into rc
|
115
|
-
profiles["ack"] = ack if (ack)
|
116
|
-
profiles["ag"] = ag if (ag)
|
117
|
-
profiles["all"] = all if (all)
|
118
|
-
profiles["default"] = default
|
119
|
-
profiles["grep"] = grep
|
120
|
-
profiles["passwords"] = Zoom::Profile::Passwords.new
|
121
|
-
profiles["pt"] = pt if (pt)
|
122
|
-
profiles["zoom_find"] = find
|
123
|
-
rc["profiles"] = profiles
|
124
|
-
|
125
|
-
# Default editor (use $EDITOR)
|
126
|
-
rc["editor"] = ""
|
127
|
-
|
128
|
-
File.open(@rc_file, "w") do |file|
|
129
|
-
file.write(JSON.pretty_generate(rc))
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def delete_profile(name)
|
134
|
-
if (!@profiles.has_key?(name))
|
135
|
-
raise Zoom::Error::ProfileDoesNotExistError.new(name)
|
136
|
-
end
|
137
|
-
|
138
|
-
if ((name == "default") || @profiles[name].immutable)
|
139
|
-
raise Zoom::Error::ProfileCanNotBeModifiedError.new(name)
|
140
|
-
end
|
141
|
-
|
142
|
-
@profiles.delete(name)
|
143
|
-
write_zoomrc
|
144
|
-
|
145
|
-
if (name == @info["profile"])
|
146
|
-
@info["profile"] = "default"
|
147
|
-
write_zoominfo
|
148
|
-
end
|
149
41
|
end
|
150
42
|
|
151
|
-
def
|
152
|
-
|
153
|
-
profile = nil,
|
154
|
-
operator = nil,
|
155
|
-
flags = nil,
|
156
|
-
envprepend = nil,
|
157
|
-
append = nil
|
158
|
-
)
|
159
|
-
profile = @profiles[name] if (profile.nil?)
|
43
|
+
def run(prof_name, args, pattern)
|
44
|
+
prof_name = @config.current_profile_name if (prof_name.nil?)
|
160
45
|
|
161
|
-
if (
|
162
|
-
raise Zoom::Error::
|
46
|
+
if (!@config.has_profile?(prof_name))
|
47
|
+
raise Zoom::Error::ProfileDoesNotExist.new(prof_name)
|
163
48
|
end
|
164
49
|
|
165
|
-
|
166
|
-
raise Zoom::Error::ProfileCanNotBeModifiedError.new(name)
|
167
|
-
end
|
168
|
-
|
169
|
-
profile.operator(operator) if (operator)
|
170
|
-
profile.flags(flags) if (flags)
|
171
|
-
profile.prepend(envprepend) if (envprepend)
|
172
|
-
profile.append(append) if (append)
|
173
|
-
|
174
|
-
@profiles[name] = profile
|
175
|
-
write_zoomrc
|
176
|
-
end
|
177
|
-
|
178
|
-
def exec_profile(name, args, pattern)
|
179
|
-
name = @info["profile"] if (name.nil?)
|
180
|
-
|
181
|
-
if (!@profiles.has_key?(name))
|
182
|
-
raise Zoom::Error::ProfileDoesNotExistError.new(name)
|
183
|
-
end
|
184
|
-
|
185
|
-
@info["last_command"] = {
|
186
|
-
"profile" => name,
|
187
|
-
"subargs" => args.nil? ? "": args,
|
188
|
-
"pattern" => pattern.nil? ? "" : pattern
|
189
|
-
}
|
190
|
-
write_zoominfo
|
191
|
-
|
192
|
-
profile = @profiles[name]
|
50
|
+
profile = @config.get_profile(prof_name)
|
193
51
|
begin
|
194
|
-
|
195
|
-
profile.exe(args, pattern)
|
196
|
-
shortcut_cache(profile) if (profile.taggable)
|
197
|
-
rescue Interrupt
|
198
|
-
# ^C
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def get_location_of_result(result)
|
203
|
-
count = 0
|
204
|
-
File.open(@shortcut_file) do |file|
|
205
|
-
file.each do |line|
|
206
|
-
count += 1
|
207
|
-
if (count == result)
|
208
|
-
return line
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
return nil
|
213
|
-
end
|
214
|
-
private :get_location_of_result
|
215
|
-
|
216
|
-
def get_new_value(val, default)
|
217
|
-
return default if (val.nil? || val.empty?)
|
218
|
-
return "" if (val.downcase == "empty")
|
219
|
-
return "" if (val.downcase == "\"empty\"")
|
220
|
-
return val
|
221
|
-
end
|
222
|
-
private :get_new_value
|
223
|
-
|
224
|
-
def initialize(cache_file = nil)
|
225
|
-
cache_file ||= "~/.zoom_cache"
|
226
|
-
Zoom::Profile.cache_file(cache_file)
|
227
|
-
@cache_file = Pathname.new(cache_file).expand_path
|
228
|
-
@info_file = Pathname.new("~/.zoominfo").expand_path
|
229
|
-
@rc_file = Pathname.new("~/.zoomrc").expand_path
|
230
|
-
@shortcut_file = Pathname.new("~/.zoom_shortcuts").expand_path
|
231
|
-
|
232
|
-
# Load custom profiles
|
233
|
-
custom_profs = Pathname.new("~/.zoom_profiles.rb").expand_path
|
234
|
-
require_relative custom_profs if (custom_profs.exist?)
|
235
|
-
|
236
|
-
read_zoomrc
|
237
|
-
read_zoominfo
|
238
|
-
|
239
|
-
# Setup editor
|
240
|
-
@editor = @rc["editor"]
|
241
|
-
@editor = ENV["EDITOR"] if (@editor.nil? || @editor.empty?)
|
242
|
-
@editor = "vim" if (@editor.nil? || @editor.empty?)
|
243
|
-
@editor = ScoobyDoo.where_are_you(@editor)
|
244
|
-
@editor = ScoobyDoo.where_are_you("vi") if (@editor.nil?)
|
245
|
-
|
246
|
-
end
|
247
|
-
|
248
|
-
def interactive_add_profile(name)
|
249
|
-
if (@profiles.has_key?(name))
|
250
|
-
raise Zoom::Error::ProfileAlreadyExistsError.new(name)
|
251
|
-
end
|
252
|
-
|
253
|
-
default_op = "grep"
|
254
|
-
if (ScoobyDoo.where_are_you("ag"))
|
255
|
-
default_op = "ag"
|
256
|
-
elsif (ScoobyDoo.where_are_you("pt"))
|
257
|
-
default_op = "pt"
|
258
|
-
elsif (ScoobyDoo.where_are_you("ack"))
|
259
|
-
default_op = "ack"
|
260
|
-
elsif (ScoobyDoo.where_are_you("ack-grep"))
|
261
|
-
default_op = "ack-grep"
|
262
|
-
end
|
263
|
-
|
264
|
-
ack_class = Zoom::Profile::Ack.to_s
|
265
|
-
ag_class = Zoom::Profile::Ag.to_s
|
266
|
-
grep_class = Zoom::Profile::Grep.to_s
|
267
|
-
pt_class = Zoom::Profile::Pt.to_s
|
268
|
-
|
269
|
-
case default_op
|
270
|
-
when "ack", "ack-grep"
|
271
|
-
puts "Enter class (default #{ack_class}):"
|
272
|
-
when "ag"
|
273
|
-
puts "Enter class (default #{ag_class}):"
|
274
|
-
when "grep"
|
275
|
-
puts "Enter class (default #{grep_class}):"
|
276
|
-
when "pt"
|
277
|
-
puts "Enter class (default #{pt_class}):"
|
278
|
-
end
|
52
|
+
@cache.clear
|
279
53
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
when "ack", "ack-grep"
|
285
|
-
clas = ack_class if (clas.nil? || clas.empty?)
|
286
|
-
when "ag"
|
287
|
-
clas = ag_class if (clas.nil? || clas.empty?)
|
288
|
-
when "grep"
|
289
|
-
clas = grep_class if (clas.nil? || clas.empty?)
|
290
|
-
when "pt"
|
291
|
-
clas = pt_class if (clas.nil? || clas.empty?)
|
292
|
-
end
|
293
|
-
|
294
|
-
add_profile(name, clas)
|
295
|
-
interactive_edit_profile(name)
|
296
|
-
end
|
297
|
-
|
298
|
-
def interactive_edit_profile(name, profile = nil)
|
299
|
-
profile = @profiles[name] if (profile.nil?)
|
300
|
-
|
301
|
-
if (profile.nil?)
|
302
|
-
raise Zoom::Error::ProfileDoesNotExistError.new(name)
|
303
|
-
end
|
304
|
-
|
305
|
-
if (profile.immutable)
|
306
|
-
raise Zoom::Error::ProfileCanNotBeModifiedError.new(name)
|
307
|
-
end
|
308
|
-
|
309
|
-
# Get new operator
|
310
|
-
puts "Enter operator (default #{profile.operator}):"
|
311
|
-
|
312
|
-
op = ScoobyDoo.where_are_you(gets.chomp)
|
313
|
-
puts if (op && !op.empty?)
|
314
|
-
op = profile.operator if (op.nil? || op.empty?)
|
315
|
-
|
316
|
-
# Get new flags
|
317
|
-
puts "For empty string put \"empty\""
|
318
|
-
puts "Enter flags (default \"#{profile.flags}\"):"
|
319
|
-
|
320
|
-
flags = gets.chomp
|
321
|
-
puts if (flags && !flags.empty?)
|
322
|
-
flags = get_new_value(flags, profile.flags)
|
323
|
-
|
324
|
-
# Get new prepend
|
325
|
-
puts "For empty string put \"empty\""
|
326
|
-
puts "Enter prepend (default \"#{profile.prepend}\"):"
|
327
|
-
|
328
|
-
envprepend = gets.chomp
|
329
|
-
puts if (envprepend && !envprepend.empty?)
|
330
|
-
envprepend = get_new_value(envprepend, profile.prepend)
|
331
|
-
|
332
|
-
# Get new append
|
333
|
-
puts "For empty string put \"empty\""
|
334
|
-
puts "Enter append (default \"#{profile.append}\"):"
|
335
|
-
|
336
|
-
append = gets.chomp
|
337
|
-
puts if (append && !append.empty?)
|
338
|
-
append = get_new_value(append, profile.append)
|
339
|
-
|
340
|
-
edit_profile(name, profile, op, flags, envprepend, append)
|
341
|
-
end
|
342
|
-
|
343
|
-
def list_profile_names
|
344
|
-
@profiles.keys.sort.each do |name|
|
345
|
-
puts name
|
346
|
-
end
|
347
|
-
end
|
348
|
-
|
349
|
-
def list_profiles
|
350
|
-
@profiles.keys.sort.each do |name|
|
351
|
-
if (name == @info["profile"])
|
352
|
-
puts "### #{name} ###".green
|
353
|
-
else
|
354
|
-
puts "### #{name} ###"
|
355
|
-
end
|
356
|
-
puts @profiles[name].info
|
357
|
-
puts
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
def list_tags
|
362
|
-
return if (!@cache_file.exist?)
|
363
|
-
|
364
|
-
# Open shortcut file for writing
|
365
|
-
shct = File.open(@shortcut_file, "r")
|
366
|
-
|
367
|
-
# Read in cache
|
368
|
-
File.open(@cache_file) do |cache|
|
369
|
-
count = 1
|
370
|
-
|
371
|
-
cache.each do |line|
|
372
|
-
line.chomp!
|
373
|
-
plain = remove_colors(line)
|
374
|
-
if (line.start_with?("ZOOM_EXE_DIR="))
|
375
|
-
# Ignore this line
|
376
|
-
elsif ((line == "-") || (line == "--") || line.empty?)
|
377
|
-
# Ignore dividers when searching with context and
|
378
|
-
# empty lines
|
379
|
-
elsif (plain.scan(/^[0-9]+[:-]/).empty?)
|
380
|
-
if (!plain.scan(/^\.\//).empty?)
|
381
|
-
# Operator was probably find
|
382
|
-
puts count
|
383
|
-
count += 1
|
384
|
-
end
|
385
|
-
else
|
386
|
-
puts count
|
387
|
-
count += 1
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
def loop_through_results(results)
|
394
|
-
tags = parse_tags(results)
|
395
|
-
return if (tags.empty?)
|
396
|
-
|
397
|
-
tag = tags.delete_at(0)
|
398
|
-
open_editor_to_result(tag)
|
399
|
-
|
400
|
-
tags.each do |tag|
|
401
|
-
print "Do you want to open result #{tag} [y]/n/q/l?: "
|
402
|
-
|
403
|
-
answer = nil
|
404
|
-
while (answer.nil?)
|
405
|
-
begin
|
406
|
-
system("stty raw -echo")
|
407
|
-
if ($stdin.ready?)
|
408
|
-
answer = $stdin.getc
|
409
|
-
else
|
410
|
-
sleep 0.1
|
411
|
-
end
|
412
|
-
ensure
|
413
|
-
system("stty -raw echo")
|
414
|
-
end
|
415
|
-
end
|
416
|
-
puts
|
417
|
-
|
418
|
-
case answer
|
419
|
-
when "n", "N"
|
420
|
-
# Do nothing
|
421
|
-
when "l", "L"
|
422
|
-
# Open this result then exit
|
423
|
-
open_editor_to_result(tag)
|
424
|
-
return
|
425
|
-
when "q", "Q", "\x03"
|
426
|
-
# Quit or ^C
|
427
|
-
return
|
54
|
+
# Store needed details
|
55
|
+
if (pattern && profile.pattern.empty?)
|
56
|
+
@cache.args(args)
|
57
|
+
@cache.pattern(pattern)
|
428
58
|
else
|
429
|
-
|
59
|
+
@cache.args("")
|
60
|
+
@cache.pattern(profile.pattern)
|
430
61
|
end
|
431
|
-
|
432
|
-
|
62
|
+
@cache.profile_name(prof_name)
|
63
|
+
@cache.pwd(Dir.pwd)
|
433
64
|
|
434
|
-
|
435
|
-
|
436
|
-
if (loc)
|
437
|
-
system("#{@editor} #{loc}")
|
438
|
-
else
|
439
|
-
puts "Invalid tag \"#{result}\"!"
|
440
|
-
end
|
441
|
-
end
|
442
|
-
private :open_editor_to_result
|
443
|
-
|
444
|
-
def pager
|
445
|
-
File.open(@cache_file, "w") do |f|
|
446
|
-
f.write("ZOOM_EXE_DIR=#{Dir.pwd}\n")
|
447
|
-
begin
|
448
|
-
$stdin.each_line do |line|
|
449
|
-
f.write(line)
|
450
|
-
end
|
451
|
-
rescue Interrupt
|
452
|
-
# ^C
|
453
|
-
end
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
def parse_tags(results)
|
458
|
-
tags = Array.new
|
459
|
-
if (results.nil? || results.empty?)
|
460
|
-
raise Zoom::Error::InvalidTagError.new
|
461
|
-
end
|
462
|
-
|
463
|
-
results.split(",").each do |num|
|
464
|
-
if (!num.scan(/^[0-9]+$/).empty?)
|
465
|
-
tags.push(num.to_i)
|
466
|
-
elsif (!num.scan(/^[0-9]+-[0-9]+$/).empty?)
|
467
|
-
range = num.split("-")
|
468
|
-
(range[0].to_i..range[1].to_i).each do |i|
|
469
|
-
tags.push(i)
|
470
|
-
end
|
471
|
-
else
|
472
|
-
raise Zoom::Error::InvalidTagError.new(num)
|
473
|
-
end
|
474
|
-
end
|
475
|
-
return tags
|
476
|
-
end
|
477
|
-
private :parse_tags
|
478
|
-
|
479
|
-
def read_zoominfo
|
480
|
-
if (!@info_file.exist? && !@info_file.symlink?)
|
481
|
-
default_zoominfo
|
482
|
-
end
|
483
|
-
|
484
|
-
@info = JSON.parse(File.read(@info_file))
|
485
|
-
end
|
486
|
-
private :read_zoominfo
|
487
|
-
|
488
|
-
def read_zoomrc
|
489
|
-
if (!@rc_file.exist? && !@rc_file.symlink?)
|
490
|
-
default_zoomrc
|
491
|
-
end
|
492
|
-
|
493
|
-
@rc = JSON.parse(File.read(@rc_file))
|
494
|
-
@profiles = Hash.new
|
495
|
-
@rc["profiles"].each do |name, prof|
|
496
|
-
@profiles[name] = Zoom::Profile.from_json(prof)
|
497
|
-
end
|
498
|
-
@rc["profiles"] = @profiles
|
499
|
-
end
|
500
|
-
private :read_zoomrc
|
65
|
+
# Execute profile
|
66
|
+
@cache.write(profile.exe(args, pattern))
|
501
67
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
raise Zoom::Error::ProfileCanNotBeModifiedError.new(name)
|
507
|
-
end
|
508
|
-
|
509
|
-
if (!@profiles.has_key?(name))
|
510
|
-
raise Zoom::Error::ProfileDoesNotExistError.new(name)
|
511
|
-
end
|
512
|
-
|
513
|
-
if (@profiles.has_key?(rename))
|
514
|
-
raise Zoom::Error::ProfileAlreadyExistsError.new(rename)
|
515
|
-
end
|
516
|
-
|
517
|
-
@profiles[rename] = @profiles[name]
|
518
|
-
@profiles.delete(name)
|
519
|
-
write_zoomrc
|
520
|
-
|
521
|
-
if (name == @info["profile"])
|
522
|
-
@info["profile"] = rename
|
523
|
-
write_zoominfo
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
|
-
def repeat
|
528
|
-
return if (@info["last_command"].empty?)
|
529
|
-
|
530
|
-
exec_profile(
|
531
|
-
@info["last_command"]["profile"],
|
532
|
-
@info["last_command"]["subargs"],
|
533
|
-
@info["last_command"]["pattern"]
|
534
|
-
)
|
535
|
-
end
|
536
|
-
|
537
|
-
def remove_colors(str)
|
538
|
-
str.unpack("C*").pack("U*").gsub(/\e\[([0-9;]*m|K)/, "")
|
539
|
-
end
|
540
|
-
private :remove_colors
|
541
|
-
|
542
|
-
def shortcut_cache(profile = nil)
|
543
|
-
return if (!@cache_file.exist?)
|
544
|
-
return if (@info["last_command"].empty?)
|
545
|
-
|
546
|
-
if (profile.nil?)
|
547
|
-
profile = @profiles[@info["last_command"]["profile"]]
|
548
|
-
end
|
549
|
-
|
550
|
-
# Open shortcut file for writing
|
551
|
-
shct = File.open(@shortcut_file, "w")
|
552
|
-
|
553
|
-
# Read in cache
|
554
|
-
File.open(@cache_file) do |cache|
|
555
|
-
start_dir = ""
|
556
|
-
file = nil
|
557
|
-
filename = ""
|
558
|
-
first_time = true
|
559
|
-
count = 1
|
560
|
-
|
561
|
-
cache.each do |line|
|
562
|
-
line.chomp!
|
563
|
-
plain = remove_colors(line)
|
564
|
-
if (line.start_with?("ZOOM_EXE_DIR="))
|
565
|
-
# Get directory where search was ran
|
566
|
-
start_dir = line.gsub("ZOOM_EXE_DIR=", "")
|
567
|
-
elsif ((line == "-") || (line == "--") || line.empty?)
|
568
|
-
# Ignore dividers when searching with context and
|
569
|
-
# empty lines
|
570
|
-
elsif (plain.scan(/^[0-9]+[:-]/).empty?)
|
571
|
-
operator = profile.operator.split("/").last
|
572
|
-
if (operator != "find")
|
573
|
-
if (file != line)
|
574
|
-
# Filename
|
575
|
-
file = line
|
576
|
-
filename = remove_colors(file)
|
577
|
-
|
578
|
-
puts if (!first_time)
|
579
|
-
first_time = false
|
580
|
-
|
581
|
-
puts "\e[0m#{file}"
|
582
|
-
end
|
583
|
-
else
|
584
|
-
# Operator was find
|
585
|
-
puts "\e[1;31m[#{count}]\e[0m #{line}"
|
586
|
-
shct.write("'#{start_dir}/#{line}'\n")
|
587
|
-
count += 1
|
588
|
-
end
|
589
|
-
elsif (file)
|
590
|
-
# Match
|
591
|
-
sanitized = line.unpack("C*").pack("U*")
|
592
|
-
.gsub(/[\u0080-\u00ff]+/, "\1".dump[1..-2])
|
593
|
-
puts "\e[1;31m[#{count}]\e[0m #{sanitized}"
|
594
|
-
|
595
|
-
lineno = remove_colors(line).split(/[:-]/)[0]
|
596
|
-
shct.write(
|
597
|
-
"+#{lineno} '#{start_dir}/#{filename}'\n"
|
598
|
-
)
|
599
|
-
|
600
|
-
count += 1
|
601
|
-
end
|
602
|
-
end
|
603
|
-
end
|
604
|
-
end
|
605
|
-
|
606
|
-
def show_current
|
607
|
-
puts "### #{@info["profile"]} ###".green
|
608
|
-
puts @profiles[@info["profile"]].info
|
609
|
-
end
|
610
|
-
|
611
|
-
def switch_profile(name)
|
612
|
-
if (!@profiles.has_key?(name))
|
613
|
-
raise Zoom::Error::ProfileDoesNotExistError.new(name)
|
614
|
-
end
|
615
|
-
|
616
|
-
@info["profile"] = name
|
617
|
-
write_zoominfo
|
618
|
-
end
|
619
|
-
|
620
|
-
def write_zoominfo
|
621
|
-
File.open(@info_file, "w") do |file|
|
622
|
-
file.write(JSON.pretty_generate(@info))
|
623
|
-
end
|
624
|
-
end
|
625
|
-
private :write_zoominfo
|
626
|
-
|
627
|
-
def write_zoomrc
|
628
|
-
@rc["profiles"] = @profiles
|
629
|
-
File.open(@rc_file, "w") do |file|
|
630
|
-
file.write(JSON.pretty_generate(@rc))
|
68
|
+
# Display results from cache
|
69
|
+
@cache.shortcut(@config)
|
70
|
+
rescue Interrupt
|
71
|
+
# ^C
|
631
72
|
end
|
632
73
|
end
|
633
|
-
private :write_zoomrc
|
634
74
|
end
|
635
75
|
|
76
|
+
require "zoom/cache"
|
77
|
+
require "zoom/config"
|
78
|
+
require "zoom/editor"
|
636
79
|
require "zoom/error"
|
637
80
|
require "zoom/profile"
|
81
|
+
require "zoom/profile_manager"
|