pry 0.10.0.pre4-x64-mingw32

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 (131) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +702 -0
  3. data/LICENSE +25 -0
  4. data/README.md +406 -0
  5. data/bin/pry +16 -0
  6. data/lib/pry.rb +161 -0
  7. data/lib/pry/cli.rb +220 -0
  8. data/lib/pry/code.rb +341 -0
  9. data/lib/pry/code/code_file.rb +103 -0
  10. data/lib/pry/code/code_range.rb +71 -0
  11. data/lib/pry/code/loc.rb +92 -0
  12. data/lib/pry/code_object.rb +172 -0
  13. data/lib/pry/color_printer.rb +55 -0
  14. data/lib/pry/command.rb +692 -0
  15. data/lib/pry/command_set.rb +443 -0
  16. data/lib/pry/commands.rb +6 -0
  17. data/lib/pry/commands/amend_line.rb +99 -0
  18. data/lib/pry/commands/bang.rb +20 -0
  19. data/lib/pry/commands/bang_pry.rb +17 -0
  20. data/lib/pry/commands/cat.rb +62 -0
  21. data/lib/pry/commands/cat/abstract_formatter.rb +27 -0
  22. data/lib/pry/commands/cat/exception_formatter.rb +77 -0
  23. data/lib/pry/commands/cat/file_formatter.rb +67 -0
  24. data/lib/pry/commands/cat/input_expression_formatter.rb +43 -0
  25. data/lib/pry/commands/cd.rb +41 -0
  26. data/lib/pry/commands/change_inspector.rb +27 -0
  27. data/lib/pry/commands/change_prompt.rb +26 -0
  28. data/lib/pry/commands/code_collector.rb +165 -0
  29. data/lib/pry/commands/disable_pry.rb +27 -0
  30. data/lib/pry/commands/disabled_commands.rb +2 -0
  31. data/lib/pry/commands/easter_eggs.rb +112 -0
  32. data/lib/pry/commands/edit.rb +195 -0
  33. data/lib/pry/commands/edit/exception_patcher.rb +25 -0
  34. data/lib/pry/commands/edit/file_and_line_locator.rb +36 -0
  35. data/lib/pry/commands/exit.rb +42 -0
  36. data/lib/pry/commands/exit_all.rb +29 -0
  37. data/lib/pry/commands/exit_program.rb +23 -0
  38. data/lib/pry/commands/find_method.rb +193 -0
  39. data/lib/pry/commands/fix_indent.rb +19 -0
  40. data/lib/pry/commands/gem_cd.rb +26 -0
  41. data/lib/pry/commands/gem_install.rb +32 -0
  42. data/lib/pry/commands/gem_list.rb +33 -0
  43. data/lib/pry/commands/gem_open.rb +29 -0
  44. data/lib/pry/commands/gist.rb +101 -0
  45. data/lib/pry/commands/help.rb +164 -0
  46. data/lib/pry/commands/hist.rb +180 -0
  47. data/lib/pry/commands/import_set.rb +22 -0
  48. data/lib/pry/commands/install_command.rb +53 -0
  49. data/lib/pry/commands/jump_to.rb +29 -0
  50. data/lib/pry/commands/list_inspectors.rb +35 -0
  51. data/lib/pry/commands/list_prompts.rb +35 -0
  52. data/lib/pry/commands/ls.rb +114 -0
  53. data/lib/pry/commands/ls/constants.rb +47 -0
  54. data/lib/pry/commands/ls/formatter.rb +49 -0
  55. data/lib/pry/commands/ls/globals.rb +48 -0
  56. data/lib/pry/commands/ls/grep.rb +21 -0
  57. data/lib/pry/commands/ls/instance_vars.rb +39 -0
  58. data/lib/pry/commands/ls/interrogatable.rb +18 -0
  59. data/lib/pry/commands/ls/jruby_hacks.rb +49 -0
  60. data/lib/pry/commands/ls/local_names.rb +35 -0
  61. data/lib/pry/commands/ls/local_vars.rb +39 -0
  62. data/lib/pry/commands/ls/ls_entity.rb +70 -0
  63. data/lib/pry/commands/ls/methods.rb +57 -0
  64. data/lib/pry/commands/ls/methods_helper.rb +46 -0
  65. data/lib/pry/commands/ls/self_methods.rb +32 -0
  66. data/lib/pry/commands/nesting.rb +25 -0
  67. data/lib/pry/commands/play.rb +103 -0
  68. data/lib/pry/commands/pry_backtrace.rb +25 -0
  69. data/lib/pry/commands/pry_version.rb +17 -0
  70. data/lib/pry/commands/raise_up.rb +32 -0
  71. data/lib/pry/commands/reload_code.rb +62 -0
  72. data/lib/pry/commands/reset.rb +18 -0
  73. data/lib/pry/commands/ri.rb +60 -0
  74. data/lib/pry/commands/save_file.rb +61 -0
  75. data/lib/pry/commands/shell_command.rb +48 -0
  76. data/lib/pry/commands/shell_mode.rb +25 -0
  77. data/lib/pry/commands/show_doc.rb +83 -0
  78. data/lib/pry/commands/show_info.rb +195 -0
  79. data/lib/pry/commands/show_input.rb +17 -0
  80. data/lib/pry/commands/show_source.rb +50 -0
  81. data/lib/pry/commands/simple_prompt.rb +22 -0
  82. data/lib/pry/commands/stat.rb +40 -0
  83. data/lib/pry/commands/switch_to.rb +23 -0
  84. data/lib/pry/commands/toggle_color.rb +24 -0
  85. data/lib/pry/commands/watch_expression.rb +105 -0
  86. data/lib/pry/commands/watch_expression/expression.rb +38 -0
  87. data/lib/pry/commands/whereami.rb +190 -0
  88. data/lib/pry/commands/wtf.rb +57 -0
  89. data/lib/pry/config.rb +24 -0
  90. data/lib/pry/config/behavior.rb +139 -0
  91. data/lib/pry/config/convenience.rb +26 -0
  92. data/lib/pry/config/default.rb +165 -0
  93. data/lib/pry/core_extensions.rb +131 -0
  94. data/lib/pry/editor.rb +133 -0
  95. data/lib/pry/exceptions.rb +77 -0
  96. data/lib/pry/helpers.rb +5 -0
  97. data/lib/pry/helpers/base_helpers.rb +113 -0
  98. data/lib/pry/helpers/command_helpers.rb +156 -0
  99. data/lib/pry/helpers/documentation_helpers.rb +75 -0
  100. data/lib/pry/helpers/options_helpers.rb +27 -0
  101. data/lib/pry/helpers/table.rb +109 -0
  102. data/lib/pry/helpers/text.rb +107 -0
  103. data/lib/pry/history.rb +125 -0
  104. data/lib/pry/history_array.rb +121 -0
  105. data/lib/pry/hooks.rb +230 -0
  106. data/lib/pry/indent.rb +406 -0
  107. data/lib/pry/input_completer.rb +242 -0
  108. data/lib/pry/input_lock.rb +132 -0
  109. data/lib/pry/inspector.rb +27 -0
  110. data/lib/pry/last_exception.rb +61 -0
  111. data/lib/pry/method.rb +546 -0
  112. data/lib/pry/method/disowned.rb +53 -0
  113. data/lib/pry/method/patcher.rb +125 -0
  114. data/lib/pry/method/weird_method_locator.rb +186 -0
  115. data/lib/pry/module_candidate.rb +136 -0
  116. data/lib/pry/object_path.rb +82 -0
  117. data/lib/pry/output.rb +50 -0
  118. data/lib/pry/pager.rb +234 -0
  119. data/lib/pry/plugins.rb +103 -0
  120. data/lib/pry/prompt.rb +26 -0
  121. data/lib/pry/pry_class.rb +375 -0
  122. data/lib/pry/pry_instance.rb +654 -0
  123. data/lib/pry/rbx_path.rb +22 -0
  124. data/lib/pry/repl.rb +202 -0
  125. data/lib/pry/repl_file_loader.rb +74 -0
  126. data/lib/pry/rubygem.rb +82 -0
  127. data/lib/pry/terminal.rb +79 -0
  128. data/lib/pry/test/helper.rb +170 -0
  129. data/lib/pry/version.rb +3 -0
  130. data/lib/pry/wrapped_module.rb +373 -0
  131. metadata +248 -0
@@ -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
@@ -0,0 +1,125 @@
1
+ class Pry
2
+ class Method
3
+ class Patcher
4
+ attr_accessor :method
5
+
6
+ @@source_cache = {}
7
+
8
+ def initialize(method)
9
+ @method = method
10
+ end
11
+
12
+ def self.code_for(filename)
13
+ @@source_cache[filename]
14
+ end
15
+
16
+ # perform the patch
17
+ def patch_in_ram(source)
18
+ if method.alias?
19
+ with_method_transaction do
20
+ redefine source
21
+ end
22
+ else
23
+ redefine source
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def redefine(source)
30
+ @@source_cache[cache_key] = source
31
+ TOPLEVEL_BINDING.eval wrap(source), cache_key
32
+ end
33
+
34
+ def cache_key
35
+ "pry-redefined(0x#{method.owner.object_id.to_s(16)}##{method.name})"
36
+ end
37
+
38
+ # Run some code ensuring that at the end target#meth_name will not have changed.
39
+ #
40
+ # When we're redefining aliased methods we will overwrite the method at the
41
+ # unaliased name (so that super continues to work). By wrapping that code in a
42
+ # transation we make that not happen, which means that alias_method_chains, etc.
43
+ # continue to work.
44
+ #
45
+ # @param [String] meth_name The method name before aliasing
46
+ # @param [Module] target The owner of the method
47
+
48
+ def with_method_transaction
49
+ temp_name = "__pry_#{method.original_name}__"
50
+ method = self.method
51
+ method.owner.class_eval do
52
+ alias_method temp_name, method.original_name
53
+ yield
54
+ alias_method method.name, method.original_name
55
+ alias_method method.original_name, temp_name
56
+ end
57
+
58
+ ensure
59
+ method.send(:remove_method, temp_name) rescue nil
60
+ end
61
+
62
+ # Update the definition line so that it can be eval'd directly on the Method's
63
+ # owner instead of from the original context.
64
+ #
65
+ # In particular this takes `def self.foo` and turns it into `def foo` so that we
66
+ # don't end up creating the method on the singleton class of the singleton class
67
+ # by accident.
68
+ #
69
+ # This is necessarily done by String manipulation because we can't find out what
70
+ # syntax is needed for the argument list by ruby-level introspection.
71
+ #
72
+ # @param [String] line The original definition line. e.g. def self.foo(bar, baz=1)
73
+ # @return [String] The new definition line. e.g. def foo(bar, baz=1)
74
+ def definition_for_owner(line)
75
+ if line =~ /\Adef (?:.*?\.)?#{Regexp.escape(method.original_name)}(?=[\(\s;]|$)/
76
+ "def #{method.original_name}#{$'}"
77
+ else
78
+ raise CommandError, "Could not find original `def #{method.original_name}` line to patch."
79
+ end
80
+ end
81
+
82
+ # Apply wrap_for_owner and wrap_for_nesting successively to `source`
83
+ # @param [String] source
84
+ # @return [String] The wrapped source.
85
+ def wrap(source)
86
+ wrap_for_nesting(wrap_for_owner(source))
87
+ end
88
+
89
+ # Update the source code so that when it has the right owner when eval'd.
90
+ #
91
+ # This (combined with definition_for_owner) is backup for the case that
92
+ # wrap_for_nesting fails, to ensure that the method will stil be defined in
93
+ # the correct place.
94
+ #
95
+ # @param [String] source The source to wrap
96
+ # @return [String]
97
+ def wrap_for_owner(source)
98
+ Pry.current[:pry_owner] = method.owner
99
+ owner_source = definition_for_owner(source)
100
+ visibility_fix = "#{method.visibility.to_s} #{method.name.to_sym.inspect}"
101
+ "Pry.current[:pry_owner].class_eval do; #{owner_source}\n#{visibility_fix}\nend"
102
+ end
103
+
104
+ # Update the new source code to have the correct Module.nesting.
105
+ #
106
+ # This method uses syntactic analysis of the original source file to determine
107
+ # the new nesting, so that we can tell the difference between:
108
+ #
109
+ # class A; def self.b; end; end
110
+ # class << A; def b; end; end
111
+ #
112
+ # The resulting code should be evaluated in the TOPLEVEL_BINDING.
113
+ #
114
+ # @param [String] source The source to wrap.
115
+ # @return [String]
116
+ def wrap_for_nesting(source)
117
+ nesting = Pry::Code.from_file(method.source_file).nesting_at(method.source_line)
118
+
119
+ (nesting + [source] + nesting.map{ "end" } + [""]).join(";")
120
+ rescue Pry::Indent::UnparseableNestingError
121
+ source
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,186 @@
1
+ class Pry
2
+ class Method
3
+
4
+ # This class is responsible for locating the *real* `Pry::Method`
5
+ # object captured by a binding.
6
+ #
7
+ # Given a `Binding` from inside a method and a 'seed' Pry::Method object,
8
+ # there are primarily two situations where the seed method doesn't match
9
+ # the Binding:
10
+ # 1. The Pry::Method is from a subclass 2. The Pry::Method represents a method of the same name
11
+ # while the original was renamed to something else. For 1. we
12
+ # search vertically up the inheritance chain,
13
+ # and for 2. we search laterally along the object's method table.
14
+ #
15
+ # When we locate the method that matches the Binding we wrap it in
16
+ # Pry::Method and return it, or return nil if we fail.
17
+ class WeirdMethodLocator
18
+ class << self
19
+
20
+ # Whether the given method object matches the associated binding.
21
+ # If the method object does not match the binding, then it's
22
+ # most likely not the method captured by the binding, and we
23
+ # must commence a search.
24
+ #
25
+ # @param [Pry::Method] method
26
+ # @param [Binding] b
27
+ # @return [Boolean]
28
+ def normal_method?(method, b)
29
+ method && (method.source_file && method.source_range rescue false) &&
30
+ File.expand_path(method.source_file) == File.expand_path(b.eval('__FILE__')) &&
31
+ method.source_range.include?(b.eval('__LINE__'))
32
+ end
33
+
34
+ def weird_method?(method, b)
35
+ !normal_method?(method, b)
36
+ end
37
+ end
38
+
39
+ attr_accessor :method
40
+ attr_accessor :target
41
+
42
+ # @param [Pry::Method] method The seed method.
43
+ # @param [Binding] target The Binding that captures the method
44
+ # we want to locate.
45
+ def initialize(method, target)
46
+ @method, @target = method, target
47
+ end
48
+
49
+ # @return [Pry::Method, nil] The Pry::Method that matches the
50
+ # given binding.
51
+ def get_method
52
+ find_method_in_superclass || find_renamed_method
53
+ end
54
+
55
+ # @return [Boolean] Whether the Pry::Method is unrecoverable
56
+ # This usually happens when the method captured by the Binding
57
+ # has been subsequently deleted.
58
+ def lost_method?
59
+ !!(get_method.nil? && renamed_method_source_location)
60
+ end
61
+
62
+ private
63
+
64
+ def normal_method?(method)
65
+ self.class.normal_method?(method, target)
66
+ end
67
+
68
+ def target_self
69
+ target.eval('self')
70
+ end
71
+
72
+ def target_file
73
+ pry_file? ? target.eval('__FILE__') : File.expand_path(target.eval('__FILE__'))
74
+ end
75
+
76
+ def target_line
77
+ target.eval('__LINE__')
78
+ end
79
+
80
+ def pry_file?
81
+ Pry.eval_path == target.eval('__FILE__')
82
+ end
83
+
84
+ # it's possible in some cases that the method we find by this approach is a sub-method of
85
+ # the one we're currently in, consider:
86
+ #
87
+ # class A; def b; binding.pry; end; end
88
+ # class B < A; def b; super; end; end
89
+ #
90
+ # Given that we can normally find the source_range of methods, and that we know which
91
+ # __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
92
+ #
93
+ # This obviously won't work if the source is unavaiable for some reason, or if both
94
+ # methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
95
+ # is broken.
96
+ #
97
+ # @return [Pry::Method, nil] The Pry::Method representing the
98
+ # superclass method.
99
+ def find_method_in_superclass
100
+ guess = method
101
+
102
+ while guess
103
+ # needs rescue if this is a Disowned method or a C method or something...
104
+ # TODO: Fix up the exception handling so we don't need a bare rescue
105
+ if normal_method?(guess)
106
+ return guess
107
+ else
108
+ guess = guess.super
109
+ end
110
+ 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
+ nil
116
+ end
117
+
118
+ # This is the case where the name of a method has changed
119
+ # (via alias_method) so we locate the Method object for the
120
+ # renamed method.
121
+ #
122
+ # @return [Pry::Method, nil] The Pry::Method representing the
123
+ # renamed method
124
+ def find_renamed_method
125
+ return if !valid_file?(target_file)
126
+ alias_name = all_methods_for(target_self).find do |v|
127
+ expanded_source_location(target_self.method(v).source_location) == renamed_method_source_location
128
+ end
129
+
130
+ alias_name && Pry::Method(target_self.method(alias_name))
131
+ end
132
+
133
+ def expanded_source_location(sl)
134
+ return if !sl
135
+
136
+ if pry_file?
137
+ sl
138
+ else
139
+ [File.expand_path(sl.first), sl.last]
140
+ end
141
+ end
142
+
143
+ # Use static analysis to locate the start of the method definition.
144
+ # We have the `__FILE__` and `__LINE__` from the binding and the
145
+ # original name of the method so we search up until we find a
146
+ # def/define_method, etc defining a method of the appropriate name.
147
+ #
148
+ # @return [Array<String, Fixnum>] The `source_location` of the
149
+ # renamed method
150
+ def renamed_method_source_location
151
+ return @original_method_source_location if defined?(@original_method_source_location)
152
+
153
+ source_index = lines_for_file(target_file)[0..(target_line - 1)].rindex do |v|
154
+ Pry::Method.method_definition?(method.name, v)
155
+ end
156
+
157
+ @original_method_source_location = source_index &&
158
+ [target_file, index_to_line_number(source_index)]
159
+ end
160
+
161
+ def index_to_line_number(index)
162
+ # Pry.line_buffer is 0-indexed
163
+ pry_file? ? index : index + 1
164
+ end
165
+
166
+ def valid_file?(file)
167
+ (File.exists?(file) && !File.directory?(file)) || Pry.eval_path == file
168
+ end
169
+
170
+ def lines_for_file(file)
171
+ @lines_for_file ||= {}
172
+ @lines_for_file[file] ||= if Pry.eval_path == file
173
+ Pry.line_buffer
174
+ else
175
+ File.readlines(file)
176
+ end
177
+ end
178
+
179
+ def all_methods_for(obj)
180
+ obj.public_methods(false) +
181
+ obj.private_methods(false) +
182
+ obj.protected_methods(false)
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,136 @@
1
+ require 'pry/helpers/documentation_helpers'
2
+ require 'forwardable'
3
+
4
+ class Pry
5
+ class WrappedModule
6
+
7
+ # This class represents a single candidate for a module/class definition.
8
+ # It provides access to the source, documentation, line and file
9
+ # for a monkeypatch (reopening) of a class/module.
10
+ class Candidate
11
+ include Pry::Helpers::DocumentationHelpers
12
+ include Pry::CodeObject::Helpers
13
+ extend Forwardable
14
+
15
+ # @return [String] The file where the module definition is located.
16
+ attr_reader :file
17
+ alias_method :source_file, :file
18
+
19
+ # @return [Fixnum] The line where the module definition is located.
20
+ attr_reader :line
21
+ alias_method :source_line, :line
22
+
23
+ # Methods to delegate to associated `Pry::WrappedModule
24
+ # instance`.
25
+ private_delegates = [:lines_for_file, :method_candidates,
26
+ :yard_docs?]
27
+
28
+ public_delegates = [:wrapped, :module?, :class?, :name, :nonblank_name,
29
+ :number_of_candidates]
30
+
31
+ def_delegators :@wrapper, *(private_delegates + public_delegates)
32
+ private(*private_delegates)
33
+ public(*public_delegates)
34
+
35
+ # @raise [Pry::CommandError] If `rank` is out of bounds.
36
+ # @param [Pry::WrappedModule] wrapper The associated
37
+ # `Pry::WrappedModule` instance that owns the candidates.
38
+ # @param [Fixnum] rank The rank of the candidate to
39
+ # retrieve. Passing 0 returns 'primary candidate' (the candidate with largest
40
+ # number of methods), passing 1 retrieves candidate with
41
+ # second largest number of methods, and so on, up to
42
+ # `Pry::WrappedModule#number_of_candidates() - 1`
43
+ def initialize(wrapper, rank)
44
+ @wrapper = wrapper
45
+
46
+ if number_of_candidates <= 0
47
+ raise CommandError, "Cannot find a definition for #{name} module!"
48
+ elsif rank > (number_of_candidates - 1)
49
+ raise CommandError, "No such module candidate. Allowed candidates range is from 0 to #{number_of_candidates - 1}"
50
+ end
51
+
52
+ @rank = rank
53
+ @file, @line = source_location
54
+ end
55
+
56
+ # @raise [Pry::CommandError] If source code cannot be found.
57
+ # @return [String] The source for the candidate, i.e the
58
+ # complete module/class definition.
59
+ def source
60
+ return nil if file.nil?
61
+ return @source if @source
62
+
63
+ @source = strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line, number_of_lines_in_first_chunk))
64
+ end
65
+
66
+ # @raise [Pry::CommandError] If documentation cannot be found.
67
+ # @return [String] The documentation for the candidate.
68
+ def doc
69
+ return nil if file.nil?
70
+ return @doc if @doc
71
+
72
+ @doc = get_comment_content(Pry::Code.from_file(file).comment_describing(line))
73
+ end
74
+
75
+ # @return [Array, nil] A `[String, Fixnum]` pair representing the
76
+ # source location (file and line) for the candidate or `nil`
77
+ # if no source location found.
78
+ def source_location
79
+ return @source_location if @source_location
80
+
81
+ file, line = first_method_source_location
82
+ return nil if !file.is_a?(String)
83
+
84
+ @source_location = [file, first_line_of_module_definition(file, line)]
85
+ rescue Pry::RescuableException
86
+ nil
87
+ end
88
+
89
+ private
90
+
91
+ # Locate the first line of the module definition.
92
+ # @param [String] file The file that contains the module
93
+ # definition (somewhere).
94
+ # @param [Fixnum] line The module definition should appear
95
+ # before this line (if it exists).
96
+ # @return [Fixnum] The line where the module is defined. This
97
+ # line number is one-indexed.
98
+ def first_line_of_module_definition(file, line)
99
+ searchable_lines = lines_for_file(file)[0..(line - 2)]
100
+ searchable_lines.rindex { |v| class_regexes.any? { |r| r =~ v } } + 1
101
+ end
102
+
103
+ def class_regexes
104
+ mod_type_string = wrapped.class.to_s.downcase
105
+ [/^\s*#{mod_type_string}\s+(?:(?:\w*)::)*?#{wrapped.name.split(/::/).last}/,
106
+ /^\s*(::)?#{wrapped.name.split(/::/).last}\s*?=\s*?#{wrapped.class}/,
107
+ /^\s*(::)?#{wrapped.name.split(/::/).last}\.(class|instance)_eval/]
108
+ end
109
+
110
+ # This method is used by `Candidate#source_location` as a
111
+ # starting point for the search for the candidate's definition.
112
+ # @return [Array] The source location of the base method used to
113
+ # calculate the source location of the candidate.
114
+ def first_method_source_location
115
+ @first_method_source_location ||= method_candidates[@rank].first.source_location
116
+ end
117
+
118
+ # @return [Array] The source location of the last method in this
119
+ # candidate's module definition.
120
+ def last_method_source_location
121
+ @end_method_source_location ||= method_candidates[@rank].last.source_location
122
+ end
123
+
124
+ # Return the number of lines between the start of the class definition
125
+ # and the start of the last method. We use this value so we can
126
+ # quickly grab these lines from the file (without having to
127
+ # check each intervening line for validity, which is expensive) speeding up source extraction.
128
+ # @return [Fixum] Number of lines.
129
+ def number_of_lines_in_first_chunk
130
+ end_method_line = last_method_source_location.last
131
+
132
+ end_method_line - line
133
+ end
134
+ end
135
+ end
136
+ end