pry 0.9.7.4 → 0.9.8pre2

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 (49) hide show
  1. data/.gitignore +1 -3
  2. data/README.markdown +3 -1
  3. data/Rakefile +48 -31
  4. data/bin/pry +2 -80
  5. data/lib/pry.rb +17 -20
  6. data/lib/pry/cli.rb +152 -0
  7. data/lib/pry/command_processor.rb +13 -0
  8. data/lib/pry/command_set.rb +102 -9
  9. data/lib/pry/config.rb +28 -6
  10. data/lib/pry/default_commands/context.rb +9 -8
  11. data/lib/pry/default_commands/documentation.rb +55 -13
  12. data/lib/pry/default_commands/easter_eggs.rb +1 -1
  13. data/lib/pry/default_commands/input.rb +25 -25
  14. data/lib/pry/default_commands/introspection.rb +19 -18
  15. data/lib/pry/default_commands/ls.rb +23 -38
  16. data/lib/pry/default_commands/shell.rb +47 -15
  17. data/lib/pry/helpers/command_helpers.rb +28 -6
  18. data/lib/pry/helpers/options_helpers.rb +7 -4
  19. data/lib/pry/helpers/text.rb +23 -3
  20. data/lib/pry/history.rb +55 -17
  21. data/lib/pry/history_array.rb +2 -0
  22. data/lib/pry/hooks.rb +108 -0
  23. data/lib/pry/indent.rb +9 -5
  24. data/lib/pry/method.rb +99 -50
  25. data/lib/pry/plugins.rb +10 -2
  26. data/lib/pry/pry_class.rb +48 -20
  27. data/lib/pry/pry_instance.rb +106 -91
  28. data/lib/pry/version.rb +1 -1
  29. data/lib/pry/wrapped_module.rb +73 -0
  30. data/man/pry.1 +195 -0
  31. data/man/pry.1.html +204 -0
  32. data/man/pry.1.ronn +141 -0
  33. data/pry.gemspec +21 -24
  34. data/test/helper.rb +12 -3
  35. data/test/test_cli.rb +78 -0
  36. data/test/test_command_set.rb +193 -1
  37. data/test/test_default_commands/test_context.rb +19 -4
  38. data/test/test_default_commands/test_input.rb +2 -2
  39. data/test/test_default_commands/test_introspection.rb +63 -6
  40. data/test/test_default_commands/test_ls.rb +8 -35
  41. data/test/test_default_commands/test_shell.rb +36 -1
  42. data/test/test_hooks.rb +175 -0
  43. data/test/test_indent.rb +2 -0
  44. data/test/test_method.rb +10 -0
  45. data/test/test_pry.rb +35 -34
  46. data/test/test_pry_history.rb +24 -24
  47. data/test/test_syntax_checking.rb +47 -0
  48. data/test/test_wrapped_module.rb +71 -0
  49. metadata +40 -34
data/lib/pry/plugins.rb CHANGED
@@ -32,13 +32,21 @@ class Pry
32
32
  self.enabled = true
33
33
  end
34
34
 
35
+ # Load the Command line options defined by this plugin (if they exist)
36
+ def load_cli_options
37
+ cli_options_file = File.join(spec.full_gem_path, "lib/#{spec.name}/cli.rb")
38
+ require cli_options_file if File.exists?(cli_options_file)
39
+ end
35
40
  # Activate the plugin (require the gem - enables/loads the
36
- # plugin immediately at point of call, even if plugin is disabled)
41
+ # plugin immediately at point of call, even if plugin is
42
+ # disabled)
43
+ # Does not reload plugin if it's already active.
37
44
  def activate!
38
45
  begin
39
46
  require gem_name if !active?
40
- rescue LoadError
47
+ rescue LoadError => e
41
48
  $stderr.puts "Warning: The plugin '#{gem_name}' was not found! (gem found but could not be loaded)"
49
+ $stderr.puts e
42
50
  end
43
51
  self.active = true
44
52
  self.enabled = true
data/lib/pry/pry_class.rb CHANGED
@@ -73,6 +73,30 @@ class Pry
73
73
  end
74
74
  end
75
75
 
76
+ # Trap interrupts on jruby, and make them behave like MRI so we can
77
+ # catch them.
78
+ def self.load_traps
79
+ trap('INT'){ raise Interrupt }
80
+ end
81
+
82
+ # Do basic setup for initial session.
83
+ # Including: loading .pryrc, loading plugins, loading requires, and
84
+ # loading history.
85
+ def self.initial_session_setup
86
+
87
+ return if !initial_session?
88
+
89
+ # note these have to be loaded here rather than in pry_instance as
90
+ # we only want them loaded once per entire Pry lifetime.
91
+ load_rc if Pry.config.should_load_rc
92
+ load_plugins if Pry.config.plugins.enabled
93
+ load_requires if Pry.config.should_load_requires
94
+ load_history if Pry.config.history.should_load
95
+ load_traps if Pry.config.should_trap_interrupts
96
+
97
+ @initial_session = false
98
+ end
99
+
76
100
  # Start a Pry REPL.
77
101
  # This method also loads the files specified in `Pry::RC_FILES` the
78
102
  # first time it is invoked.
@@ -82,19 +106,23 @@ class Pry
82
106
  # @example
83
107
  # Pry.start(Object.new, :input => MyInput.new)
84
108
  def self.start(target=TOPLEVEL_BINDING, options={})
85
- if initial_session?
86
- # note these have to be loaded here rather than in pry_instance as
87
- # we only want them loaded once per entire Pry lifetime, not
88
- # multiple times per each new session (i.e in debugging)
89
- load_rc if Pry.config.should_load_rc
90
- load_plugins if Pry.config.plugins.enabled
91
- load_requires if Pry.config.should_load_requires
92
- load_history if Pry.config.history.should_load
93
-
94
- @initial_session = false
95
- end
109
+ target = Pry.binding_for(target)
110
+ initial_session_setup
96
111
 
97
- new(options).repl(target)
112
+ # create the Pry instance to manage the session
113
+ pry_instance = new(options)
114
+
115
+ # save backtrace
116
+ pry_instance.backtrace = caller.tap(&:shift)
117
+
118
+ # yield the binding_stack to the hook for modification
119
+ Pry.config.hooks.exec_hook(:when_started, binding_stack = [target], pry_instance)
120
+
121
+ head, *tail = binding_stack
122
+ pry_instance.binding_stack.push(*tail)
123
+
124
+ # Enter the matrix
125
+ pry_instance.repl(head)
98
126
  end
99
127
 
100
128
  # An inspector that clips the output to `max_length` chars.
@@ -120,17 +148,12 @@ class Pry
120
148
 
121
149
  # Load Readline history if required.
122
150
  def self.load_history
123
- Pry.history.load(history_file) if File.exists?(history_file)
151
+ Pry.history.load
124
152
  end
125
153
 
126
154
  # Save new lines of Readline history if required.
127
155
  def self.save_history
128
- Pry.history.save(history_file)
129
- end
130
-
131
- # Get the full path of the history_path for pry.
132
- def self.history_file
133
- File.expand_path(Pry.config.history.file)
156
+ Pry.history.save
134
157
  end
135
158
 
136
159
  # @return [Boolean] Whether this is the first time a Pry session has
@@ -165,7 +188,7 @@ class Pry
165
188
 
166
189
  output = options[:show_output] ? options[:output] : StringIO.new
167
190
 
168
- Pry.new(:output => output, :input => StringIO.new(command_string), :commands => options[:commands], :prompt => proc {""}, :hooks => {}).rep(options[:context])
191
+ Pry.new(:output => output, :input => StringIO.new(command_string), :commands => options[:commands], :prompt => proc {""}, :hooks => Pry::Hooks.new).rep(options[:context])
169
192
  end
170
193
 
171
194
  def self.default_editor_for_platform
@@ -192,10 +215,15 @@ class Pry
192
215
  config.system = DEFAULT_SYSTEM
193
216
  config.editor = default_editor_for_platform
194
217
  config.should_load_rc = true
218
+ config.should_trap_interrupts = defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
195
219
  config.disable_auto_reload = false
196
220
  config.command_prefix = ""
197
221
  config.auto_indent = true
198
222
  config.correct_indent = true
223
+ config.collision_warning = false
224
+
225
+ config.gist ||= OpenStruct.new
226
+ config.gist.inspecter = proc &:pretty_inspect
199
227
 
200
228
  config.plugins ||= OpenStruct.new
201
229
  config.plugins.enabled = true
@@ -23,6 +23,8 @@ class Pry
23
23
  attr_reader :input_array
24
24
  attr_reader :output_array
25
25
 
26
+ attr_accessor :backtrace
27
+
26
28
  # Create a new `Pry` object.
27
29
  # @param [Hash] options The optional configuration parameters.
28
30
  # @option options [#readline] :input The object to use for input.
@@ -106,13 +108,6 @@ class Pry
106
108
  @output_array = Pry::HistoryArray.new(size)
107
109
  end
108
110
 
109
- # Execute the hook `hook_name`, if it is defined.
110
- # @param [Symbol] hook_name The hook to execute
111
- # @param [Array] args The arguments to pass to the hook.
112
- def exec_hook(hook_name, *args, &block)
113
- hooks[hook_name].call(*args, &block) if hooks[hook_name]
114
- end
115
-
116
111
  # Make sure special locals exist at start of session
117
112
  def initialize_special_locals(target)
118
113
  inject_local("_in_", @input_array, target)
@@ -149,7 +144,7 @@ class Pry
149
144
  # Initialize the repl session.
150
145
  # @param [Binding] target The target binding for the session.
151
146
  def repl_prologue(target)
152
- exec_hook :before_session, output, target, self
147
+ hooks.exec_hook :before_session, output, target, self
153
148
  initialize_special_locals(target)
154
149
 
155
150
  @input_array << nil # add empty input so _in_ and _out_ match
@@ -161,7 +156,7 @@ class Pry
161
156
  # Clean-up after the repl session.
162
157
  # @param [Binding] target The target binding for the session.
163
158
  def repl_epilogue(target)
164
- exec_hook :after_session, output, target, self
159
+ hooks.exec_hook :after_session, output, target, self
165
160
 
166
161
  Pry.active_sessions -= 1
167
162
  binding_stack.pop
@@ -189,7 +184,7 @@ class Pry
189
184
  end
190
185
 
191
186
  repl_epilogue(target)
192
- break_data || target_self
187
+ break_data || nil
193
188
  end
194
189
 
195
190
  # Perform a read-eval-print.
@@ -234,13 +229,11 @@ class Pry
234
229
 
235
230
  result = set_last_result(target.eval(code, Pry.eval_path, Pry.current_line), target)
236
231
  result
237
- rescue CommandError, Slop::InvalidOptionError => e
238
- output.puts "Error: #{e.message}"
239
- @suppress_output = true
240
232
  rescue RescuableException => e
241
- set_last_exception(e, target)
233
+ result = set_last_exception(e, target)
242
234
  ensure
243
235
  update_input_history(code)
236
+ hooks.exec_hook :after_eval, result, self
244
237
  end
245
238
 
246
239
  # Perform a read.
@@ -259,16 +252,19 @@ class Pry
259
252
 
260
253
  val = ""
261
254
  loop do
262
- val = retrieve_line(eval_string, target)
263
-
264
- # eval_string may be mutated by this method
265
- process_line(val, eval_string, target)
255
+ begin
256
+ # eval_string will probably be mutated by this method
257
+ retrieve_line(eval_string, target)
258
+ rescue CommandError, Slop::InvalidOptionError => e
259
+ output.puts "Error: #{e.message}"
260
+ end
266
261
 
267
- break if valid_expression?(eval_string)
262
+ break if complete_expression?(eval_string)
268
263
  end
269
264
 
270
265
  @suppress_output = true if eval_string =~ /;\Z/ || eval_string.empty?
271
266
 
267
+ hooks.exec_hook :after_read, eval_string, self
272
268
  eval_string
273
269
  end
274
270
 
@@ -294,10 +290,15 @@ class Pry
294
290
  end
295
291
  end
296
292
 
297
- # Read a line of input and check for ^d, also determine prompt to use.
298
- # This method should not need to be invoked directly. This method also
299
- # automatically indents the input value using Pry::Indent if auto
300
- # indenting is enabled.
293
+ def should_force_encoding?(eval_string, val)
294
+ eval_string.empty? && val.respond_to?(:encoding) && val.encoding != eval_string.encoding
295
+ end
296
+ private :should_force_encoding?
297
+
298
+ # Read and process a line of input -- check for ^D, determine which prompt to
299
+ # use, rewrite the indentation if `Pry.config.auto_indent` is enabled, and,
300
+ # if the line is a command, process it and alter the eval_string accordingly.
301
+ # This method should not need to be invoked directly.
301
302
  #
302
303
  # @param [String] eval_string The cumulative lines of input.
303
304
  # @param [Binding] target The target of the session.
@@ -305,44 +306,53 @@ class Pry
305
306
  def retrieve_line(eval_string, target)
306
307
  @indent.reset if eval_string.empty?
307
308
 
308
- current_prompt = select_prompt(eval_string.empty?, target.eval('self'))
309
+ current_prompt = select_prompt(eval_string, target)
309
310
  indentation = Pry.config.auto_indent ? @indent.indent_level : ''
310
311
 
311
- val = readline(current_prompt + indentation)
312
+ val = readline("#{current_prompt}#{indentation}")
312
313
 
313
314
  # invoke handler if we receive EOF character (^D)
314
315
  if !val
315
316
  output.puts ""
316
317
  Pry.config.control_d_handler.call(eval_string, self)
317
- ""
318
- else
319
- # Change the eval_string into the input encoding (Issue 284)
320
- # TODO: This wouldn't be necessary if the eval_string was constructed from
321
- # input strings only.
322
- if eval_string.empty? && val.respond_to?(:encoding) && val.encoding != eval_string.encoding
323
- eval_string.force_encoding(val.encoding)
324
- end
318
+ return
319
+ end
325
320
 
326
- if !@command_processor.valid_command?(val, target) && Pry.config.auto_indent && !input.is_a?(StringIO)
327
- orig_val = "#{indentation}#{val}"
328
- val = @indent.indent(val)
321
+ # Change the eval_string into the input encoding (Issue 284)
322
+ # TODO: This wouldn't be necessary if the eval_string was constructed from
323
+ # input strings only.
324
+ if should_force_encoding?(eval_string, val)
325
+ eval_string.force_encoding(val.encoding)
326
+ end
329
327
 
330
- if orig_val != val && output.tty? && Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
331
- output.print @indent.correct_indentation(current_prompt + val, orig_val.length - val.length)
332
- end
328
+ if Pry.config.auto_indent && !input.is_a?(StringIO)
329
+ original_val = "#{indentation}#{val}"
330
+ indented_val = @indent.indent(val)
331
+
332
+ if original_val != indented_val && output.tty? && Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
333
+ output.print @indent.correct_indentation(current_prompt + indented_val, original_val.length - indented_val.length)
333
334
  end
335
+ else
336
+ indented_val = val
337
+ end
334
338
 
335
- Pry.history << val.dup unless input.is_a?(StringIO)
336
- val
339
+ begin
340
+ if !process_command(val, eval_string, target)
341
+ eval_string << "#{indented_val.rstrip}\n" unless val.empty?
342
+ end
343
+ ensure
344
+ Pry.history << indented_val unless input.is_a?(StringIO)
337
345
  end
338
346
  end
339
347
 
340
- # Process the line received.
348
+ # If the given line is a valid command, process it in the context of the
349
+ # current `eval_string` and context.
341
350
  # This method should not need to be invoked directly.
342
351
  # @param [String] val The line to process.
343
352
  # @param [String] eval_string The cumulative lines of input.
344
353
  # @param [Binding] target The target of the Pry session.
345
- def process_line(val, eval_string, target)
354
+ # @return [Boolean] `true` if `val` is a command, `false` otherwise
355
+ def process_command(val, eval_string, target)
346
356
  result = @command_processor.process_commands(val, eval_string, target)
347
357
 
348
358
  # set a temporary (just so we can inject the value we want into eval_string)
@@ -351,16 +361,16 @@ class Pry
351
361
  # note that `result` wraps the result of command processing; if a
352
362
  # command was matched and invoked then `result.command?` returns true,
353
363
  # otherwise it returns false.
354
- if result.command? && !result.void_command?
355
-
356
- # the command that was invoked was non-void (had a return value) and so we make
357
- # the value of the current expression equal to the return value
358
- # of the command.
359
- eval_string.replace "Thread.current[:__pry_cmd_result__].retval\n"
364
+ if result.command?
365
+ if !result.void_command?
366
+ # the command that was invoked was non-void (had a return value) and so we make
367
+ # the value of the current expression equal to the return value
368
+ # of the command.
369
+ eval_string.replace "Thread.current[:__pry_cmd_result__].retval\n"
370
+ end
371
+ true
360
372
  else
361
- # only commands should have an empty `val`
362
- # so this ignores their result
363
- eval_string << "#{val.rstrip}\n" if !val.empty?
373
+ false
364
374
  end
365
375
  end
366
376
 
@@ -372,7 +382,7 @@ class Pry
372
382
  # @example
373
383
  # pry_instance.run_command("ls -m")
374
384
  def run_command(val, eval_string = "", target = binding_stack.last)
375
- process_line(val, eval_string, target)
385
+ @command_processor.process_commands(val, eval_string, target)
376
386
  Pry::CommandContext::VOID_VALUE
377
387
  end
378
388
 
@@ -480,14 +490,17 @@ class Pry
480
490
 
481
491
  # Returns the appropriate prompt to use.
482
492
  # This method should not need to be invoked directly.
483
- # @param [Boolean] first_line Whether this is the first line of input
484
- # (and not multi-line input).
485
- # @param [Object] target_self The receiver of the Pry session.
493
+ # @param [String] eval_string The current input buffer.
494
+ # @param [Binding] target The target Binding of the Pry session.
486
495
  # @return [String] The prompt.
487
- def select_prompt(first_line, target_self)
496
+ def select_prompt(eval_string, target)
497
+ target_self = target.eval('self')
488
498
 
489
- if first_line
499
+ # If input buffer is empty then use normal prompt
500
+ if eval_string.empty?
490
501
  Array(prompt).first.call(target_self, binding_stack.size - 1, self)
502
+
503
+ # Otherwise use the wait prompt (indicating multi-line expression)
491
504
  else
492
505
  Array(prompt).last.call(target_self, binding_stack.size - 1, self)
493
506
  end
@@ -526,45 +539,47 @@ class Pry
526
539
  prompt_stack.size > 1 ? prompt_stack.pop : prompt
527
540
  end
528
541
 
529
- if RUBY_VERSION =~ /1.9/ && RUBY_ENGINE == "ruby"
530
- require 'ripper'
531
-
532
- # Determine if a string of code is a valid Ruby expression.
533
- # Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
534
- # @param [String] code The code to validate.
535
- # @return [Boolean] Whether or not the code is a valid Ruby expression.
536
- # @example
537
- # valid_expression?("class Hello") #=> false
538
- # valid_expression?("class Hello; end") #=> true
539
- def valid_expression?(code)
540
- !!Ripper::SexpBuilder.new(code).parse
542
+ # Determine if a string of code is a complete Ruby expression.
543
+ # @param [String] code The code to validate.
544
+ # @return [Boolean] Whether or not the code is a complete Ruby expression.
545
+ # @raise [SyntaxError] Any SyntaxError that does not represent incompleteness.
546
+ # @example
547
+ # complete_expression?("class Hello") #=> false
548
+ # complete_expression?("class Hello; end") #=> true
549
+ def complete_expression?(str)
550
+ if defined?(Rubinius::Melbourne19) && RUBY_VERSION =~ /^1\.9/
551
+ Rubinius::Melbourne19.parse_string(str, Pry.eval_path)
552
+ elsif defined?(Rubinius::Melbourne)
553
+ Rubinius::Melbourne.parse_string(str, Pry.eval_path)
554
+ else
555
+ catch(:valid) {
556
+ eval("BEGIN{throw :valid}\n#{str}", binding, Pry.eval_path)
557
+ }
541
558
  end
542
559
 
543
- elsif RUBY_VERSION =~ /1.9/ && RUBY_ENGINE == 'jruby'
544
-
545
- # JRuby doesn't have Ripper, so use its native parser for 1.9 mode.
546
- def valid_expression?(code)
547
- JRuby.parse(code)
548
- true
549
- rescue SyntaxError
560
+ # Assert that a line which ends with a , or a \ is incomplete.
561
+ str !~ /[,\\]$/
562
+ rescue SyntaxError => e
563
+ if incomplete_user_input_exception?(e)
550
564
  false
565
+ else
566
+ raise e
551
567
  end
568
+ end
552
569
 
553
- else
554
- require 'ruby_parser'
555
-
556
- # Determine if a string of code is a valid Ruby expression.
557
- # Ruby 1.9 uses Ripper, Ruby 1.8 uses RubyParser.
558
- # @param [String] code The code to validate.
559
- # @return [Boolean] Whether or not the code is a valid Ruby expression.
560
- # @example
561
- # valid_expression?("class Hello") #=> false
562
- # valid_expression?("class Hello; end") #=> true
563
- def valid_expression?(code)
564
- # NOTE: we're using .dup because RubyParser mutates the input
565
- RubyParser.new.parse(code.dup)
570
+ # Check whether the exception indicates that the user should input more.
571
+ #
572
+ # @param [SyntaxError] the exception object that was raised.
573
+ # @param [Array<String>] The stack frame of the function that executed eval.
574
+ # @return [Boolean]
575
+ #
576
+ def incomplete_user_input_exception?(ex)
577
+ case ex.message
578
+ when /unexpected (\$end|end-of-file|END_OF_FILE)/, # mri, jruby, ironruby
579
+ /unterminated (quoted string|string|regexp) meets end of file/, # "quoted string" is ironruby
580
+ /missing 'end' for/, /: expecting '[})\]]'$/, /can't find string ".*" anywhere before EOF/, /expecting keyword_end/ # rbx
566
581
  true
567
- rescue Racc::ParseError, SyntaxError
582
+ else
568
583
  false
569
584
  end
570
585
  end