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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/bin/z +189 -147
  3. data/bin/zc +189 -147
  4. data/bin/zf +189 -147
  5. data/bin/zg +189 -147
  6. data/bin/zl +189 -147
  7. data/bin/zr +189 -147
  8. data/lib/zoom.rb +48 -604
  9. data/lib/zoom/cache.rb +253 -0
  10. data/lib/zoom/cache/result.rb +49 -0
  11. data/lib/zoom/config.rb +156 -0
  12. data/lib/zoom/editor.rb +121 -0
  13. data/lib/zoom/error.rb +6 -6
  14. data/lib/zoom/error/{executable_not_found_error.rb → executable_not_found.rb} +1 -3
  15. data/lib/zoom/error/invalid_color.rb +5 -0
  16. data/lib/zoom/error/{invalid_tag_error.rb → invalid_tag.rb} +1 -3
  17. data/lib/zoom/error/{profile_class_unknown_error.rb → profile_class_unknown.rb} +1 -3
  18. data/lib/zoom/error/{profile_does_not_exist_error.rb → profile_does_not_exist.rb} +1 -3
  19. data/lib/zoom/error/profile_not_named.rb +7 -0
  20. data/lib/zoom/profile.rb +162 -66
  21. data/lib/zoom/profile/ack.rb +5 -30
  22. data/lib/zoom/profile/ag.rb +2 -37
  23. data/lib/zoom/profile/find.rb +2 -25
  24. data/lib/zoom/profile/grep.rb +2 -44
  25. data/lib/zoom/profile/passwords.rb +27 -59
  26. data/lib/zoom/profile/pt.rb +2 -26
  27. data/lib/zoom/profile_manager.rb +49 -0
  28. data/lib/zoom/wish/add_wish.rb +58 -0
  29. data/lib/zoom/wish/color_wish.rb +147 -0
  30. data/lib/zoom/wish/copy_wish.rb +57 -0
  31. data/lib/zoom/wish/delete_wish.rb +51 -0
  32. data/lib/zoom/wish/edit_wish.rb +130 -0
  33. data/lib/zoom/wish/editor_wish.rb +31 -0
  34. data/lib/zoom/wish/list_wish.rb +58 -0
  35. data/lib/zoom/wish/rename_wish.rb +69 -0
  36. data/lib/zoom/wish/reset_wish.rb +40 -0
  37. data/lib/zoom/wish/use_wish.rb +61 -0
  38. metadata +118 -24
  39. data/lib/string.rb +0 -48
  40. data/lib/zoom/error/profile_already_exists_error.rb +0 -8
  41. 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
- EXCEPTION = 4
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 = "Do you like to search through code using ag, ack, or " \
21
- "grep? Good! This tool is for you! Zoom adds some " \
22
- "convenience to ag/ack/grep by allowing you to quickly " \
23
- "open your search results in your editor of choice. When " \
24
- "looking at large code-bases, it can be a pain to have to " \
25
- "scroll to find the filename of each result. Zoom prints a " \
26
- "tag number in front of each result that ag/ack/grep " \
27
- "outputs. Then you can quickly open that tag number with " \
28
- "Zoom to jump straight to the source. Zoom is even " \
29
- "persistent across all your sessions! You can search in " \
30
- "one terminal and jump to a tag in another terminal from " \
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] <pattern>"
44
+ "Usage: #{File.basename($0)} [OPTIONS] [pattern]"
36
45
 
37
- opts.on(
38
- "-a",
39
- "--add=NAME",
40
- "Add a new profile with specified name"
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 the specified cache file"
61
+ "Use alternate cache"
53
62
  ) do |file|
54
63
  options["cache_file"] = file
55
64
  end
56
65
 
57
- opts.on(
58
- "-d",
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
- "--list-profile-names",
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
- "--list-tags",
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
- "--pager",
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("-r", "--repeat", "Repeat the last Zoom command") do
131
- options["action"] = "repeat"
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 .zoomrc file") do
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
- "--rename=NAME",
140
- "Rename the current profile"
141
- ) do |name|
142
- options["action"] = "rename"
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
- "-s",
148
- "--switch=NAME",
149
- "Switch to profile with specified name"
150
- ) do |name|
151
- options["action"] = "switch"
152
- options["use"] = name
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
- "-u",
157
- "--use=NAME",
158
- "Use specified profile one time only"
159
- ) do |name|
160
- options["use"] = name
134
+ "-v",
135
+ "--verbose",
136
+ "Show backtrace when error occurs"
137
+ ) do
138
+ options["verbose"] = true
161
139
  end
162
140
 
163
- opts.on("-w", "--which", "Display the current profile") do
164
- options["action"] = "which"
141
+ opts.on("--version", "Show version") do
142
+ options["action"] = "version"
165
143
  end
166
144
 
167
- opts.on("", info.word_wrap(80))
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"] = "zoom_find"
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
- # do nothing, this is the normal usage
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.shortcut_cache
218
- when "delete"
219
- zoom.delete_profile(options["use"])
220
- when "edit"
221
- zoom.interactive_edit_profile(options["use"])
222
- when "editor"
223
- zoom.configure_editor(options["use"])
224
- when "examples"
225
- puts [
226
- "EXAMPLES:",
227
- "",
228
- "Add a profile named test:",
229
- " $ z --add test",
230
- "",
231
- "Edit a profile named test:",
232
- " $ z --edit test",
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
- zoom.loop_through_results(options["use"])
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.list_profiles
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.list_profile_names
256
+ puts zoom.config.get_profile_names
255
257
  when "list_tags"
256
- zoom.list_tags
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.default
263
- when "rename"
264
- zoom.rename_profile(
265
- options["rename"],
266
- options["use"]
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.show_current
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 save results
274
- zoom.exec_profile(
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 e.message
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
@@ -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
- def add_profile(
8
- name,
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
- edit_profile(
27
- name,
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 clear_cache
37
- @cache_file.delete if (@cache_file.exist?)
11
+ def self.hilight?
12
+ @@hilight ||= false
13
+ return @@hilight
38
14
  end
39
15
 
40
- def configure_editor(editor)
41
- e = ScoobyDoo.where_are_you(editor)
42
- if (e.nil?)
43
- raise Zoom::Error::ExecutableNotFoundError.new(editor)
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
- @rc["editor"] = e
47
- write_zoomrc
21
+ # Prioritize false, so only reassign if true
22
+ @@hilight = @config.hilight if (hilight)
48
23
  end
49
24
 
50
- def default
51
- default_zoominfo
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
- File.open(@info_file, "w") do |file|
63
- file.write(JSON.pretty_generate(info))
64
- end
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 default_zoomrc
68
- rc = Hash.new
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
- # Default ack profile
89
- if (
90
- ScoobyDoo.where_are_you("ack") ||
91
- ScoobyDoo.where_are_you("ack-grep")
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 edit_profile(
152
- name,
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 (profile.nil?)
162
- raise Zoom::Error::ProfileDoesNotExistsError.new(name)
46
+ if (!@config.has_profile?(prof_name))
47
+ raise Zoom::Error::ProfileDoesNotExist.new(prof_name)
163
48
  end
164
49
 
165
- if (profile.immutable)
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
- clear_cache if (profile.taggable)
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
- clas = gets.chomp
281
- puts if (clas && !clas.empty?)
282
-
283
- case default_op
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
- open_editor_to_result(tag)
59
+ @cache.args("")
60
+ @cache.pattern(profile.pattern)
430
61
  end
431
- end
432
- end
62
+ @cache.profile_name(prof_name)
63
+ @cache.pwd(Dir.pwd)
433
64
 
434
- def open_editor_to_result(result)
435
- loc = get_location_of_result(result.to_i)
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
- def rename_profile(rename, name = nil)
503
- name = @info["profile"] if (name.nil?)
504
-
505
- if ((name == "default") || (name == "zoom_find"))
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"