pry 0.10.0.pre4-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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,220 @@
1
+ class Pry
2
+
3
+ # Manage the processing of command line options
4
+ class CLI
5
+
6
+ NoOptionsError = Class.new(StandardError)
7
+
8
+ class << self
9
+
10
+ # @return [Proc] The Proc defining the valid command line options.
11
+ attr_accessor :options
12
+
13
+ # @return [Array] The Procs that process the parsed options. Plugins can
14
+ # utilize this facility in order to add and process their own Pry
15
+ # options.
16
+ attr_accessor :option_processors
17
+
18
+ # @return [Array<String>] The input array of strings to process
19
+ # as CLI options.
20
+ attr_accessor :input_args
21
+
22
+ # Add another set of CLI options (a Slop block)
23
+ def add_options(&block)
24
+ if options
25
+ old_options = options
26
+ self.options = proc do
27
+ instance_exec(&old_options)
28
+ instance_exec(&block)
29
+ end
30
+ else
31
+ self.options = block
32
+ end
33
+
34
+ self
35
+ end
36
+
37
+ # Bring in options defined in plugins
38
+ def add_plugin_options
39
+ Pry.plugins.values.each do |plugin|
40
+ plugin.load_cli_options
41
+ end
42
+
43
+ self
44
+ end
45
+
46
+ # Add a block responsible for processing parsed options.
47
+ def add_option_processor(&block)
48
+ self.option_processors ||= []
49
+ option_processors << block
50
+
51
+ self
52
+ end
53
+
54
+ # Clear `options` and `option_processors`
55
+ def reset
56
+ self.options = nil
57
+ self.option_processors = nil
58
+ end
59
+
60
+ def parse_options(args=ARGV)
61
+ unless options
62
+ raise NoOptionsError, "No command line options defined! Use Pry::CLI.add_options to add command line options."
63
+ end
64
+
65
+ self.input_args = args
66
+
67
+ begin
68
+ opts = Slop.parse!(
69
+ args,
70
+ :help => true,
71
+ :multiple_switches => false,
72
+ :strict => true,
73
+ &options
74
+ )
75
+ rescue Slop::InvalidOptionError
76
+ # Display help message on unknown switches and exit.
77
+ puts Slop.new(&options)
78
+ exit
79
+ end
80
+
81
+ # Option processors are optional.
82
+ if option_processors
83
+ option_processors.each { |processor| processor.call(opts) }
84
+ end
85
+
86
+ self
87
+ end
88
+
89
+ end
90
+
91
+ reset
92
+ end
93
+ end
94
+
95
+
96
+ # String that is built to be executed on start (created by -e and -exec switches)
97
+ exec_string = ""
98
+
99
+ # Bring in options defined by plugins
100
+ Slop.new do
101
+ on "no-plugins" do
102
+ Pry.config.should_load_plugins = false
103
+ end
104
+ end.parse(ARGV.dup)
105
+
106
+ if Pry.config.should_load_plugins
107
+ Pry::CLI.add_plugin_options
108
+ end
109
+
110
+ # The default Pry command line options (before plugin options are included)
111
+ Pry::CLI.add_options do
112
+ banner %{Usage: pry [OPTIONS]
113
+ Start a Pry session.
114
+ See http://pryrepl.org/ for more information.
115
+ Copyright (c) 2013 John Mair (banisterfiend)
116
+ --
117
+ }
118
+ on :e, :exec=, "A line of code to execute in context before the session starts" do |input|
119
+ exec_string << input << "\n"
120
+ end
121
+
122
+ on "no-pager", "Disable pager for long output" do
123
+ Pry.config.pager = false
124
+ end
125
+
126
+ on "no-history", "Disable history loading" do
127
+ Pry.config.history.should_load = false
128
+ end
129
+
130
+ on "no-color", "Disable syntax highlighting for session" do
131
+ Pry.config.color = false
132
+ end
133
+
134
+ on :f, "Suppress loading of ~/.pryrc and ./.pryrc" do
135
+ Pry.config.should_load_rc = false
136
+ Pry.config.should_load_local_rc = false
137
+ end
138
+
139
+ on :s, "select-plugin=", "Only load specified plugin (and no others)." do |plugin_name|
140
+ Pry.config.should_load_plugins = false
141
+ Pry.plugins[plugin_name].activate!
142
+ end
143
+
144
+ on :d, "disable-plugin=", "Disable a specific plugin." do |plugin_name|
145
+ Pry.plugins[plugin_name].disable!
146
+ end
147
+
148
+ on "no-plugins", "Suppress loading of plugins." do
149
+ Pry.config.should_load_plugins = false
150
+ end
151
+
152
+ on "plugins", "List installed plugins." do
153
+ puts "Installed Plugins:"
154
+ puts "--"
155
+ Pry.locate_plugins.each do |plugin|
156
+ puts "#{plugin.name}".ljust(18) << plugin.spec.summary
157
+ end
158
+ exit
159
+ end
160
+
161
+ on "simple-prompt", "Enable simple prompt mode" do
162
+ Pry.config.prompt = Pry::SIMPLE_PROMPT
163
+ end
164
+
165
+ on "noprompt", "No prompt mode" do
166
+ Pry.config.prompt = Pry::NO_PROMPT
167
+ end
168
+
169
+ on :r, :require=, "`require` a Ruby script at startup" do |file|
170
+ Pry.config.requires << file
171
+ end
172
+
173
+ on :I=, "Add a path to the $LOAD_PATH", :as => Array, :delimiter => ":" do |load_path|
174
+ load_path.map! do |path|
175
+ /\A\.\// =~ path ? path : File.expand_path(path)
176
+ end
177
+
178
+ $LOAD_PATH.unshift(*load_path)
179
+ end
180
+
181
+ on "gem", "Shorthand for -I./lib -rgemname" do |load_path|
182
+ $LOAD_PATH.unshift("./lib")
183
+ Dir["./lib/*.rb"].each do |file|
184
+ Pry.config.requires << file
185
+ end
186
+ end
187
+
188
+ on :v, :version, "Display the Pry version" do
189
+ puts "Pry version #{Pry::VERSION} on Ruby #{RUBY_VERSION}"
190
+ exit
191
+ end
192
+
193
+ on(:c, :context=,
194
+ "Start the session in the specified context. Equivalent to `context.pry` in a session.",
195
+ :default => "Pry.toplevel_binding"
196
+ )
197
+ end.add_option_processor do |opts|
198
+
199
+ exit if opts.help?
200
+
201
+ # invoked via cli
202
+ Pry.cli = true
203
+
204
+ # create the actual context
205
+ if opts[:context]
206
+ Pry.initial_session_setup
207
+ context = Pry.binding_for(eval(opts[:context]))
208
+ else
209
+ context = Pry.toplevel_binding
210
+ end
211
+
212
+ if Pry::CLI.input_args.any? && Pry::CLI.input_args != ["pry"]
213
+ full_name = File.expand_path(Pry::CLI.input_args.first)
214
+ Pry.load_file_through_repl(full_name)
215
+ exit
216
+ end
217
+
218
+ # Start the session (running any code passed with -e, if there is any)
219
+ Pry.start(context, :input => StringIO.new(exec_string))
220
+ end
@@ -0,0 +1,341 @@
1
+ require 'pry/code/loc'
2
+ require 'pry/code/code_range'
3
+ require 'pry/code/code_file'
4
+
5
+ class Pry
6
+ class << self
7
+ # Convert the given object into an instance of `Pry::Code`, if it isn't
8
+ # already one.
9
+ #
10
+ # @param [Code, Method, UnboundMethod, Proc, Pry::Method, String, Array,
11
+ # IO] obj
12
+ def Code(obj)
13
+ case obj
14
+ when Code
15
+ obj
16
+ when ::Method, UnboundMethod, Proc, Pry::Method
17
+ Code.from_method(obj)
18
+ else
19
+ Code.new(obj)
20
+ end
21
+ end
22
+ end
23
+
24
+ # `Pry::Code` is a class that encapsulates lines of source code and their
25
+ # line numbers and formats them for terminal output. It can read from a file
26
+ # or method definition or be instantiated with a `String` or an `Array`.
27
+ #
28
+ # In general, the formatting methods in `Code` return a new `Code` object
29
+ # which will format the text as specified when `#to_s` is called. This allows
30
+ # arbitrary chaining of formatting methods without mutating the original
31
+ # object.
32
+ class Code
33
+ class << self
34
+ include MethodSource::CodeHelpers
35
+
36
+ # Instantiate a `Code` object containing code loaded from a file or
37
+ # Pry's line buffer.
38
+ #
39
+ # @param [String] filename The name of a file, or "(pry)".
40
+ # @param [Symbol] code_type The type of code the file contains.
41
+ # @return [Code]
42
+ def from_file(filename, code_type = nil)
43
+ code_file = CodeFile.new(filename, code_type)
44
+ new(code_file.code, 1, code_file.code_type)
45
+ end
46
+
47
+ # Instantiate a `Code` object containing code extracted from a
48
+ # `::Method`, `UnboundMethod`, `Proc`, or `Pry::Method` object.
49
+ #
50
+ # @param [::Method, UnboundMethod, Proc, Pry::Method] meth The method
51
+ # object.
52
+ # @param [Integer, nil] start_line The line number to start on, or nil to
53
+ # use the method's original line numbers.
54
+ # @return [Code]
55
+ def from_method(meth, start_line = nil)
56
+ meth = Pry::Method(meth)
57
+ start_line ||= meth.source_line || 1
58
+ new(meth.source, start_line, meth.source_type)
59
+ end
60
+
61
+ # Attempt to extract the source code for module (or class) `mod`.
62
+ #
63
+ # @param [Module, Class] mod The module (or class) of interest.
64
+ # @param [Integer] candidate_rank The module candidate (by rank)
65
+ # to use (see `Pry::WrappedModule::Candidate` for more information).
66
+ # @param [Integer, nil] start_line The line number to start on, or nil to
67
+ # use the method's original line numbers.
68
+ # @return [Code]
69
+ def from_module(mod, candidate_rank = 0, start_line=nil)
70
+ candidate = Pry::WrappedModule(mod).candidate(candidate_rank)
71
+ start_line ||= candidate.line
72
+ new(candidate.source, start_line, :ruby)
73
+ end
74
+ end
75
+
76
+ # @return [Symbol] The type of code stored in this wrapper.
77
+ attr_accessor :code_type
78
+
79
+ # Instantiate a `Code` object containing code from the given `Array`,
80
+ # `String`, or `IO`. The first line will be line 1 unless specified
81
+ # otherwise. If you need non-contiguous line numbers, you can create an
82
+ # empty `Code` object and then use `#push` to insert the lines.
83
+ #
84
+ # @param [Array<String>, String, IO] lines
85
+ # @param [Integer?] start_line
86
+ # @param [Symbol?] code_type
87
+ def initialize(lines = [], start_line = 1, code_type = :ruby)
88
+ if lines.is_a? String
89
+ lines = lines.lines
90
+ end
91
+ @lines = lines.each_with_index.map { |line, lineno|
92
+ LOC.new(line, lineno + start_line.to_i) }
93
+ @code_type = code_type
94
+ end
95
+
96
+ # Append the given line. +lineno+ is one more than the last existing
97
+ # line, unless specified otherwise.
98
+ #
99
+ # @param [String] line
100
+ # @param [Integer?] lineno
101
+ # @return [String] The inserted line.
102
+ def push(line, lineno = nil)
103
+ if lineno.nil?
104
+ lineno = @lines.last.lineno + 1
105
+ end
106
+ @lines.push(LOC.new(line, lineno))
107
+ line
108
+ end
109
+ alias << push
110
+
111
+ # Filter the lines using the given block.
112
+ #
113
+ # @yield [LOC]
114
+ # @return [Code]
115
+ def select(&block)
116
+ alter do
117
+ @lines = @lines.select(&block)
118
+ end
119
+ end
120
+
121
+ # Remove all lines that aren't in the given range, expressed either as a
122
+ # `Range` object or a first and last line number (inclusive). Negative
123
+ # indices count from the end of the array of lines.
124
+ #
125
+ # @param [Range, Integer] start_line
126
+ # @param [Integer?] end_line
127
+ # @return [Code]
128
+ def between(start_line, end_line = nil)
129
+ return self unless start_line
130
+
131
+ code_range = CodeRange.new(start_line, end_line)
132
+
133
+ alter do
134
+ @lines = @lines[code_range.indices_range(@lines)] || []
135
+ end
136
+ end
137
+
138
+ # Take `num_lines` from `start_line`, forward or backwards.
139
+ #
140
+ # @param [Integer] start_line
141
+ # @param [Integer] num_lines
142
+ # @return [Code]
143
+ def take_lines(start_line, num_lines)
144
+ start_idx =
145
+ if start_line >= 0
146
+ @lines.index { |loc| loc.lineno >= start_line } || @lines.length
147
+ else
148
+ [@lines.length + start_line, 0].max
149
+ end
150
+
151
+ alter do
152
+ @lines = @lines.slice(start_idx, num_lines)
153
+ end
154
+ end
155
+
156
+ # Remove all lines except for the +lines+ up to and excluding +lineno+.
157
+ #
158
+ # @param [Integer] lineno
159
+ # @param [Integer] lines
160
+ # @return [Code]
161
+ def before(lineno, lines = 1)
162
+ return self unless lineno
163
+
164
+ select do |loc|
165
+ loc.lineno >= lineno - lines && loc.lineno < lineno
166
+ end
167
+ end
168
+
169
+ # Remove all lines except for the +lines+ on either side of and including
170
+ # +lineno+.
171
+ #
172
+ # @param [Integer] lineno
173
+ # @param [Integer] lines
174
+ # @return [Code]
175
+ def around(lineno, lines = 1)
176
+ return self unless lineno
177
+
178
+ select do |loc|
179
+ loc.lineno >= lineno - lines && loc.lineno <= lineno + lines
180
+ end
181
+ end
182
+
183
+ # Remove all lines except for the +lines+ after and excluding +lineno+.
184
+ #
185
+ # @param [Integer] lineno
186
+ # @param [Integer] lines
187
+ # @return [Code]
188
+ def after(lineno, lines = 1)
189
+ return self unless lineno
190
+
191
+ select do |loc|
192
+ loc.lineno > lineno && loc.lineno <= lineno + lines
193
+ end
194
+ end
195
+
196
+ # Remove all lines that don't match the given `pattern`.
197
+ #
198
+ # @param [Regexp] pattern
199
+ # @return [Code]
200
+ def grep(pattern)
201
+ return self unless pattern
202
+ pattern = Regexp.new(pattern)
203
+
204
+ select do |loc|
205
+ loc.line =~ pattern
206
+ end
207
+ end
208
+
209
+ # Format output with line numbers next to it, unless `y_n` is falsy.
210
+ #
211
+ # @param [Boolean?] y_n
212
+ # @return [Code]
213
+ def with_line_numbers(y_n = true)
214
+ alter do
215
+ @with_line_numbers = y_n
216
+ end
217
+ end
218
+
219
+ # Format output with a marker next to the given +lineno+, unless +lineno+ is
220
+ # falsy.
221
+ #
222
+ # @param [Integer?] lineno
223
+ # @return [Code]
224
+ def with_marker(lineno = 1)
225
+ alter do
226
+ @with_marker = !!lineno
227
+ @marker_lineno = lineno
228
+ end
229
+ end
230
+
231
+ # Format output with the specified number of spaces in front of every line,
232
+ # unless `spaces` is falsy.
233
+ #
234
+ # @param [Integer?] spaces
235
+ # @return [Code]
236
+ def with_indentation(spaces = 0)
237
+ alter do
238
+ @with_indentation = !!spaces
239
+ @indentation_num = spaces
240
+ end
241
+ end
242
+
243
+ # @return [String]
244
+ def inspect
245
+ Object.instance_method(:to_s).bind(self).call
246
+ end
247
+
248
+ # @return [Integer] the number of digits in the last line.
249
+ def max_lineno_width
250
+ @lines.length > 0 ? @lines.last.lineno.to_s.length : 0
251
+ end
252
+
253
+ # @return [String] a formatted representation (based on the configuration of
254
+ # the object).
255
+ def to_s
256
+ print_to_output("")
257
+ end
258
+
259
+ # Writes a formatted representation (based on the configuration of the
260
+ # object) to the given output, which must respond to `#<<`.
261
+ def print_to_output(output, color=false)
262
+ @lines.each do |loc|
263
+ loc = loc.dup
264
+ loc.colorize(@code_type)
265
+ loc.add_line_number(max_lineno_width, color) if @with_line_numbers
266
+ loc.add_marker(@marker_lineno) if @with_marker
267
+ loc.indent(@indentation_num) if @with_indentation
268
+ output << loc.line
269
+ output << "\n"
270
+ end
271
+ output
272
+ end
273
+
274
+ # Get the comment that describes the expression on the given line number.
275
+ #
276
+ # @param [Integer] line_number (1-based)
277
+ # @return [String] the code.
278
+ def comment_describing(line_number)
279
+ self.class.comment_describing(raw, line_number)
280
+ end
281
+
282
+ # Get the multiline expression that starts on the given line number.
283
+ #
284
+ # @param [Integer] line_number (1-based)
285
+ # @return [String] the code.
286
+ def expression_at(line_number, consume = 0)
287
+ self.class.expression_at(raw, line_number, :consume => consume)
288
+ end
289
+
290
+ # Get the (approximate) Module.nesting at the give line number.
291
+ #
292
+ # @param [Integer] line_number line number starting from 1
293
+ # @param [Module] top_module the module in which this code exists
294
+ # @return [Array<Module>] a list of open modules.
295
+ def nesting_at(line_number, top_module = Object)
296
+ Pry::Indent.nesting_at(raw, line_number)
297
+ end
298
+
299
+ # Return an unformatted String of the code.
300
+ #
301
+ # @return [String]
302
+ def raw
303
+ @lines.map(&:line).join("\n") << "\n"
304
+ end
305
+
306
+ # Return the number of lines stored.
307
+ #
308
+ # @return [Integer]
309
+ def length
310
+ @lines ? @lines.length : 0
311
+ end
312
+
313
+ # Two `Code` objects are equal if they contain the same lines with the same
314
+ # numbers. Otherwise, call `to_s` and `chomp` and compare as Strings.
315
+ #
316
+ # @param [Code, Object] other
317
+ # @return [Boolean]
318
+ def ==(other)
319
+ if other.is_a?(Code)
320
+ other_lines = other.instance_variable_get(:@lines)
321
+ @lines.each_with_index.all? { |loc, i| loc == other_lines[i] }
322
+ else
323
+ to_s.chomp == other.to_s.chomp
324
+ end
325
+ end
326
+
327
+ # Forward any missing methods to the output of `#to_s`.
328
+ def method_missing(name, *args, &block)
329
+ to_s.send(name, *args, &block)
330
+ end
331
+ undef =~
332
+
333
+ protected
334
+
335
+ # An abstraction of the `dup.instance_eval` pattern used throughout this
336
+ # class.
337
+ def alter(&block)
338
+ dup.tap { |o| o.instance_eval(&block) }
339
+ end
340
+ end
341
+ end