pry 0.9.8.4 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG +26 -0
- data/README.markdown +3 -3
- data/lib/pry.rb +9 -1
- data/lib/pry/code.rb +93 -0
- data/lib/pry/command.rb +35 -22
- data/lib/pry/command_set.rb +97 -67
- data/lib/pry/config.rb +63 -10
- data/lib/pry/core_extensions.rb +24 -18
- data/lib/pry/default_commands/context.rb +72 -12
- data/lib/pry/default_commands/easter_eggs.rb +4 -0
- data/lib/pry/default_commands/editing.rb +43 -15
- data/lib/pry/default_commands/find_method.rb +171 -0
- data/lib/pry/default_commands/hist.rb +10 -6
- data/lib/pry/default_commands/introspection.rb +183 -30
- data/lib/pry/default_commands/ls.rb +77 -7
- data/lib/pry/default_commands/misc.rb +1 -0
- data/lib/pry/default_commands/navigating_pry.rb +1 -8
- data/lib/pry/helpers/base_helpers.rb +10 -2
- data/lib/pry/helpers/command_helpers.rb +23 -40
- data/lib/pry/helpers/documentation_helpers.rb +65 -0
- data/lib/pry/indent.rb +17 -4
- data/lib/pry/method.rb +61 -45
- data/lib/pry/pry_class.rb +9 -3
- data/lib/pry/pry_instance.rb +99 -65
- data/lib/pry/rbx_method.rb +2 -9
- data/lib/pry/version.rb +1 -1
- data/lib/pry/wrapped_module.rb +236 -1
- data/pry.gemspec +5 -5
- data/test/helper.rb +22 -0
- data/test/test_command.rb +29 -0
- data/test/test_command_integration.rb +193 -10
- data/test/test_command_set.rb +82 -17
- data/test/test_default_commands/test_cd.rb +16 -0
- data/test/test_default_commands/test_context.rb +61 -0
- data/test/test_default_commands/test_documentation.rb +163 -43
- data/test/test_default_commands/test_find_method.rb +42 -0
- data/test/test_default_commands/test_input.rb +32 -0
- data/test/test_default_commands/test_introspection.rb +50 -197
- data/test/test_default_commands/test_ls.rb +22 -0
- data/test/test_default_commands/test_show_source.rb +306 -0
- data/test/test_pry.rb +3 -3
- data/test/test_pry_defaults.rb +21 -0
- data/test/test_sticky_locals.rb +81 -1
- data/test/test_syntax_checking.rb +7 -6
- metadata +22 -14
data/lib/pry/pry_class.rb
CHANGED
@@ -46,11 +46,14 @@ class Pry
|
|
46
46
|
# @return [Fixnum] The number of active Pry sessions.
|
47
47
|
attr_accessor :active_sessions
|
48
48
|
|
49
|
+
# @return [Boolean] Whether Pry sessions are quiet by default.
|
50
|
+
attr_accessor :quiet
|
51
|
+
|
49
52
|
# plugin forwardables
|
50
53
|
def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins
|
51
54
|
|
52
55
|
delegate_accessors :@config, :input, :output, :commands, :prompt, :print, :exception_handler,
|
53
|
-
:hooks, :color, :pager, :editor, :memory_size, :input_stack
|
56
|
+
:hooks, :color, :pager, :editor, :memory_size, :input_stack, :extra_sticky_locals
|
54
57
|
end
|
55
58
|
|
56
59
|
# Load the rc files given in the `Pry::RC_FILES` array.
|
@@ -261,6 +264,8 @@ class Pry
|
|
261
264
|
|
262
265
|
config.memory_size = 100
|
263
266
|
|
267
|
+
config.extra_sticky_locals = {}
|
268
|
+
|
264
269
|
config.ls ||= OpenStruct.new({
|
265
270
|
:heading_color => :default,
|
266
271
|
|
@@ -279,9 +284,10 @@ class Pry
|
|
279
284
|
:builtin_global_color => :cyan, # e.g. $stdin, $-w, $PID
|
280
285
|
:pseudo_global_color => :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO
|
281
286
|
|
282
|
-
:constant_color => :default,
|
287
|
+
:constant_color => :default, # e.g. VERSION, ARGF
|
283
288
|
:class_constant_color => :blue, # e.g. Object, Kernel
|
284
289
|
:exception_constant_color => :magenta, # e.g. Exception, RuntimeError
|
290
|
+
:unloaded_constant_color => :yellow, # Any constant that is still in .autoload? state
|
285
291
|
|
286
292
|
# What should separate items listed by ls? (TODO: we should allow a columnar layout)
|
287
293
|
:separator => " ",
|
@@ -345,7 +351,7 @@ class Pry
|
|
345
351
|
# @param [Object] target The object to get a `Binding` object for.
|
346
352
|
# @return [Binding] The `Binding` object.
|
347
353
|
def self.binding_for(target)
|
348
|
-
if target
|
354
|
+
if Binding === target
|
349
355
|
target
|
350
356
|
else
|
351
357
|
if TOPLEVEL_BINDING.eval('self') == target
|
data/lib/pry/pry_instance.rb
CHANGED
@@ -8,6 +8,8 @@ class Pry
|
|
8
8
|
attr_accessor :print
|
9
9
|
attr_accessor :exception_handler
|
10
10
|
attr_accessor :input_stack
|
11
|
+
attr_accessor :quiet
|
12
|
+
alias :quiet? :quiet
|
11
13
|
|
12
14
|
attr_accessor :custom_completions
|
13
15
|
|
@@ -24,6 +26,8 @@ class Pry
|
|
24
26
|
|
25
27
|
attr_accessor :backtrace
|
26
28
|
|
29
|
+
attr_accessor :extra_sticky_locals
|
30
|
+
|
27
31
|
# Special treatment for hooks as we want to alert people of the
|
28
32
|
# changed API
|
29
33
|
attr_reader :hooks
|
@@ -48,6 +52,7 @@ class Pry
|
|
48
52
|
# @option options [Hash] :hooks The defined hook Procs
|
49
53
|
# @option options [Array<Proc>] :prompt The array of Procs to use for the prompts.
|
50
54
|
# @option options [Proc] :print The Proc to use for the 'print'
|
55
|
+
# @option options [Boolean] :quiet If true, omit the whereami banner when starting.
|
51
56
|
# component of the REPL. (see print.rb)
|
52
57
|
def initialize(options={})
|
53
58
|
refresh(options)
|
@@ -63,9 +68,9 @@ class Pry
|
|
63
68
|
def refresh(options={})
|
64
69
|
defaults = {}
|
65
70
|
attributes = [
|
66
|
-
:input, :output, :commands, :print,
|
71
|
+
:input, :output, :commands, :print, :quiet,
|
67
72
|
:exception_handler, :hooks, :custom_completions,
|
68
|
-
:prompt, :memory_size, :input_stack
|
73
|
+
:prompt, :memory_size, :input_stack, :extra_sticky_locals
|
69
74
|
]
|
70
75
|
|
71
76
|
attributes.each do |attribute|
|
@@ -155,7 +160,7 @@ class Pry
|
|
155
160
|
:_file_ => proc { last_file },
|
156
161
|
:_dir_ => proc { last_dir },
|
157
162
|
:_ => proc { last_result }
|
158
|
-
}
|
163
|
+
}.merge(extra_sticky_locals)
|
159
164
|
end
|
160
165
|
|
161
166
|
# Initialize the repl session.
|
@@ -195,13 +200,19 @@ class Pry
|
|
195
200
|
|
196
201
|
repl_prologue(target)
|
197
202
|
|
198
|
-
break_data =
|
199
|
-
|
200
|
-
|
203
|
+
break_data = nil
|
204
|
+
exception = catch(:raise_up) do
|
205
|
+
break_data = catch(:breakout) do
|
206
|
+
loop do
|
207
|
+
rep(binding_stack.last)
|
208
|
+
end
|
201
209
|
end
|
210
|
+
exception = false
|
202
211
|
end
|
203
212
|
|
204
|
-
|
213
|
+
raise exception if exception
|
214
|
+
|
215
|
+
break_data
|
205
216
|
ensure
|
206
217
|
repl_epilogue(target)
|
207
218
|
end
|
@@ -229,17 +240,6 @@ class Pry
|
|
229
240
|
def re(target=TOPLEVEL_BINDING)
|
230
241
|
target = Pry.binding_for(target)
|
231
242
|
|
232
|
-
compl = Pry::InputCompleter.build_completion_proc(target,
|
233
|
-
instance_eval(&custom_completions))
|
234
|
-
|
235
|
-
if defined? Coolline and input.is_a? Coolline
|
236
|
-
input.completion_proc = proc do |cool|
|
237
|
-
compl.call cool.completed_word
|
238
|
-
end
|
239
|
-
elsif input.respond_to? :completion_proc=
|
240
|
-
input.completion_proc = compl
|
241
|
-
end
|
242
|
-
|
243
243
|
# It's not actually redundant to inject them continually as we may have
|
244
244
|
# moved into the scope of a new Binding (e.g the user typed `cd`)
|
245
245
|
inject_sticky_locals(target)
|
@@ -282,7 +282,7 @@ class Pry
|
|
282
282
|
end
|
283
283
|
|
284
284
|
begin
|
285
|
-
break if complete_expression?(eval_string)
|
285
|
+
break if Pry::Code.complete_expression?(eval_string)
|
286
286
|
rescue SyntaxError => e
|
287
287
|
output.puts "SyntaxError: #{e.message.sub(/.*syntax error, */m, '')}"
|
288
288
|
eval_string = ""
|
@@ -334,9 +334,23 @@ class Pry
|
|
334
334
|
@indent.reset if eval_string.empty?
|
335
335
|
|
336
336
|
current_prompt = select_prompt(eval_string, target)
|
337
|
+
completion_proc = Pry::InputCompleter.build_completion_proc(target,
|
338
|
+
instance_eval(&custom_completions))
|
339
|
+
|
340
|
+
|
337
341
|
indentation = Pry.config.auto_indent ? @indent.indent_level : ''
|
338
342
|
|
339
|
-
|
343
|
+
begin
|
344
|
+
val = readline("#{current_prompt}#{indentation}", completion_proc)
|
345
|
+
|
346
|
+
# Handle <Ctrl+C> like Bash, empty the current input buffer but do not quit.
|
347
|
+
# This is only for ruby-1.9; other versions of ruby do not let you send Interrupt
|
348
|
+
# from within Readline.
|
349
|
+
rescue Interrupt => e
|
350
|
+
output.puts ""
|
351
|
+
eval_string.replace("")
|
352
|
+
return
|
353
|
+
end
|
340
354
|
|
341
355
|
# invoke handler if we receive EOF character (^D)
|
342
356
|
if !val
|
@@ -356,8 +370,8 @@ class Pry
|
|
356
370
|
original_val = "#{indentation}#{val}"
|
357
371
|
indented_val = @indent.indent(val)
|
358
372
|
|
359
|
-
if
|
360
|
-
output.print @indent.correct_indentation(current_prompt
|
373
|
+
if output.tty? && Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
|
374
|
+
output.print @indent.correct_indentation(current_prompt, indented_val, original_val.length - indented_val.length)
|
361
375
|
output.flush
|
362
376
|
end
|
363
377
|
else
|
@@ -514,17 +528,42 @@ class Pry
|
|
514
528
|
self.input = input_stack.pop
|
515
529
|
end
|
516
530
|
retry
|
531
|
+
|
532
|
+
# Interrupts are handled in r() because they need to tweak eval_string
|
533
|
+
# TODO: Refactor this baby.
|
534
|
+
rescue Interrupt
|
535
|
+
raise
|
536
|
+
|
537
|
+
# If we get a random error when trying to read a line we don't want to automatically
|
538
|
+
# retry, as the user will see a lot of error messages scroll past and be unable to do
|
539
|
+
# anything about it.
|
540
|
+
rescue RescuableException => e
|
541
|
+
puts "Error: #{e.message}"
|
542
|
+
puts "FATAL: Pry failed to get user input using `#{input}`."
|
543
|
+
puts "To fix this you may be able to pass input and output file descriptors to pry directly. e.g."
|
544
|
+
puts " Pry.config.input = STDIN"
|
545
|
+
puts " Pry.config.output = STDOUT"
|
546
|
+
puts " binding.pry"
|
547
|
+
throw(:breakout)
|
517
548
|
end
|
518
549
|
end
|
519
|
-
|
520
550
|
private :handle_read_errors
|
521
551
|
|
522
552
|
# Returns the next line of input to be used by the pry instance.
|
523
553
|
# This method should not need to be invoked directly.
|
524
554
|
# @param [String] current_prompt The prompt to use for input.
|
525
555
|
# @return [String] The next line of input.
|
526
|
-
def readline(current_prompt="> ")
|
556
|
+
def readline(current_prompt="> ", completion_proc=nil)
|
527
557
|
handle_read_errors do
|
558
|
+
|
559
|
+
if defined? Coolline and input.is_a? Coolline
|
560
|
+
input.completion_proc = proc do |cool|
|
561
|
+
completion_proc.call cool.completed_word
|
562
|
+
end
|
563
|
+
elsif input.respond_to? :completion_proc=
|
564
|
+
input.completion_proc = completion_proc
|
565
|
+
end
|
566
|
+
|
528
567
|
if input == Readline
|
529
568
|
input.readline(current_prompt, false) # false since we'll add it manually
|
530
569
|
elsif defined? Coolline and input.is_a? Coolline
|
@@ -598,50 +637,45 @@ class Pry
|
|
598
637
|
prompt_stack.size > 1 ? prompt_stack.pop : prompt
|
599
638
|
end
|
600
639
|
|
601
|
-
#
|
602
|
-
# @param [String] code The code to validate.
|
603
|
-
# @return [Boolean] Whether or not the code is a complete Ruby expression.
|
604
|
-
# @raise [SyntaxError] Any SyntaxError that does not represent incompleteness.
|
605
|
-
# @example
|
606
|
-
# complete_expression?("class Hello") #=> false
|
607
|
-
# complete_expression?("class Hello; end") #=> true
|
608
|
-
def complete_expression?(str)
|
609
|
-
if defined?(Rubinius::Melbourne19) && RUBY_VERSION =~ /^1\.9/
|
610
|
-
Rubinius::Melbourne19.parse_string(str, Pry.eval_path)
|
611
|
-
elsif defined?(Rubinius::Melbourne)
|
612
|
-
Rubinius::Melbourne.parse_string(str, Pry.eval_path)
|
613
|
-
else
|
614
|
-
catch(:valid) do
|
615
|
-
Helpers::BaseHelpers.silence_warnings do
|
616
|
-
eval("BEGIN{throw :valid}\n#{str}", binding, Pry.eval_path)
|
617
|
-
end
|
618
|
-
end
|
619
|
-
end
|
620
|
-
|
621
|
-
# Assert that a line which ends with a , is incomplete.
|
622
|
-
str !~ /[,]\z/
|
623
|
-
rescue SyntaxError => e
|
624
|
-
if incomplete_user_input_exception?(e)
|
625
|
-
false
|
626
|
-
else
|
627
|
-
raise e
|
628
|
-
end
|
629
|
-
end
|
630
|
-
|
631
|
-
# Check whether the exception indicates that the user should input more.
|
640
|
+
# Raise an exception out of Pry.
|
632
641
|
#
|
633
|
-
#
|
634
|
-
#
|
635
|
-
# @return [Boolean]
|
642
|
+
# See Kernel#raise for documentation of parameters.
|
643
|
+
# See rb_make_exception for the inbuilt implementation.
|
636
644
|
#
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
645
|
+
# This is necessary so that the raise-up command can tell the
|
646
|
+
# difference between an exception the user has decided to raise,
|
647
|
+
# and a mistake in specifying that exception.
|
648
|
+
#
|
649
|
+
# (i.e. raise-up RunThymeError.new should not be the same as
|
650
|
+
# raise-up NameError, "unititialized constant RunThymeError")
|
651
|
+
#
|
652
|
+
def raise_up_common(force, *args)
|
653
|
+
exception = if args == []
|
654
|
+
last_exception || RuntimeError.new
|
655
|
+
elsif args.length == 1 && args.first.is_a?(String)
|
656
|
+
RuntimeError.new(args.first)
|
657
|
+
elsif args.length > 3
|
658
|
+
raise ArgumentError, "wrong number of arguments"
|
659
|
+
elsif !args.first.respond_to?(:exception)
|
660
|
+
raise TypeError, "exception class/object expected"
|
661
|
+
elsif args.length === 1
|
662
|
+
args.first.exception
|
663
|
+
else
|
664
|
+
args.first.exception(args[1])
|
665
|
+
end
|
666
|
+
|
667
|
+
raise TypeError, "exception object expected" unless exception.is_a? Exception
|
668
|
+
|
669
|
+
exception.set_backtrace(args.length === 3 ? args[2] : caller(1))
|
670
|
+
|
671
|
+
if force || binding_stack.one?
|
672
|
+
binding_stack.clear
|
673
|
+
throw :raise_up, exception
|
643
674
|
else
|
644
|
-
|
675
|
+
binding_stack.pop
|
676
|
+
raise exception
|
645
677
|
end
|
646
678
|
end
|
679
|
+
def raise_up(*args); raise_up_common(false, *args); end
|
680
|
+
def raise_up!(*args); raise_up_common(true, *args); end
|
647
681
|
end
|
data/lib/pry/rbx_method.rb
CHANGED
@@ -1,20 +1,13 @@
|
|
1
1
|
class Pry
|
2
2
|
module RbxMethod
|
3
3
|
private
|
4
|
-
def core?
|
5
|
-
source_file and RbxPath.is_core_path?(source_file)
|
6
|
-
end
|
7
4
|
|
8
5
|
def core_code
|
9
|
-
MethodSource.source_helper(
|
6
|
+
MethodSource.source_helper(source_location)
|
10
7
|
end
|
11
8
|
|
12
9
|
def core_doc
|
13
|
-
MethodSource.comment_helper(
|
14
|
-
end
|
15
|
-
|
16
|
-
def core_path_line
|
17
|
-
[RbxPath.convert_path_to_full(source_file), source_line]
|
10
|
+
MethodSource.comment_helper(source_location)
|
18
11
|
end
|
19
12
|
end
|
20
13
|
end
|
data/lib/pry/version.rb
CHANGED
data/lib/pry/wrapped_module.rb
CHANGED
@@ -1,15 +1,55 @@
|
|
1
|
+
require 'pry/helpers/documentation_helpers'
|
2
|
+
|
1
3
|
class Pry
|
4
|
+
class << self
|
5
|
+
# If the given object is a `Pry::WrappedModule`, return it unaltered. If it's
|
6
|
+
# anything else, return it wrapped in a `Pry::WrappedModule` instance.
|
7
|
+
def WrappedModule(obj)
|
8
|
+
if obj.is_a? Pry::WrappedModule
|
9
|
+
obj
|
10
|
+
else
|
11
|
+
Pry::WrappedModule.new(obj)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
2
16
|
class WrappedModule
|
17
|
+
include Helpers::DocumentationHelpers
|
3
18
|
|
4
19
|
attr_reader :wrapped
|
5
20
|
private :wrapped
|
6
21
|
|
22
|
+
# Convert a string to a module.
|
23
|
+
#
|
24
|
+
# @param [String] mod_name
|
25
|
+
# @param [Binding] target The binding where the lookup takes place.
|
26
|
+
# @return [Module, nil] The module or `nil` (if conversion failed).
|
27
|
+
# @example
|
28
|
+
# Pry::WrappedModule.from_str("Pry::Code")
|
29
|
+
def self.from_str(mod_name, target=TOPLEVEL_BINDING)
|
30
|
+
kind = target.eval("defined?(#{mod_name})")
|
31
|
+
|
32
|
+
# if we dont limit it to constants then from_str could end up
|
33
|
+
# executing methods which is not good, i.e `show-source pry`
|
34
|
+
if (kind == "constant" && target.eval(mod_name).is_a?(Module))
|
35
|
+
Pry::WrappedModule.new(target.eval(mod_name))
|
36
|
+
else
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
rescue RescuableException
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
7
43
|
# Create a new WrappedModule
|
8
44
|
# @raise ArgumentError, if the argument is not a Module
|
9
45
|
# @param [Module]
|
10
46
|
def initialize(mod)
|
11
47
|
raise ArgumentError, "Tried to initialize a WrappedModule with a non-module #{mod.inspect}" unless ::Module === mod
|
12
48
|
@wrapped = mod
|
49
|
+
@host_file_lines = nil
|
50
|
+
@source = nil
|
51
|
+
@source_location = nil
|
52
|
+
@doc = nil
|
13
53
|
end
|
14
54
|
|
15
55
|
# The prefix that would appear before methods defined on this class.
|
@@ -67,7 +107,202 @@ class Pry
|
|
67
107
|
end
|
68
108
|
|
69
109
|
def respond_to?(method_name)
|
70
|
-
super || wrapped.
|
110
|
+
super || wrapped.respond_to?(method_name)
|
111
|
+
end
|
112
|
+
|
113
|
+
def yard_docs?
|
114
|
+
!!(defined?(YARD) && YARD::Registry.at(name))
|
115
|
+
end
|
116
|
+
|
117
|
+
def process_doc(doc)
|
118
|
+
process_comment_markup(strip_leading_hash_and_whitespace_from_ruby_comments(doc),
|
119
|
+
:ruby)
|
120
|
+
end
|
121
|
+
|
122
|
+
def doc
|
123
|
+
return @doc if @doc
|
124
|
+
|
125
|
+
file_name, line = source_location
|
126
|
+
|
127
|
+
if yard_docs?
|
128
|
+
from_yard = YARD::Registry.at(name)
|
129
|
+
@doc = from_yard.docstring
|
130
|
+
elsif source_location.nil?
|
131
|
+
raise CommandError, "Can't find module's source location"
|
132
|
+
else
|
133
|
+
@doc = extract_doc_for_candidate(0)
|
134
|
+
end
|
135
|
+
|
136
|
+
raise CommandError, "Can't find docs for module: #{name}." if !@doc
|
137
|
+
|
138
|
+
@doc = process_doc(@doc)
|
139
|
+
end
|
140
|
+
|
141
|
+
def doc_for_candidate(idx)
|
142
|
+
doc = extract_doc_for_candidate(idx)
|
143
|
+
raise CommandError, "Can't find docs for module: #{name}." if !doc
|
144
|
+
|
145
|
+
process_doc(doc)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Retrieve the source for the module.
|
149
|
+
def source
|
150
|
+
@source ||= source_for_candidate(0)
|
71
151
|
end
|
152
|
+
|
153
|
+
def source_for_candidate(idx)
|
154
|
+
file, line = module_source_location_for_candidate(idx)
|
155
|
+
raise CommandError, "Could not locate source for #{wrapped}!" if file.nil?
|
156
|
+
|
157
|
+
strip_leading_whitespace(Pry::Code.retrieve_complete_expression_from(lines_for_file(file)[(line - 1)..-1]))
|
158
|
+
end
|
159
|
+
|
160
|
+
def source_file
|
161
|
+
if yard_docs?
|
162
|
+
from_yard = YARD::Registry.at(name)
|
163
|
+
from_yard.file
|
164
|
+
else
|
165
|
+
source_file_for_candidate(0)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def source_line
|
170
|
+
source_line_for_candidate(0)
|
171
|
+
end
|
172
|
+
|
173
|
+
def source_file_for_candidate(idx)
|
174
|
+
Array(module_source_location_for_candidate(idx)).first
|
175
|
+
end
|
176
|
+
|
177
|
+
def source_line_for_candidate(idx)
|
178
|
+
Array(module_source_location_for_candidate(idx)).last
|
179
|
+
end
|
180
|
+
|
181
|
+
# Retrieve the source location of a module. Return value is in same
|
182
|
+
# format as Method#source_location. If the source location
|
183
|
+
# cannot be found this method returns `nil`.
|
184
|
+
#
|
185
|
+
# @param [Module] mod The module (or class).
|
186
|
+
# @return [Array<String, Fixnum>] The source location of the
|
187
|
+
# module (or class).
|
188
|
+
def source_location
|
189
|
+
@source_location ||= module_source_location_for_candidate(0)
|
190
|
+
rescue Pry::RescuableException
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
|
194
|
+
# memoized lines for file
|
195
|
+
def lines_for_file(file)
|
196
|
+
@lines_for_file ||= {}
|
197
|
+
|
198
|
+
if file == Pry.eval_path
|
199
|
+
@lines_for_file[file] ||= Pry.line_buffer.drop(1)
|
200
|
+
else
|
201
|
+
@lines_for_file[file] ||= File.readlines(file)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def module_source_location_for_candidate(idx)
|
206
|
+
mod_type_string = wrapped.class.to_s.downcase
|
207
|
+
file, line = method_source_location_for_candidate(idx)
|
208
|
+
|
209
|
+
return nil if !file.is_a?(String)
|
210
|
+
|
211
|
+
class_regex1 = /#{mod_type_string}\s*(\w*)(::)?#{wrapped.name.split(/::/).last}/
|
212
|
+
class_regex2 = /(::)?#{wrapped.name.split(/::/).last}\s*?=\s*?#{wrapped.class}/
|
213
|
+
|
214
|
+
host_file_lines = lines_for_file(file)
|
215
|
+
|
216
|
+
search_lines = host_file_lines[0..(line - 2)]
|
217
|
+
idx = search_lines.rindex { |v| class_regex1 =~ v || class_regex2 =~ v }
|
218
|
+
|
219
|
+
source_location = [file, idx + 1]
|
220
|
+
rescue Pry::RescuableException
|
221
|
+
nil
|
222
|
+
end
|
223
|
+
|
224
|
+
def extract_doc
|
225
|
+
extract_doc_for_candidate(0)
|
226
|
+
end
|
227
|
+
|
228
|
+
def extract_doc_for_candidate(idx)
|
229
|
+
file_name, line = module_source_location_for_candidate(idx)
|
230
|
+
|
231
|
+
buffer = ""
|
232
|
+
lines_for_file(source_file_for_candidate(idx))[0..(line - 2)].each do |line|
|
233
|
+
# Add any line that is a valid ruby comment,
|
234
|
+
# but clear as soon as we hit a non comment line.
|
235
|
+
if (line =~ /^\s*#/) || (line =~ /^\s*$/)
|
236
|
+
buffer << line.lstrip
|
237
|
+
else
|
238
|
+
buffer.replace("")
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
buffer
|
243
|
+
end
|
244
|
+
|
245
|
+
# FIXME: this method is also found in Pry::Method
|
246
|
+
def safe_send(obj, method, *args, &block)
|
247
|
+
(Module === obj ? Module : Object).instance_method(method).bind(obj).call(*args, &block)
|
248
|
+
end
|
249
|
+
|
250
|
+
# FIXME: a variant of this method is also found in Pry::Method
|
251
|
+
def all_from_common(mod, method_type)
|
252
|
+
%w(public protected private).map do |visibility|
|
253
|
+
safe_send(mod, :"#{visibility}_#{method_type}s", false).select do |method_name|
|
254
|
+
if method_type == :method
|
255
|
+
safe_send(mod, method_type, method_name).owner == class << mod; self; end
|
256
|
+
else
|
257
|
+
safe_send(mod, method_type, method_name).owner == mod
|
258
|
+
end
|
259
|
+
end.map do |method_name|
|
260
|
+
Pry::Method.new(safe_send(mod, method_type, method_name), :visibility => visibility.to_sym)
|
261
|
+
end
|
262
|
+
end.flatten
|
263
|
+
end
|
264
|
+
|
265
|
+
def all_methods_for(mod)
|
266
|
+
all_from_common(mod, :instance_method) + all_from_common(mod, :method)
|
267
|
+
end
|
268
|
+
|
269
|
+
def all_source_locations_by_popularity
|
270
|
+
return @all_source_locations_by_popularity if @all_source_locations_by_popularity
|
271
|
+
|
272
|
+
ims = all_methods_for(wrapped)
|
273
|
+
|
274
|
+
ims.reject! do |v|
|
275
|
+
begin
|
276
|
+
v.alias? || v.source_location.nil?
|
277
|
+
rescue Pry::RescuableException
|
278
|
+
true
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
@all_source_locations_by_popularity = ims.group_by { |v| Array(v.source_location).first }.
|
283
|
+
sort_by { |k, v| -v.size }
|
284
|
+
end
|
285
|
+
|
286
|
+
def method_candidates
|
287
|
+
@method_candidtates ||= all_source_locations_by_popularity.map do |group|
|
288
|
+
sorted_by_lowest_line_number = group.last.sort_by(&:source_line)
|
289
|
+
best_candidate_for_group = sorted_by_lowest_line_number.first
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def number_of_candidates
|
294
|
+
method_candidates.count
|
295
|
+
end
|
296
|
+
|
297
|
+
def method_source_location_for_candidate(idx)
|
298
|
+
file, line = method_candidates[idx].source_location
|
299
|
+
|
300
|
+
if file && RbxPath.is_core_path?(file)
|
301
|
+
file = RbxPath.convert_path_to_full(file)
|
302
|
+
end
|
303
|
+
|
304
|
+
[file, line]
|
305
|
+
end
|
306
|
+
|
72
307
|
end
|
73
308
|
end
|