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.
Files changed (46) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG +26 -0
  3. data/README.markdown +3 -3
  4. data/lib/pry.rb +9 -1
  5. data/lib/pry/code.rb +93 -0
  6. data/lib/pry/command.rb +35 -22
  7. data/lib/pry/command_set.rb +97 -67
  8. data/lib/pry/config.rb +63 -10
  9. data/lib/pry/core_extensions.rb +24 -18
  10. data/lib/pry/default_commands/context.rb +72 -12
  11. data/lib/pry/default_commands/easter_eggs.rb +4 -0
  12. data/lib/pry/default_commands/editing.rb +43 -15
  13. data/lib/pry/default_commands/find_method.rb +171 -0
  14. data/lib/pry/default_commands/hist.rb +10 -6
  15. data/lib/pry/default_commands/introspection.rb +183 -30
  16. data/lib/pry/default_commands/ls.rb +77 -7
  17. data/lib/pry/default_commands/misc.rb +1 -0
  18. data/lib/pry/default_commands/navigating_pry.rb +1 -8
  19. data/lib/pry/helpers/base_helpers.rb +10 -2
  20. data/lib/pry/helpers/command_helpers.rb +23 -40
  21. data/lib/pry/helpers/documentation_helpers.rb +65 -0
  22. data/lib/pry/indent.rb +17 -4
  23. data/lib/pry/method.rb +61 -45
  24. data/lib/pry/pry_class.rb +9 -3
  25. data/lib/pry/pry_instance.rb +99 -65
  26. data/lib/pry/rbx_method.rb +2 -9
  27. data/lib/pry/version.rb +1 -1
  28. data/lib/pry/wrapped_module.rb +236 -1
  29. data/pry.gemspec +5 -5
  30. data/test/helper.rb +22 -0
  31. data/test/test_command.rb +29 -0
  32. data/test/test_command_integration.rb +193 -10
  33. data/test/test_command_set.rb +82 -17
  34. data/test/test_default_commands/test_cd.rb +16 -0
  35. data/test/test_default_commands/test_context.rb +61 -0
  36. data/test/test_default_commands/test_documentation.rb +163 -43
  37. data/test/test_default_commands/test_find_method.rb +42 -0
  38. data/test/test_default_commands/test_input.rb +32 -0
  39. data/test/test_default_commands/test_introspection.rb +50 -197
  40. data/test/test_default_commands/test_ls.rb +22 -0
  41. data/test/test_default_commands/test_show_source.rb +306 -0
  42. data/test/test_pry.rb +3 -3
  43. data/test/test_pry_defaults.rb +21 -0
  44. data/test/test_sticky_locals.rb +81 -1
  45. data/test/test_syntax_checking.rb +7 -6
  46. metadata +22 -14
@@ -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, # e.g. VERSION, ARGF
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.is_a?(Binding)
354
+ if Binding === target
349
355
  target
350
356
  else
351
357
  if TOPLEVEL_BINDING.eval('self') == target
@@ -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 = catch(:breakout) do
199
- loop do
200
- rep(binding_stack.last)
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
- break_data || nil
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
- val = readline("#{current_prompt}#{indentation}")
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 original_val != indented_val && output.tty? && Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
360
- output.print @indent.correct_indentation(current_prompt + indented_val, original_val.length - indented_val.length)
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
- # Determine if a string of code is a complete Ruby expression.
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
- # @param [SyntaxError] the exception object that was raised.
634
- # @param [Array<String>] The stack frame of the function that executed eval.
635
- # @return [Boolean]
642
+ # See Kernel#raise for documentation of parameters.
643
+ # See rb_make_exception for the inbuilt implementation.
636
644
  #
637
- def incomplete_user_input_exception?(ex)
638
- case ex.message
639
- when /unexpected (\$end|end-of-file|END_OF_FILE)/, # mri, jruby, ironruby
640
- /unterminated (quoted string|string|regexp) meets end of file/, # "quoted string" is ironruby
641
- /missing 'end' for/, /: expecting '[})\]]'$/, /can't find string ".*" anywhere before EOF/, /expecting keyword_end/ # rbx
642
- true
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
- false
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
@@ -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(core_path_line)
6
+ MethodSource.source_helper(source_location)
10
7
  end
11
8
 
12
9
  def core_doc
13
- MethodSource.comment_helper(core_path_line)
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
@@ -1,3 +1,3 @@
1
1
  class Pry
2
- VERSION = "0.9.8.4"
2
+ VERSION = "0.9.9"
3
3
  end
@@ -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.send(method_name, *args, &block)
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