pry 0.9.11.4 → 0.9.12pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.travis.yml +2 -0
  2. data/Rakefile +4 -0
  3. data/lib/pry.rb +1 -1
  4. data/lib/pry/cli.rb +14 -8
  5. data/lib/pry/code.rb +3 -3
  6. data/lib/pry/command.rb +20 -5
  7. data/lib/pry/command_set.rb +3 -3
  8. data/lib/pry/commands.rb +1 -1
  9. data/lib/pry/commands/disabled_commands.rb +2 -0
  10. data/lib/pry/commands/ls.rb +1 -2
  11. data/lib/pry/commands/reload_code.rb +8 -1
  12. data/lib/pry/commands/show_info.rb +27 -4
  13. data/lib/pry/commands/show_source.rb +2 -1
  14. data/lib/pry/commands/whereami.rb +87 -19
  15. data/lib/pry/completion.rb +7 -4
  16. data/lib/pry/helpers/base_helpers.rb +5 -2
  17. data/lib/pry/helpers/command_helpers.rb +3 -1
  18. data/lib/pry/helpers/documentation_helpers.rb +18 -7
  19. data/lib/pry/helpers/table.rb +4 -4
  20. data/lib/pry/indent.rb +2 -7
  21. data/lib/pry/method.rb +89 -129
  22. data/lib/pry/method/disowned.rb +53 -0
  23. data/lib/pry/method/weird_method_locator.rb +186 -0
  24. data/lib/pry/module_candidate.rb +3 -3
  25. data/lib/pry/pager.rb +13 -11
  26. data/lib/pry/pry_class.rb +10 -3
  27. data/lib/pry/terminal.rb +73 -0
  28. data/lib/pry/version.rb +1 -1
  29. data/lib/pry/wrapped_module.rb +51 -1
  30. data/spec/command_helpers_spec.rb +21 -1
  31. data/spec/commands/ls_spec.rb +4 -0
  32. data/spec/commands/show_doc_spec.rb +28 -28
  33. data/spec/commands/show_source_spec.rb +70 -22
  34. data/spec/commands/whereami_spec.rb +60 -11
  35. data/spec/documentation_helper_spec.rb +73 -0
  36. data/spec/fixtures/whereami_helper.rb +6 -0
  37. data/spec/helpers/table_spec.rb +19 -0
  38. data/spec/method_spec.rb +24 -7
  39. metadata +13 -7
  40. data/lib/pry/commands/deprecated_commands.rb +0 -2
  41. data/lib/pry/terminal_info.rb +0 -48
@@ -71,9 +71,12 @@ class Pry
71
71
  # get new target for 1/2 and find candidates for 3
72
72
  path, input = build_path(input)
73
73
 
74
- unless path.call.empty?
75
- target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, pry)
76
- target = target.last
74
+ # we silence warnings here or ruby 1.8 cries about "multiple values for block 0 for 1"
75
+ Helpers::BaseHelpers.silence_warnings do
76
+ unless path.call.empty?
77
+ target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, pry)
78
+ target = target.last
79
+ end
77
80
  end
78
81
 
79
82
  begin
@@ -283,7 +286,7 @@ class Pry
283
286
  contexts = input.chomp('/').split(/\//)
284
287
  input = contexts[-1]
285
288
 
286
- path = proc do |input|
289
+ path = Proc.new do |input|
287
290
  p = contexts[0..-2].push(input).join('/')
288
291
  p += '/' if trailing_slash && !input.nil?
289
292
  p
@@ -83,12 +83,15 @@ class Pry
83
83
  defined?(Win32::Console) || ENV['ANSICON']
84
84
  end
85
85
 
86
- # are we on Jruby platform?
87
86
  def jruby?
88
87
  RbConfig::CONFIG['ruby_install_name'] == 'jruby'
89
88
  end
90
89
 
91
- # are we on rbx platform?
90
+ def jruby_19?
91
+ RbConfig::CONFIG['ruby_install_name'] == 'jruby' &&
92
+ RbConfig::CONFIG['ruby_version'] == '1.9'
93
+ end
94
+
92
95
  def rbx?
93
96
  RbConfig::CONFIG['ruby_install_name'] == 'rbx'
94
97
  end
@@ -84,7 +84,9 @@ class Pry
84
84
  text = text.sub(/^[ \t]+$/, '')
85
85
 
86
86
  # Find the longest common whitespace to all indented lines
87
- margin = text.scan(/^[ \t]*(?=[^ \t\n])/).inject do |current_margin, next_indent|
87
+ # Ignore lines containing just -- or ++ as these seem to be used by
88
+ # comment authors as delimeters.
89
+ margin = text.scan(/^[ \t]*(?!--\n|\+\+\n)(?=[^ \t\n])/).inject do |current_margin, next_indent|
88
90
  if next_indent.start_with?(current_margin)
89
91
  current_margin
90
92
  elsif current_margin.start_with?(next_indent)
@@ -4,14 +4,18 @@ class Pry
4
4
  # This class contains methods useful for extracting
5
5
  # documentation from methods and classes.
6
6
  module DocumentationHelpers
7
+
8
+ module_function
9
+
7
10
  def process_rdoc(comment)
11
+ return comment unless Pry.color
8
12
  comment = comment.dup
9
- comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { Pry.color ? CodeRay.scan($1, :ruby).term : $1 }.
10
- gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { Pry.color ? "\e[1m#{$1}\e[0m": $1 }.
11
- gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { Pry.color ? "\e[1m#{$1}\e[0m" : $1 }.
12
- gsub(/\B\+(\w*?)\+\B/) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
13
- gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { Pry.color ? CodeRay.scan($1, :ruby).term : $1 }.
14
- gsub(/`(?:\s*\n)?([^\e]*?)\s*`/) { "`#{Pry.color ? CodeRay.scan($1, :ruby).term : $1}`" }
13
+ comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { CodeRay.scan($1, :ruby).term }.
14
+ gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { "\e[1m#{$1}\e[0m" }.
15
+ gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { "\e[1m#{$1}\e[0m" }.
16
+ gsub(/\B\+(\w+?)\+\B/) { "\e[32m#{$1}\e[0m" }.
17
+ gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { CodeRay.scan($1, :ruby).term }.
18
+ gsub(/`(?:\s*\n)?([^\e]*?)\s*`/) { "`#{CodeRay.scan($1, :ruby).term}`" }
15
19
  end
16
20
 
17
21
  def process_yardoc_tag(comment, tag)
@@ -46,10 +50,17 @@ class Pry
46
50
  code.sub(/\A\s*\/\*.*?\*\/\s*/m, '')
47
51
  end
48
52
 
53
+ # Given a string that makes up a comment in a source-code file parse out the content
54
+ # that the user is intended to read. (i.e. without leading indentation, #-characters
55
+ # or shebangs)
56
+ #
49
57
  # @param [String] comment
50
58
  # @return [String]
51
- def strip_leading_hash_and_whitespace_from_ruby_comments(comment)
59
+ def get_comment_content(comment)
52
60
  comment = comment.dup
61
+ # Remove #!/usr/bin/ruby
62
+ comment.gsub!(/\A\#!.*$/, '')
63
+ # Remove leading empty comment lines
53
64
  comment.gsub!(/\A\#+?$/, '')
54
65
  comment.gsub!(/^\s*#/, '')
55
66
  strip_leading_whitespace(comment)
@@ -3,7 +3,7 @@ class Pry
3
3
  def self.tablify_or_one_line(heading, things)
4
4
  plain_heading = Pry::Helpers::Text.strip_color(heading)
5
5
  attempt = Table.new(things, :column_count => things.size)
6
- if attempt.fits_on_line?(TerminalInfo.width! - plain_heading.size - 2)
6
+ if attempt.fits_on_line?(Terminal.width! - plain_heading.size - 2)
7
7
  "#{heading}: #{attempt}\n"
8
8
  else
9
9
  "#{heading}: \n#{tablify_to_screen_width(things, :indent => ' ')}\n"
@@ -13,16 +13,16 @@ class Pry
13
13
  def self.tablify_to_screen_width(things, options = {})
14
14
  things = things.compact
15
15
  if indent = options[:indent]
16
- usable_width = TerminalInfo.width! - indent.size
16
+ usable_width = Terminal.width! - indent.size
17
17
  tablify(things, usable_width).to_s.gsub(/^/, indent)
18
18
  else
19
- tablify(things, TerminalInfo.width!).to_s
19
+ tablify(things, Terminal.width!).to_s
20
20
  end
21
21
  end
22
22
 
23
23
  def self.tablify(things, line_length)
24
24
  table = Table.new(things, :column_count => things.size)
25
- table.column_count -= 1 until 0 == table.column_count or
25
+ table.column_count -= 1 until 1 == table.column_count or
26
26
  table.fits_on_line?(line_length)
27
27
  table
28
28
  end
@@ -1,12 +1,6 @@
1
1
  require 'coderay'
2
2
 
3
3
  class Pry
4
- # Load io-console if possible, so that we can use $stdout.winsize.
5
- begin
6
- require 'io/console'
7
- rescue LoadError
8
- end
9
-
10
4
  ##
11
5
  # Pry::Indent is a class that can be used to indent a number of lines
12
6
  # containing Ruby code similar as to how IRB does it (but better). The class
@@ -389,10 +383,11 @@ class Pry
389
383
  # the difference in length between the old line and the new one).
390
384
  # @return [String]
391
385
  def correct_indentation(prompt, code, overhang=0)
386
+ prompt = prompt.delete("\001\002")
392
387
  full_line = prompt + code
393
388
  whitespace = ' ' * overhang
394
389
 
395
- _, cols = TerminalInfo.screen_size
390
+ _, cols = Terminal.screen_size
396
391
 
397
392
  cols = cols.to_i
398
393
  lines = cols != 0 ? (full_line.length / cols + 1) : 1
@@ -17,9 +17,12 @@ class Pry
17
17
  # This class wraps the normal `Method` and `UnboundMethod` classes
18
18
  # to provide extra functionality useful to Pry.
19
19
  class Method
20
+ require 'pry/method/weird_method_locator'
21
+ require 'pry/method/disowned'
22
+
20
23
  extend Helpers::BaseHelpers
21
24
  include Helpers::BaseHelpers
22
- include RbxMethod if Helpers::BaseHelpers.rbx?
25
+ include RbxMethod if rbx?
23
26
  include Helpers::DocumentationHelpers
24
27
  include CodeObject::Helpers
25
28
 
@@ -82,37 +85,11 @@ class Pry
82
85
  Disowned.new(b.eval('self'), meth_name.to_s)
83
86
  end
84
87
 
85
- # it's possible in some cases that the method we find by this approach is a sub-method of
86
- # the one we're currently in, consider:
87
- #
88
- # class A; def b; binding.pry; end; end
89
- # class B < A; def b; super; end; end
90
- #
91
- # Given that we can normally find the source_range of methods, and that we know which
92
- # __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
93
- #
94
- # This obviously won't work if the source is unavaiable for some reason, or if both
95
- # methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
96
- # is broken.
97
- #
98
- guess = method
99
-
100
- while guess
101
- # needs rescue if this is a Disowned method or a C method or something...
102
- # TODO: Fix up the exception handling so we don't need a bare rescue
103
- if (guess.source_file && guess.source_range rescue false) &&
104
- File.expand_path(guess.source_file) == File.expand_path(b.eval('__FILE__')) &&
105
- guess.source_range.include?(b.eval('__LINE__'))
106
- return guess
107
- else
108
- guess = guess.super
109
- end
88
+ if WeirdMethodLocator.weird_method?(method, b)
89
+ WeirdMethodLocator.new(method, b).get_method || method
90
+ else
91
+ method
110
92
  end
111
-
112
- # Uhoh... none of the methods in the chain had the right __FILE__ and __LINE__
113
- # This may be caused by rbx https://github.com/rubinius/rubinius/issues/953,
114
- # or other unknown circumstances (TODO: we should warn the user when this happens)
115
- method
116
93
  end
117
94
  end
118
95
 
@@ -196,6 +173,19 @@ class Pry
196
173
  ([klass] + klass.ancestors).uniq
197
174
  end
198
175
 
176
+ def method_definition?(name, definition_line)
177
+ singleton_method_definition?(name, definition_line) ||
178
+ instance_method_definition?(name, definition_line)
179
+ end
180
+
181
+ def singleton_method_definition?(name, definition_line)
182
+ /^define_singleton_method\(?\s*[:\"\']#{name}|^def\s*self\.#{name}/ =~ definition_line.strip
183
+ end
184
+
185
+ def instance_method_definition?(name, definition_line)
186
+ /^define_method\(?\s*[:\"\']#{name}|^def\s*#{name}/ =~ definition_line.strip
187
+ end
188
+
199
189
  private
200
190
 
201
191
  # See all_from_class and all_from_obj.
@@ -310,12 +300,12 @@ class Pry
310
300
  info = pry_doc_info
311
301
  info.docstring if info
312
302
  when :ruby
313
- if Helpers::BaseHelpers.rbx? && !pry_method?
314
- strip_leading_hash_and_whitespace_from_ruby_comments(core_doc)
303
+ if rbx? && !pry_method?
304
+ get_comment_content(core_doc)
315
305
  elsif pry_method?
316
- strip_leading_hash_and_whitespace_from_ruby_comments(doc_for_pry_method)
306
+ get_comment_content(doc_for_pry_method)
317
307
  else
318
- strip_leading_hash_and_whitespace_from_ruby_comments(@method.comment)
308
+ get_comment_content(@method.comment)
319
309
  end
320
310
  end
321
311
  end
@@ -327,7 +317,7 @@ class Pry
327
317
  end
328
318
 
329
319
  def source_location
330
- if @method.source_location && Helpers::BaseHelpers.rbx?
320
+ if @method.source_location && rbx?
331
321
  file, line = @method.source_location
332
322
  [RbxPath.convert_path_to_full(file), line]
333
323
  else
@@ -339,7 +329,7 @@ class Pry
339
329
  # `nil` if the filename is unavailable.
340
330
  def source_file
341
331
  if source_location.nil?
342
- if !Helpers::BaseHelpers.rbx? and source_type == :c
332
+ if !rbx? and source_type == :c
343
333
  info = pry_doc_info
344
334
  info.file if info
345
335
  end
@@ -423,6 +413,21 @@ class Pry
423
413
  !!(source_file and source_file =~ /(\(.*\))|<.*>/)
424
414
  end
425
415
 
416
+ # @return [Boolean] Whether the method is unbound.
417
+ def unbound_method?
418
+ is_a?(::UnboundMethod)
419
+ end
420
+
421
+ # @return [Boolean] Whether the method is bound.
422
+ def bound_method?
423
+ is_a?(::Method)
424
+ end
425
+
426
+ # @return [Boolean] Whether the method is a singleton method.
427
+ def singleton_method?
428
+ wrapped_owner.singleton_class?
429
+ end
430
+
426
431
  # @return [Boolean] Was the method defined within the Pry REPL?
427
432
  def pry_method?
428
433
  source_file == Pry.eval_path
@@ -479,113 +484,68 @@ class Pry
479
484
  end
480
485
 
481
486
  private
482
- # @return [YARD::CodeObjects::MethodObject]
483
- # @raise [CommandError] Raises when the method can't be found or `pry-doc` isn't installed.
484
- def pry_doc_info
485
- if Pry.config.has_pry_doc
486
- Pry::MethodInfo.info_for(@method) or raise CommandError, "Cannot locate this method: #{name}. (source_location returns nil)"
487
- else
488
- raise CommandError, "Cannot locate this method: #{name}. Try `gem install pry-doc` to get access to Ruby Core documentation."
489
- end
490
- end
491
-
492
- # FIXME: a very similar method to this exists on WrappedModule: extract_doc_for_candidate
493
- def doc_for_pry_method
494
- _, line_num = source_location
495
-
496
- buffer = ""
497
- Pry.line_buffer[0..(line_num - 1)].each do |line|
498
- # Add any line that is a valid ruby comment,
499
- # but clear as soon as we hit a non comment line.
500
- if (line =~ /^\s*#/) || (line =~ /^\s*$/)
501
- buffer << line.lstrip
502
- else
503
- buffer.replace("")
504
- end
505
- end
506
487
 
507
- buffer
508
- end
509
-
510
- # @param [Class, Module] ancestors The ancestors to investigate
511
- # @return [Method] The unwrapped super-method
512
- def super_using_ancestors(ancestors, times=1)
513
- next_owner = self.owner
514
- times.times do
515
- i = ancestors.index(next_owner) + 1
516
- while ancestors[i] && !(ancestors[i].method_defined?(name) || ancestors[i].private_method_defined?(name))
517
- i += 1
518
- end
519
- next_owner = ancestors[i] or return nil
488
+ # @return [YARD::CodeObjects::MethodObject]
489
+ # @raise [CommandError] when the method can't be found or `pry-doc` isn't installed.
490
+ def pry_doc_info
491
+ if Pry.config.has_pry_doc
492
+ Pry::MethodInfo.info_for(@method) or raise CommandError, "Cannot locate this method: #{name}. (source_location returns nil)"
493
+ else
494
+ fail_msg = "Cannot locate this method: #{name}."
495
+ if mri_18? || mri_19?
496
+ fail_msg += ' Try `gem-install pry-doc` to get access to Ruby Core documentation.'
520
497
  end
521
-
522
- safe_send(next_owner, :instance_method, name) rescue nil
498
+ raise CommandError, fail_msg
523
499
  end
500
+ end
524
501
 
525
- # @param [String] first_ln The first line of a method definition.
526
- # @return [String, nil]
527
- def method_name_from_first_line(first_ln)
528
- return nil if first_ln.strip !~ /^def /
502
+ # FIXME: a very similar method to this exists on WrappedModule: extract_doc_for_candidate
503
+ def doc_for_pry_method
504
+ _, line_num = source_location
529
505
 
530
- tokens = CodeRay.scan(first_ln, :ruby)
531
- tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens)
532
- tokens.each_cons(2) do |t1, t2|
533
- if t2.last == :method || t2.last == :ident && t1 == [".", :operator]
534
- return t2.first
535
- end
506
+ buffer = ""
507
+ Pry.line_buffer[0..(line_num - 1)].each do |line|
508
+ # Add any line that is a valid ruby comment,
509
+ # but clear as soon as we hit a non comment line.
510
+ if (line =~ /^\s*#/) || (line =~ /^\s*$/)
511
+ buffer << line.lstrip
512
+ else
513
+ buffer.replace("")
536
514
  end
537
-
538
- nil
539
515
  end
540
516
 
541
- # A Disowned Method is one that's been removed from the class on which it was defined.
542
- #
543
- # e.g.
544
- # class C
545
- # def foo
546
- # C.send(:undefine_method, :foo)
547
- # Pry::Method.from_binding(binding)
548
- # end
549
- # end
550
- #
551
- # In this case we assume that the "owner" is the singleton class of the receiver.
552
- #
553
- # This occurs mainly in Sinatra applications.
554
- class Disowned < Method
555
- attr_reader :receiver, :name
517
+ buffer
518
+ end
556
519
 
557
- # Create a new Disowned method.
558
- #
559
- # @param [Object] receiver
560
- # @param [String] method_name
561
- def initialize(receiver, method_name)
562
- @receiver, @name = receiver, method_name
520
+ # @param [Class, Module] ancestors The ancestors to investigate
521
+ # @return [Method] The unwrapped super-method
522
+ def super_using_ancestors(ancestors, times=1)
523
+ next_owner = self.owner
524
+ times.times do
525
+ i = ancestors.index(next_owner) + 1
526
+ while ancestors[i] && !(ancestors[i].method_defined?(name) || ancestors[i].private_method_defined?(name))
527
+ i += 1
528
+ end
529
+ next_owner = ancestors[i] or return nil
563
530
  end
564
531
 
565
- # Is the method undefined? (aka `Disowned`)
566
- # @return [Boolean] true
567
- def undefined?
568
- true
569
- end
532
+ safe_send(next_owner, :instance_method, name) rescue nil
533
+ end
570
534
 
571
- # Can we get the source for this method?
572
- # @return [Boolean] false
573
- def source?
574
- false
575
- end
535
+ # @param [String] first_ln The first line of a method definition.
536
+ # @return [String, nil]
537
+ def method_name_from_first_line(first_ln)
538
+ return nil if first_ln.strip !~ /^def /
576
539
 
577
- # Get the hypothesized owner of the method.
578
- #
579
- # @return [Object]
580
- def owner
581
- class << receiver; self; end
540
+ tokens = CodeRay.scan(first_ln, :ruby)
541
+ tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens)
542
+ tokens.each_cons(2) do |t1, t2|
543
+ if t2.last == :method || t2.last == :ident && t1 == [".", :operator]
544
+ return t2.first
545
+ end
582
546
  end
583
547
 
584
- # Raise a more useful error message instead of trying to forward to nil.
585
- def method_missing(meth_name, *args, &block)
586
- raise "Cannot call '#{meth_name}' on an undef'd method." if method(:name).respond_to?(meth_name)
587
- Object.instance_method(:method_missing).bind(self).call(meth_name, *args, &block)
588
- end
548
+ nil
589
549
  end
590
550
  end
591
551
  end
@@ -0,0 +1,53 @@
1
+ class Pry
2
+ class Method
3
+ # A Disowned Method is one that's been removed from the class on which it was defined.
4
+ #
5
+ # e.g.
6
+ # class C
7
+ # def foo
8
+ # C.send(:undefine_method, :foo)
9
+ # Pry::Method.from_binding(binding)
10
+ # end
11
+ # end
12
+ #
13
+ # In this case we assume that the "owner" is the singleton class of the receiver.
14
+ #
15
+ # This occurs mainly in Sinatra applications.
16
+ class Disowned < Method
17
+ attr_reader :receiver, :name
18
+
19
+ # Create a new Disowned method.
20
+ #
21
+ # @param [Object] receiver
22
+ # @param [String] method_name
23
+ def initialize(receiver, method_name, binding=nil)
24
+ @receiver, @name = receiver, method_name
25
+ end
26
+
27
+ # Is the method undefined? (aka `Disowned`)
28
+ # @return [Boolean] true
29
+ def undefined?
30
+ true
31
+ end
32
+
33
+ # Can we get the source for this method?
34
+ # @return [Boolean] false
35
+ def source?
36
+ false
37
+ end
38
+
39
+ # Get the hypothesized owner of the method.
40
+ #
41
+ # @return [Object]
42
+ def owner
43
+ class << receiver; self; end
44
+ end
45
+
46
+ # Raise a more useful error message instead of trying to forward to nil.
47
+ def method_missing(meth_name, *args, &block)
48
+ raise "Cannot call '#{meth_name}' on an undef'd method." if method(:name).respond_to?(meth_name)
49
+ Object.instance_method(:method_missing).bind(self).call(meth_name, *args, &block)
50
+ end
51
+ end
52
+ end
53
+ end