pry 0.10.3 → 0.14.2

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 (159) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +439 -16
  3. data/LICENSE +1 -1
  4. data/README.md +362 -302
  5. data/bin/pry +4 -7
  6. data/lib/pry/basic_object.rb +10 -0
  7. data/lib/pry/block_command.rb +22 -0
  8. data/lib/pry/class_command.rb +194 -0
  9. data/lib/pry/cli.rb +84 -97
  10. data/lib/pry/code/code_file.rb +37 -26
  11. data/lib/pry/code/code_range.rb +7 -5
  12. data/lib/pry/code/loc.rb +26 -13
  13. data/lib/pry/code.rb +42 -31
  14. data/lib/pry/code_object.rb +53 -28
  15. data/lib/pry/color_printer.rb +46 -35
  16. data/lib/pry/command.rb +197 -369
  17. data/lib/pry/command_set.rb +89 -114
  18. data/lib/pry/command_state.rb +31 -0
  19. data/lib/pry/commands/amend_line.rb +86 -82
  20. data/lib/pry/commands/bang.rb +18 -14
  21. data/lib/pry/commands/bang_pry.rb +15 -11
  22. data/lib/pry/commands/cat/abstract_formatter.rb +23 -18
  23. data/lib/pry/commands/cat/exception_formatter.rb +85 -72
  24. data/lib/pry/commands/cat/file_formatter.rb +56 -46
  25. data/lib/pry/commands/cat/input_expression_formatter.rb +35 -30
  26. data/lib/pry/commands/cat.rb +62 -54
  27. data/lib/pry/commands/cd.rb +40 -35
  28. data/lib/pry/commands/change_inspector.rb +29 -22
  29. data/lib/pry/commands/change_prompt.rb +48 -23
  30. data/lib/pry/commands/clear_screen.rb +20 -0
  31. data/lib/pry/commands/code_collector.rb +148 -131
  32. data/lib/pry/commands/disable_pry.rb +23 -19
  33. data/lib/pry/commands/easter_eggs.rb +23 -34
  34. data/lib/pry/commands/edit/exception_patcher.rb +21 -17
  35. data/lib/pry/commands/edit/file_and_line_locator.rb +34 -23
  36. data/lib/pry/commands/edit.rb +185 -157
  37. data/lib/pry/commands/exit.rb +40 -35
  38. data/lib/pry/commands/exit_all.rb +24 -20
  39. data/lib/pry/commands/exit_program.rb +20 -16
  40. data/lib/pry/commands/find_method.rb +168 -162
  41. data/lib/pry/commands/fix_indent.rb +16 -12
  42. data/lib/pry/commands/help.rb +140 -133
  43. data/lib/pry/commands/hist.rb +151 -149
  44. data/lib/pry/commands/import_set.rb +20 -15
  45. data/lib/pry/commands/jump_to.rb +25 -21
  46. data/lib/pry/commands/list_inspectors.rb +35 -28
  47. data/lib/pry/commands/ls/constants.rb +59 -31
  48. data/lib/pry/commands/ls/formatter.rb +42 -36
  49. data/lib/pry/commands/ls/globals.rb +38 -36
  50. data/lib/pry/commands/ls/grep.rb +17 -15
  51. data/lib/pry/commands/ls/instance_vars.rb +29 -28
  52. data/lib/pry/commands/ls/interrogatable.rb +18 -12
  53. data/lib/pry/commands/ls/jruby_hacks.rb +47 -41
  54. data/lib/pry/commands/ls/local_names.rb +26 -24
  55. data/lib/pry/commands/ls/local_vars.rb +38 -30
  56. data/lib/pry/commands/ls/ls_entity.rb +47 -52
  57. data/lib/pry/commands/ls/methods.rb +49 -51
  58. data/lib/pry/commands/ls/methods_helper.rb +46 -42
  59. data/lib/pry/commands/ls/self_methods.rb +23 -21
  60. data/lib/pry/commands/ls.rb +124 -103
  61. data/lib/pry/commands/nesting.rb +21 -17
  62. data/lib/pry/commands/play.rb +92 -82
  63. data/lib/pry/commands/pry_backtrace.rb +22 -17
  64. data/lib/pry/commands/pry_version.rb +15 -11
  65. data/lib/pry/commands/raise_up.rb +33 -27
  66. data/lib/pry/commands/reload_code.rb +60 -48
  67. data/lib/pry/commands/reset.rb +16 -12
  68. data/lib/pry/commands/ri.rb +57 -42
  69. data/lib/pry/commands/save_file.rb +45 -43
  70. data/lib/pry/commands/shell_command.rb +56 -29
  71. data/lib/pry/commands/shell_mode.rb +22 -18
  72. data/lib/pry/commands/show_doc.rb +80 -70
  73. data/lib/pry/commands/show_info.rb +194 -155
  74. data/lib/pry/commands/show_input.rb +16 -11
  75. data/lib/pry/commands/show_source.rb +110 -42
  76. data/lib/pry/commands/stat.rb +35 -31
  77. data/lib/pry/commands/switch_to.rb +21 -15
  78. data/lib/pry/commands/toggle_color.rb +20 -16
  79. data/lib/pry/commands/watch_expression/expression.rb +32 -27
  80. data/lib/pry/commands/watch_expression.rb +89 -84
  81. data/lib/pry/commands/whereami.rb +156 -141
  82. data/lib/pry/commands/wtf.rb +78 -40
  83. data/lib/pry/config/attributable.rb +22 -0
  84. data/lib/pry/config/lazy_value.rb +29 -0
  85. data/lib/pry/config/memoized_value.rb +34 -0
  86. data/lib/pry/config/value.rb +24 -0
  87. data/lib/pry/config.rb +310 -20
  88. data/lib/pry/control_d_handler.rb +28 -0
  89. data/lib/pry/core_extensions.rb +22 -9
  90. data/lib/pry/editor.rb +56 -34
  91. data/lib/pry/env.rb +18 -0
  92. data/lib/pry/exception_handler.rb +43 -0
  93. data/lib/pry/exceptions.rb +13 -18
  94. data/lib/pry/forwardable.rb +27 -0
  95. data/lib/pry/helpers/base_helpers.rb +20 -62
  96. data/lib/pry/helpers/command_helpers.rb +52 -62
  97. data/lib/pry/helpers/documentation_helpers.rb +21 -12
  98. data/lib/pry/helpers/options_helpers.rb +15 -8
  99. data/lib/pry/helpers/platform.rb +55 -0
  100. data/lib/pry/helpers/table.rb +44 -32
  101. data/lib/pry/helpers/text.rb +96 -85
  102. data/lib/pry/helpers.rb +3 -0
  103. data/lib/pry/history.rb +81 -55
  104. data/lib/pry/hooks.rb +60 -110
  105. data/lib/pry/indent.rb +74 -68
  106. data/lib/pry/input_completer.rb +199 -158
  107. data/lib/pry/input_lock.rb +7 -10
  108. data/lib/pry/inspector.rb +36 -24
  109. data/lib/pry/last_exception.rb +45 -45
  110. data/lib/pry/method/disowned.rb +19 -5
  111. data/lib/pry/method/patcher.rb +14 -8
  112. data/lib/pry/method/weird_method_locator.rb +79 -45
  113. data/lib/pry/method.rb +178 -124
  114. data/lib/pry/object_path.rb +37 -28
  115. data/lib/pry/output.rb +102 -16
  116. data/lib/pry/pager.rb +187 -174
  117. data/lib/pry/prompt.rb +213 -25
  118. data/lib/pry/pry_class.rb +119 -98
  119. data/lib/pry/pry_instance.rb +261 -224
  120. data/lib/pry/repl.rb +83 -29
  121. data/lib/pry/repl_file_loader.rb +27 -22
  122. data/lib/pry/ring.rb +89 -0
  123. data/lib/pry/slop/LICENSE +20 -0
  124. data/lib/pry/slop/commands.rb +190 -0
  125. data/lib/pry/slop/option.rb +210 -0
  126. data/lib/pry/slop.rb +672 -0
  127. data/lib/pry/syntax_highlighter.rb +26 -0
  128. data/lib/pry/system_command_handler.rb +17 -0
  129. data/lib/pry/testable/evalable.rb +24 -0
  130. data/lib/pry/testable/mockable.rb +22 -0
  131. data/lib/pry/testable/pry_tester.rb +88 -0
  132. data/lib/pry/testable/utility.rb +34 -0
  133. data/lib/pry/testable/variables.rb +52 -0
  134. data/lib/pry/testable.rb +68 -0
  135. data/lib/pry/version.rb +3 -1
  136. data/lib/pry/warning.rb +20 -0
  137. data/lib/pry/{module_candidate.rb → wrapped_module/candidate.rb} +35 -32
  138. data/lib/pry/wrapped_module.rb +68 -63
  139. data/lib/pry.rb +133 -149
  140. metadata +58 -69
  141. data/lib/pry/commands/disabled_commands.rb +0 -2
  142. data/lib/pry/commands/gem_cd.rb +0 -26
  143. data/lib/pry/commands/gem_install.rb +0 -32
  144. data/lib/pry/commands/gem_list.rb +0 -33
  145. data/lib/pry/commands/gem_open.rb +0 -29
  146. data/lib/pry/commands/gist.rb +0 -101
  147. data/lib/pry/commands/install_command.rb +0 -53
  148. data/lib/pry/commands/list_prompts.rb +0 -35
  149. data/lib/pry/commands/simple_prompt.rb +0 -22
  150. data/lib/pry/commands.rb +0 -6
  151. data/lib/pry/config/behavior.rb +0 -139
  152. data/lib/pry/config/convenience.rb +0 -25
  153. data/lib/pry/config/default.rb +0 -161
  154. data/lib/pry/history_array.rb +0 -121
  155. data/lib/pry/plugins.rb +0 -103
  156. data/lib/pry/rbx_path.rb +0 -22
  157. data/lib/pry/rubygem.rb +0 -82
  158. data/lib/pry/terminal.rb +0 -79
  159. data/lib/pry/test/helper.rb +0 -170
data/lib/pry/slop.rb ADDED
@@ -0,0 +1,672 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pry
4
+ # rubocop:disable Metrics/ClassLength
5
+ class Slop
6
+ require_relative 'slop/option'
7
+ require_relative 'slop/commands'
8
+ include Enumerable
9
+ VERSION = '3.4.0'.freeze
10
+
11
+ # The main Error class, all Exception classes inherit from this class.
12
+ class Error < StandardError; end
13
+
14
+ # Raised when an option argument is expected but none are given.
15
+ class MissingArgumentError < Error; end
16
+
17
+ # Raised when an option is expected/required but not present.
18
+ class MissingOptionError < Error; end
19
+
20
+ # Raised when an argument does not match its intended match constraint.
21
+ class InvalidArgumentError < Error; end
22
+
23
+ # Raised when an invalid option is found and the strict flag is enabled.
24
+ class InvalidOptionError < Error; end
25
+
26
+ # Raised when an invalid command is found and the strict flag is enabled.
27
+ class InvalidCommandError < Error; end
28
+
29
+ # Returns a default Hash of configuration options this Slop instance uses.
30
+ DEFAULT_OPTIONS = {
31
+ strict: false,
32
+ help: false,
33
+ banner: nil,
34
+ ignore_case: false,
35
+ autocreate: false,
36
+ arguments: false,
37
+ optional_arguments: false,
38
+ multiple_switches: true,
39
+ longest_flag: 0
40
+ }.freeze
41
+
42
+ class << self
43
+ # items - The Array of items to extract options from (default: ARGV).
44
+ # config - The Hash of configuration options to send to Slop.new().
45
+ # block - An optional block used to add options.
46
+ #
47
+ # Examples:
48
+ #
49
+ # Slop.parse(ARGV, :help => true) do
50
+ # on '-n', '--name', 'Your username', :argument => true
51
+ # end
52
+ #
53
+ # Returns a new instance of Slop.
54
+ def parse(items = ARGV, config = {}, &block)
55
+ parse! items.dup, config, &block
56
+ end
57
+
58
+ # items - The Array of items to extract options from (default: ARGV).
59
+ # config - The Hash of configuration options to send to Slop.new().
60
+ # block - An optional block used to add options.
61
+ #
62
+ # Returns a new instance of Slop.
63
+ def parse!(items = ARGV, config = {}, &block)
64
+ if items.is_a?(Hash) && config.empty?
65
+ config = items
66
+ items = ARGV
67
+ end
68
+ slop = Pry::Slop.new config, &block
69
+ slop.parse! items
70
+ slop
71
+ end
72
+
73
+ # Build a Slop object from a option specification.
74
+ #
75
+ # This allows you to design your options via a simple String rather
76
+ # than programatically. Do note though that with this method, you're
77
+ # unable to pass any advanced options to the on() method when creating
78
+ # options.
79
+ #
80
+ # string - The optspec String
81
+ # config - A Hash of configuration options to pass to Slop.new
82
+ #
83
+ # Examples:
84
+ #
85
+ # opts = Slop.optspec(<<-SPEC)
86
+ # ruby foo.rb [options]
87
+ # ---
88
+ # n,name= Your name
89
+ # a,age= Your age
90
+ # A,auth Sign in with auth
91
+ # p,passcode= Your secret pass code
92
+ # SPEC
93
+ #
94
+ # opts.fetch_option(:name).description #=> "Your name"
95
+ #
96
+ # Returns a new instance of Slop.
97
+ def optspec(string, config = {})
98
+ config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
99
+ lines = optspec.split("\n").reject(&:empty?)
100
+ opts = Slop.new(config)
101
+
102
+ lines.each do |line|
103
+ opt, description = line.split(' ', 2)
104
+ short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
105
+ opt = opts.on(short, long, description)
106
+
107
+ if long && long.end_with?('=')
108
+ long.sub!(/\=$/, '')
109
+ opt.config[:argument] = true
110
+ end
111
+ end
112
+
113
+ opts
114
+ end
115
+ end
116
+
117
+ # The Hash of configuration options for this Slop instance.
118
+ attr_reader :config
119
+
120
+ # The Array of Slop::Option objects tied to this Slop instance.
121
+ attr_reader :options
122
+
123
+ # Create a new instance of Slop and optionally build options via a block.
124
+ #
125
+ # config - A Hash of configuration options.
126
+ # block - An optional block used to specify options.
127
+ def initialize(config = {}, &block)
128
+ @config = DEFAULT_OPTIONS.merge(config)
129
+ @options = []
130
+ @commands = {}
131
+ @trash = []
132
+ @triggered_options = []
133
+ @unknown_options = []
134
+ @callbacks = {}
135
+ @separators = {}
136
+ @runner = nil
137
+
138
+ if block_given?
139
+ block.arity == 1 ? yield(self) : instance_eval(&block)
140
+ end
141
+
142
+ return unless config[:help]
143
+
144
+ on('-h', '--help', 'Display this help message.', tail: true) do
145
+ warn help
146
+ end
147
+ end
148
+
149
+ # Is strict mode enabled?
150
+ #
151
+ # Returns true if strict mode is enabled, false otherwise.
152
+ def strict?
153
+ config[:strict]
154
+ end
155
+
156
+ # Set the banner.
157
+ #
158
+ # banner - The String to set the banner.
159
+ def banner=(banner)
160
+ config[:banner] = banner
161
+ end
162
+
163
+ # Get or set the banner.
164
+ #
165
+ # banner - The String to set the banner.
166
+ #
167
+ # Returns the banner String.
168
+ def banner(banner = nil)
169
+ config[:banner] = banner if banner
170
+ config[:banner]
171
+ end
172
+
173
+ # Set the description (used for commands).
174
+ #
175
+ # desc - The String to set the description.
176
+ def description=(desc)
177
+ config[:description] = desc
178
+ end
179
+
180
+ # Get or set the description (used for commands).
181
+ #
182
+ # desc - The String to set the description.
183
+ #
184
+ # Returns the description String.
185
+ def description(desc = nil)
186
+ config[:description] = desc if desc
187
+ config[:description]
188
+ end
189
+
190
+ # Add a new command.
191
+ #
192
+ # command - The Symbol or String used to identify this command.
193
+ # options - A Hash of configuration options (see Slop::new)
194
+ #
195
+ # Returns a new instance of Slop mapped to this command.
196
+ def command(command, options = {}, &block)
197
+ @commands[command.to_s] = Pry::Slop.new(options, &block)
198
+ end
199
+
200
+ # Parse a list of items, executing and gathering options along the way.
201
+ #
202
+ # items - The Array of items to extract options from (default: ARGV).
203
+ # block - An optional block which when used will yield non options.
204
+ #
205
+ # Returns an Array of original items.
206
+ def parse(items = ARGV, &block)
207
+ parse! items.dup, &block
208
+ items
209
+ end
210
+
211
+ # Parse a list of items, executing and gathering options along the way.
212
+ # unlike parse() this method will remove any options and option arguments
213
+ # from the original Array.
214
+ #
215
+ # items - The Array of items to extract options from (default: ARGV).
216
+ # block - An optional block which when used will yield non options.
217
+ #
218
+ # Returns an Array of original items with options removed.
219
+ def parse!(items = ARGV, &block)
220
+ if items.empty? && @callbacks[:empty]
221
+ @callbacks[:empty].each { |cb| cb.call(self) }
222
+ return items
223
+ end
224
+
225
+ if (cmd = @commands[items[0]])
226
+ return cmd.parse! items[1..-1]
227
+ end
228
+
229
+ items.each_with_index do |item, index|
230
+ @trash << index && break if item == '--'
231
+ autocreate(items, index) if config[:autocreate]
232
+ process_item(items, index, &block) unless @trash.include?(index)
233
+ end
234
+ items.reject!.with_index { |_item, index| @trash.include?(index) }
235
+
236
+ missing_options = options.select { |opt| opt.required? && opt.count < 1 }
237
+ if missing_options.any?
238
+ raise MissingOptionError,
239
+ "Missing required option(s): #{missing_options.map(&:key).join(', ')}"
240
+ end
241
+
242
+ if @unknown_options.any?
243
+ raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
244
+ end
245
+
246
+ if @triggered_options.empty? && @callbacks[:no_options]
247
+ @callbacks[:no_options].each { |cb| cb.call(self) }
248
+ end
249
+
250
+ @runner.call(self, items) if @runner.respond_to?(:call)
251
+
252
+ items
253
+ end
254
+
255
+ # Add an Option.
256
+ #
257
+ # objects - An Array with an optional Hash as the last element.
258
+ #
259
+ # Examples:
260
+ #
261
+ # on '-u', '--username=', 'Your username'
262
+ # on :v, :verbose, 'Enable verbose mode'
263
+ #
264
+ # Returns the created instance of Slop::Option.
265
+ def on(*objects, &block)
266
+ option = build_option(objects, &block)
267
+ options << option
268
+ option
269
+ end
270
+ alias option on
271
+ alias opt on
272
+
273
+ # Fetch an options argument value.
274
+ #
275
+ # key - The Symbol or String option short or long flag.
276
+ #
277
+ # Returns the Object value for this option, or nil.
278
+ def [](key)
279
+ option = fetch_option(key)
280
+ option.value if option
281
+ end
282
+ alias get []
283
+
284
+ # Returns a new Hash with option flags as keys and option values as values.
285
+ #
286
+ # include_commands - If true, merge options from all sub-commands.
287
+ def to_hash(include_commands = false)
288
+ hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
289
+ if include_commands
290
+ @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
291
+ end
292
+ hash
293
+ end
294
+ alias to_h to_hash
295
+
296
+ # Enumerable interface. Yields each Slop::Option.
297
+ def each(&block)
298
+ options.each(&block)
299
+ end
300
+
301
+ # Specify code to be executed when these options are parsed.
302
+ #
303
+ # callable - An object responding to a call method.
304
+ #
305
+ # yields - The instance of Slop parsing these options
306
+ # An Array of unparsed arguments
307
+ #
308
+ # Example:
309
+ #
310
+ # Slop.parse do
311
+ # on :v, :verbose
312
+ #
313
+ # run do |opts, args|
314
+ # puts "Arguments: #{args.inspect}" if opts.verbose?
315
+ # end
316
+ # end
317
+ def run(callable = nil, &block)
318
+ @runner = callable || block
319
+ return if @runner.respond_to?(:call)
320
+
321
+ raise ArgumentError, "You must specify a callable object or a block to #run"
322
+ end
323
+
324
+ # Check for an options presence.
325
+ #
326
+ # Examples:
327
+ #
328
+ # opts.parse %w( --foo )
329
+ # opts.present?(:foo) #=> true
330
+ # opts.present?(:bar) #=> false
331
+ #
332
+ # Returns true if all of the keys are present in the parsed arguments.
333
+ def present?(*keys)
334
+ keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
335
+ end
336
+
337
+ # Override this method so we can check if an option? method exists.
338
+ #
339
+ # Returns true if this option key exists in our list of options.
340
+ def respond_to_missing?(method_name, include_all = false)
341
+ options.any? { |o| o.key == method_name.to_s.chop } || super
342
+ end
343
+
344
+ # Fetch a list of options which were missing from the parsed list.
345
+ #
346
+ # Examples:
347
+ #
348
+ # opts = Slop.new do
349
+ # on :n, :name=
350
+ # on :p, :password=
351
+ # end
352
+ #
353
+ # opts.parse %w[ --name Lee ]
354
+ # opts.missing #=> ['password']
355
+ #
356
+ # Returns an Array of Strings representing missing options.
357
+ def missing
358
+ (options - @triggered_options).map(&:key)
359
+ end
360
+
361
+ # Fetch a Slop::Option object.
362
+ #
363
+ # key - The Symbol or String option key.
364
+ #
365
+ # Examples:
366
+ #
367
+ # opts.on(:foo, 'Something fooey', :argument => :optional)
368
+ # opt = opts.fetch_option(:foo)
369
+ # opt.class #=> Slop::Option
370
+ # opt.accepts_optional_argument? #=> true
371
+ #
372
+ # Returns an Option or nil if none were found.
373
+ def fetch_option(key)
374
+ options.find { |option| [option.long, option.short].include?(clean(key)) }
375
+ end
376
+
377
+ # Fetch a Slop object associated with this command.
378
+ #
379
+ # command - The String or Symbol name of the command.
380
+ #
381
+ # Examples:
382
+ #
383
+ # opts.command :foo do
384
+ # on :v, :verbose, 'Enable verbose mode'
385
+ # end
386
+ #
387
+ # # ruby run.rb foo -v
388
+ # opts.fetch_command(:foo).verbose? #=> true
389
+ def fetch_command(command)
390
+ @commands[command.to_s]
391
+ end
392
+
393
+ # Add a callback.
394
+ #
395
+ # label - The Symbol identifier to attach this callback.
396
+ #
397
+ # Returns nothing.
398
+ def add_callback(label, &block)
399
+ (@callbacks[label] ||= []) << block
400
+ end
401
+
402
+ # Add string separators between options.
403
+ #
404
+ # text - The String text to print.
405
+ def separator(text)
406
+ if @separators[options.size]
407
+ @separators[options.size] << "\n#{text}"
408
+ else
409
+ @separators[options.size] = text
410
+ end
411
+ end
412
+
413
+ # Print a handy Slop help string.
414
+ #
415
+ # Returns the banner followed by available option help strings.
416
+ def to_s
417
+ heads = options.reject(&:tail?)
418
+ tails = (options - heads)
419
+ opts = (heads + tails).select(&:help).map(&:to_s)
420
+ optstr = opts.each_with_index.map do |o, i|
421
+ (str = @separators[i + 1]) ? [o, str].join("\n") : o
422
+ end.join("\n")
423
+
424
+ if @commands.any?
425
+ optstr << "\n" unless optstr.empty?
426
+ optstr << "\nAvailable commands:\n\n"
427
+ optstr << commands_to_help
428
+ optstr << "\n\nSee `<command> --help` for more information on a specific command."
429
+ end
430
+
431
+ banner = config[:banner]
432
+ banner ||= "Usage: #{File.basename($PROGRAM_NAME, '.*')}" \
433
+ "#{' [command]' if @commands.any?} [options]"
434
+ if banner
435
+ "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
436
+ else
437
+ optstr
438
+ end
439
+ end
440
+ alias help to_s
441
+
442
+ private
443
+
444
+ # Convenience method for present?(:option).
445
+ #
446
+ # Examples:
447
+ #
448
+ # opts.parse %( --verbose )
449
+ # opts.verbose? #=> true
450
+ # opts.other? #=> false
451
+ #
452
+ # Returns true if this option is present. If this method does not end
453
+ # with a ? character it will instead call super().
454
+ def method_missing(method, *args, &block)
455
+ meth = method.to_s
456
+ if meth.end_with?('?')
457
+ meth = meth.chop
458
+ present?(meth) || present?(meth.tr('_', '-'))
459
+ else
460
+ super
461
+ end
462
+ end
463
+
464
+ # Process a list item, figure out if it's an option, execute any
465
+ # callbacks, assign any option arguments, and do some sanity checks.
466
+ #
467
+ # items - The Array of items to process.
468
+ # index - The current Integer index of the item we want to process.
469
+ # block - An optional block which when passed will yield non options.
470
+ #
471
+ # Returns nothing.
472
+ def process_item(items, index, &block)
473
+ return unless (item = items[index])
474
+
475
+ option, argument = extract_option(item) if item.start_with?('-')
476
+
477
+ if option
478
+ option.count += 1 unless item.start_with?('--no-')
479
+ option.count += 1 if option.key[0, 3] == "no-"
480
+ @trash << index
481
+ @triggered_options << option
482
+
483
+ if option.expects_argument?
484
+ argument ||= items.at(index + 1)
485
+
486
+ if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
487
+ raise MissingArgumentError, "#{option.key} expects an argument"
488
+ end
489
+
490
+ execute_option(option, argument, index, item)
491
+ elsif option.accepts_optional_argument?
492
+ argument ||= items.at(index + 1)
493
+
494
+ if argument && argument =~ /\A([^\-?]|-\d)+/
495
+ execute_option(option, argument, index, item)
496
+ else
497
+ option.call(nil)
498
+ end
499
+ elsif config[:multiple_switches] && argument
500
+ execute_multiple_switches(option, argument, index)
501
+ else
502
+ option.value = option.count > 0
503
+ option.call(nil)
504
+ end
505
+ else
506
+ @unknown_options << item if strict? && item =~ /\A--?/
507
+ yield(item) if block && !@trash.include?(index)
508
+ end
509
+ end
510
+
511
+ # Execute an option, firing off callbacks and assigning arguments.
512
+ #
513
+ # option - The Slop::Option object found by #process_item.
514
+ # argument - The argument Object to assign to this option.
515
+ # index - The current Integer index of the object we're processing.
516
+ # item - The optional String item we're processing.
517
+ #
518
+ # Returns nothing.
519
+ def execute_option(option, argument, index, item = nil)
520
+ unless option
521
+ if config[:multiple_switches] && strict?
522
+ raise InvalidOptionError, "Unknown option -#{item}"
523
+ end
524
+
525
+ return
526
+ end
527
+
528
+ if argument
529
+ unless item && item.end_with?("=#{argument}")
530
+ @trash << index + 1 unless option.argument_in_value
531
+ end
532
+ option.value = argument
533
+ else
534
+ option.value = option.count > 0
535
+ end
536
+
537
+ if option.match? && !argument.match(option.config[:match])
538
+ raise InvalidArgumentError, "#{argument} is an invalid argument"
539
+ end
540
+
541
+ option.call(option.value)
542
+ end
543
+
544
+ # Execute a `-abc` type option where a, b and c are all options. This
545
+ # method is only executed if the multiple_switches argument is true.
546
+ #
547
+ # option - The first Option object.
548
+ # argument - The argument to this option. (Split into multiple Options).
549
+ # index - The index of the current item being processed.
550
+ #
551
+ # Returns nothing.
552
+ def execute_multiple_switches(option, argument, index)
553
+ execute_option(option, nil, index)
554
+ argument.split('').each do |key|
555
+ next unless (opt = fetch_option(key))
556
+
557
+ opt.count += 1
558
+ execute_option(opt, nil, index, key)
559
+ end
560
+ end
561
+
562
+ # Extract an option from a flag.
563
+ #
564
+ # flag - The flag key used to extract an option.
565
+ #
566
+ # Returns an Array of [option, argument].
567
+ def extract_option(flag)
568
+ option = fetch_option(flag)
569
+ option ||= fetch_option(flag.downcase) if config[:ignore_case]
570
+ option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
571
+
572
+ unless option
573
+ case flag
574
+ when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
575
+ option = fetch_option(Regexp.last_match(1))
576
+ argument = Regexp.last_match(2) || false
577
+ option.argument_in_value = true if option
578
+ end
579
+ end
580
+
581
+ [option, argument]
582
+ end
583
+
584
+ # Autocreate an option on the fly. See the :autocreate Slop config option.
585
+ #
586
+ # items - The Array of items we're parsing.
587
+ # index - The current Integer index for the item we're processing.
588
+ #
589
+ # Returns nothing.
590
+ def autocreate(items, index)
591
+ flag = items[index]
592
+ return if fetch_option(flag) || @trash.include?(index)
593
+
594
+ option = build_option(Array(flag))
595
+ argument = items[index + 1]
596
+ option.config[:argument] = (argument && argument !~ /\A--?/)
597
+ option.config[:autocreated] = true
598
+ options << option
599
+ end
600
+
601
+ # Build an option from a list of objects.
602
+ #
603
+ # objects - An Array of objects used to build this option.
604
+ #
605
+ # Returns a new instance of Slop::Option.
606
+ def build_option(objects, &block)
607
+ config = {}
608
+ config[:argument] = true if @config[:arguments]
609
+ config[:optional_argument] = true if @config[:optional_arguments]
610
+
611
+ if objects.last.is_a?(Hash)
612
+ config.merge!(objects.last)
613
+ objects.pop
614
+ end
615
+ short = extract_short_flag(objects, config)
616
+ long = extract_long_flag(objects, config)
617
+ desc = objects[0].respond_to?(:to_str) ? objects.shift : nil
618
+
619
+ Option.new(self, short, long, desc, config, &block)
620
+ end
621
+
622
+ # Extract the short flag from an item.
623
+ #
624
+ # objects - The Array of objects passed from #build_option.
625
+ # config - The Hash of configuration options built in #build_option.
626
+ def extract_short_flag(objects, config)
627
+ flag = clean(objects.first)
628
+
629
+ if flag.size == 2 && flag.end_with?('=')
630
+ config[:argument] ||= true
631
+ flag.chop!
632
+ end
633
+
634
+ return unless flag.size == 1
635
+
636
+ objects.shift
637
+ flag
638
+ end
639
+
640
+ # Extract the long flag from an item.
641
+ #
642
+ # objects - The Array of objects passed from #build_option.
643
+ # config - The Hash of configuration options built in #build_option.
644
+ def extract_long_flag(objects, config)
645
+ flag = objects.first.to_s
646
+ return unless flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/
647
+
648
+ config[:argument] ||= true if flag.end_with?('=')
649
+ config[:optional_argument] = true if flag.end_with?('=?')
650
+ objects.shift
651
+ clean(flag).sub(/\=\??\z/, '')
652
+ end
653
+
654
+ # Remove any leading -- characters from a string.
655
+ #
656
+ # object - The Object we want to cast to a String and clean.
657
+ #
658
+ # Returns the newly cleaned String with leading -- characters removed.
659
+ def clean(object)
660
+ object.to_s.sub(/\A--?/, '')
661
+ end
662
+
663
+ def commands_to_help
664
+ padding = 0
665
+ @commands.each { |c, _| padding = c.size if c.size > padding }
666
+ @commands.map do |cmd, opts|
667
+ " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
668
+ end.join("\n")
669
+ end
670
+ end
671
+ # rubocop:enable Metrics/ClassLength
672
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coderay'
4
+
5
+ class Pry
6
+ # @api private
7
+ # @since v0.13.0
8
+ class SyntaxHighlighter
9
+ def self.highlight(code, language = :ruby)
10
+ tokenize(code, language).term
11
+ end
12
+
13
+ def self.tokenize(code, language = :ruby)
14
+ CodeRay.scan(code, language)
15
+ end
16
+
17
+ def self.keyword_token_color
18
+ CodeRay::Encoders::Terminal::TOKEN_COLORS[:keyword]
19
+ end
20
+
21
+ # Sets comment token to blue (black by default), so it's more legible.
22
+ def self.overwrite_coderay_comment_token!
23
+ CodeRay::Encoders::Terminal::TOKEN_COLORS[:comment][:self] = "\e[1;34m"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Pry
4
+ # @api private
5
+ # @since v0.13.0
6
+ module SystemCommandHandler
7
+ class << self
8
+ def default(output, command, _pry_instance)
9
+ return if Kernel.system(command)
10
+
11
+ output.puts(
12
+ "Error: there was a problem executing system command: #{command}"
13
+ )
14
+ end
15
+ end
16
+ end
17
+ end