1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
require 'forwardable'
class Pry
class REPL
extend Forwardable
def_delegators :@pry, :input, :output
# @return [Pry] The instance of {Pry} that the user is controlling.
attr_accessor :pry
# Instantiate a new {Pry} instance with the given options, then start a
# {REPL} instance wrapping it.
# @option options See {Pry#initialize}
def self.start(options)
new(Pry.new(options)).start
end
# Create an instance of {REPL} wrapping the given {Pry}.
# @param [Pry] pry The instance of {Pry} that this {REPL} will control.
# @param [Hash] options Options for this {REPL} instance.
# @option options [Object] :target The initial target of the session.
def initialize(pry, options = {})
@pry = pry
@indent = Pry::Indent.new
if options[:target]
@pry.push_binding options[:target]
end
end
# Start the read-eval-print loop.
# @return [Object?] If the session throws `:breakout`, return the value
# thrown with it.
# @raise [Exception] If the session throws `:raise_up`, raise the exception
# thrown with it.
def start
prologue
repl
ensure
epilogue
end
private
# Set up the repl session.
# @return [void]
def prologue
pry.exec_hook :before_session, pry.output, pry.current_binding, pry
# Clear the line before starting Pry. This fixes issue #566.
if Pry.config.correct_indent
Kernel.print Pry::Helpers::BaseHelpers.windows_ansi? ? "\e[0F" : "\e[0G"
end
end
# The actual read-eval-print loop.
#
# The {REPL} instance is responsible for reading and looping, whereas the
# {Pry} instance is responsible for evaluating user input and printing
# return values and command output.
#
# @return [Object?] If the session throws `:breakout`, return the value
# thrown with it.
# @raise [Exception] If the session throws `:raise_up`, raise the exception
# thrown with it.
def repl
loop do
case line = read(pry.select_prompt)
when :control_c
advance_cursor
pry.reset_eval_string
when :no_more_input
advance_cursor
break
else
advance_cursor if line.nil?
return pry.exit_value unless pry.eval(line)
end
end
end
# Print an empty line if the output is a TTY.
def advance_cursor
output.puts "" if output.tty?
end
# Clean up after the repl session.
# @return [void]
def epilogue
pry.exec_hook :after_session, pry.output, pry.current_binding, pry
end
# Return the next line of input to be sent to the {Pry} instance.
# @param [String] prompt The prompt to use for input.
# @return [String] The line entered by the user.
# @return [nil] On `<Ctrl-D>`.
# @return [:control_c] On `<Ctrl+C>`.
# @return [:no_more_input] On EOF.
def read(prompt)
set_completion_proc
if input == Readline
input.readline(prompt, false) # false since we'll add it manually
elsif input.method(:readline).arity == 1
input.readline(prompt)
else
input.readline
end
end
private
# Set the default completion proc, if applicable.
def set_completion_proc
if input.respond_to? :completion_proc=
input.completion_proc = proc do |input|
@pry.complete input
end
end
end
# Manage switching of input objects on encountering `EOFError`s.
# @return [Object] Whatever the given block returns.
# @return [:no_more_input] Indicates that no more input can be read.
def read_with_handle_eof(prompt)
should_retry = true
begin
read_without_handle_eof(prompt)
rescue EOFError
pry.input = Pry.config.input
if should_retry
should_retry = false
retry
else
output.puts "Error: Pry ran out of things to read from! " \
"Attempting to break out of REPL."
return :no_more_input
end
end
end
alias_method :read_without_handle_eof, :read
alias_method :read, :read_with_handle_eof
# Handle `Ctrl-C` like Bash: empty the current input buffer, but don't
# quit. This is only for MRI 1.9; other versions of Ruby don't let you
# send Interrupt from within Readline.
# @return [Object] Whatever the given block returns.
# @return [:control_c] Indicates that the user hit `Ctrl-C`.
def read_with_handle_interrupt(prompt)
read_without_handle_interrupt(prompt)
rescue Interrupt
return :control_c
end
alias_method :read_without_handle_interrupt, :read
alias_method :read, :read_with_handle_interrupt
# Deal with any random errors that happen while trying to get user input.
# @return [Object] Whatever the given block returns.
# @return [:no_more_input] Indicates that no more input can be read.
def read_with_handle_read_errors(prompt)
exception_count = 0
begin
read_without_handle_read_errors(prompt)
# If we get a random error when trying to read a line we don't want to
# automatically retry, as the user will see a lot of error messages
# scroll past and be unable to do anything about it.
rescue RescuableException => e
puts "Error: #{e.message}"
output.puts e.backtrace
exception_count += 1
if exception_count < 5
retry
end
puts "FATAL: Pry failed to get user input using `#{input}`."
puts "To fix this you may be able to pass input and output file " \
"descriptors to pry directly. e.g."
puts " Pry.config.input = STDIN"
puts " Pry.config.output = STDOUT"
puts " binding.pry"
return :no_more_input
end
end
alias_method :read_without_handle_read_errors, :read
alias_method :read, :read_with_handle_read_errors
def read_with_fix_indentation(prompt)
@indent.reset if pry.eval_string.empty?
indentation = Pry.config.auto_indent ? @indent.current_prefix : ''
line = read_without_fix_indentation("#{prompt}#{indentation}")
if line.is_a? String
fix_indentation(line, indentation, prompt)
else
# nil for EOF, :no_more_input for error, or :control_c for <Ctrl-C>
line
end
end
alias_method :read_without_fix_indentation, :read
alias_method :read, :read_with_fix_indentation
def fix_indentation(line, indentation, prompt)
if Pry.config.auto_indent
original_line = "#{indentation}#{line}"
indented_line = @indent.indent(line)
if output.tty? && @indent.should_correct_indentation?
output.print @indent.correct_indentation(
prompt, indented_line,
original_line.length - indented_line.length
)
output.flush
end
indented_line
else
line
end
end
end
end
|