Class: Pry::REPL

Inherits:
Object show all
Extended by:
Forwardable
Defined in:
lib/pry/repl.rb

Constant Summary collapse

UNEXPECTED_TOKENS =
%i[unexpected_token_ignore lambda_open].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Forwardable

def_private_delegators

Constructor Details

#initialize(pry, options = {}) ⇒ REPL

Create an instance of Pry::REPL wrapping the given Pry.

Parameters:

  • pry (Pry)

    The instance of Pry that this Pry::REPL will control.

  • options (Hash) (defaults to: {})

    Options for this Pry::REPL instance.

Options Hash (options):

  • :target (Object)

    The initial target of the session.



22
23
24
25
26
27
28
29
# File 'lib/pry/repl.rb', line 22

def initialize(pry, options = {})
  @pry    = pry
  @indent = Pry::Indent.new(pry)

  @readline_output = nil

  @pry.push_binding options[:target] if options[:target]
end

Instance Attribute Details

#pryPry

Returns The instance of Pry that the user is controlling.

Returns:

  • (Pry)

    The instance of Pry that the user is controlling.



9
10
11
# File 'lib/pry/repl.rb', line 9

def pry
  @pry
end

Class Method Details

.start(options) ⇒ Object

Instantiate a new Pry instance with the given options, then start a Pry::REPL instance wrapping it.

Parameters:

  • options (Hash)

    a customizable set of options

Options Hash (options):



14
15
16
# File 'lib/pry/repl.rb', line 14

def self.start(options)
  new(Pry.new(options)).start
end

Instance Method Details

#calculate_overhang(current_prompt, original_val, indented_val) ⇒ Integer (private)

Note:

This doesn't calculate overhang for Readline's emacs mode with an indicator because emacs is the default mode and it doesn't use indicators in 99% of cases.

Calculates correct overhang for current line. Supports vi Readline mode and its indicators such as "(ins)" or "(cmd)".

Returns:

  • (Integer)


307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/pry/repl.rb', line 307

def calculate_overhang(current_prompt, original_val, indented_val)
  overhang = original_val.length - indented_val.length

  if readline_available? && Readline.respond_to?(:vi_editing_mode?)
    begin
      # rb-readline doesn't support this method:
      # https://github.com/ConnorAtherton/rb-readline/issues/152
      if Readline.vi_editing_mode?
        overhang = output.width - current_prompt.size - indented_val.size
      end
    rescue NotImplementedError
      # VI editing mode is unsupported on JRuby.
      # https://github.com/pry/pry/issues/1840
      nil
    end
  end
  [0, overhang].max
end

#complete_expression?(multiline_input) ⇒ Boolean (private)

Returns:

  • (Boolean)


287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/pry/repl.rb', line 287

def complete_expression?(multiline_input)
  if prism_available?
    lex = Prism.lex(multiline_input)

    errors = lex.errors
    return true if errors.empty?

    errors.any? { |error| UNEXPECTED_TOKENS.include?(error.type) }
  else
    Pry::Code.complete_expression?(multiline_input)
  end
end

#coolline_available?Boolean (private)

Returns:

  • (Boolean)


246
247
248
# File 'lib/pry/repl.rb', line 246

def coolline_available?
  defined?(Coolline) && input.is_a?(Coolline)
end

#epilogue (private)

This method returns an undefined value.

Clean up after the repl session.



84
85
86
# File 'lib/pry/repl.rb', line 84

def epilogue
  pry.exec_hook :after_session, pry.output, pry.current_binding, pry
end

#handle_read_errorsObject, :no_more_input (private)

Manage switching of input objects on encountering EOFErrors.

Returns:

  • (Object)

    Whatever the given block returns.

  • (:no_more_input)

    Indicates that no more input can be read.



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
# File 'lib/pry/repl.rb', line 127

def handle_read_errors
  should_retry = true
  exception_count = 0

  begin
    yield
  rescue EOFError
    pry.config.input = Pry.config.input
    unless should_retry
      output.puts "Error: Pry ran out of things to read from! " \
        "Attempting to break out of REPL."
      return :no_more_input
    end
    should_retry = false
    retry

  # Handle <Ctrl+C> like Bash: empty the current input buffer, but don't
  # quit.
  rescue Interrupt
    return :control_c

  # 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
    retry if exception_count < 5
    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

#input_multiline?Boolean (private)

Returns:

  • (Boolean)


234
235
236
# File 'lib/pry/repl.rb', line 234

def input_multiline?
  !!pry.config.multiline && reline_available?
end

#input_readline(*args) ⇒ Object (private)



228
229
230
231
232
# File 'lib/pry/repl.rb', line 228

def input_readline(*args)
  Pry::InputLock.for(:all).interruptible_region do
    input.readline(*args)
  end
end

#input_readmultiline(*args) ⇒ Object (private)



219
220
221
222
223
224
225
226
# File 'lib/pry/repl.rb', line 219

def input_readmultiline(*args)
  Pry::InputLock.for(:all).interruptible_region do
    input.readmultiline(*args) do |multiline_input|
      Pry.commands.find_command(multiline_input) ||
        (complete_expression?(multiline_input) && !Reline::IOGate.in_pasting?)
    end
  end
end

#piping?Boolean (private)

If $stdout is not a tty, it's probably a pipe.

Examples:

# `piping?` returns `false`
% pry
[1] pry(main)

# `piping?` returns `true`
% pry | tee log

Returns:

  • (Boolean)


272
273
274
275
276
# File 'lib/pry/repl.rb', line 272

def piping?
  return false unless $stdout.respond_to?(:tty?)

  !$stdout.tty? && $stdin.tty? && !Helpers::Platform.windows?
end

#prism_available?Boolean (private)

Returns:

  • (Boolean)


250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/pry/repl.rb', line 250

def prism_available?
  @prism_available ||= begin
    # rubocop:disable Lint/SuppressedException
    begin
      require 'prism'
    rescue LoadError
    end
    # rubocop:enable Lint/SuppressedException

    defined?(Prism::VERSION) &&
      Gem::Version.new(Prism::VERSION) >= Gem::Version.new('0.25.0')
  end
end

#prologue (private)

This method returns an undefined value.

Set up the repl session.



47
48
49
50
51
52
53
54
# File 'lib/pry/repl.rb', line 47

def prologue
  pry.exec_hook :before_session, pry.output, pry.current_binding, pry

  return unless pry.config.correct_indent

  # Clear the line before starting Pry. This fixes issue #566.
  output.print(Helpers::Platform.windows_ansi? ? "\e[0F" : "\e[0G")
end

#readString, ... (private)

Read a line of input from the user.

Returns:

  • (String)

    The line entered by the user.

  • (nil)

    On <Ctrl-D>.

  • (:control_c)

    On <Ctrl+C>.

  • (:no_more_input)

    On EOF.



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
# File 'lib/pry/repl.rb', line 93

def read
  @indent.reset if pry.eval_string.empty?
  current_prompt = pry.select_prompt
  indentation = pry.config.auto_indent ? @indent.current_prefix : ''

  val = read_line("#{current_prompt}#{indentation}")

  # Return nil for EOF, :no_more_input for error, or :control_c for <Ctrl-C>
  return val unless val.is_a?(String)

  if pry.config.auto_indent && !input_multiline?
    original_val = "#{indentation}#{val}"
    indented_val = @indent.indent(val)

    if output.tty? &&
       pry.config.correct_indent &&
       Pry::Helpers::BaseHelpers.use_ansi_codes?
      output.print @indent.correct_indentation(
        current_prompt,
        indented_val,
        calculate_overhang(current_prompt, original_val, indented_val)
      )
      output.flush
    end
  else
    indented_val = val
  end

  indented_val
end

#read_line(current_prompt) ⇒ String? (private)

Returns the next line of input to be sent to the Pry instance.

Parameters:

  • current_prompt (String)

    The prompt to use for input.

Returns:

  • (String?)

    The next line of input, or nil on .



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
# File 'lib/pry/repl.rb', line 169

def read_line(current_prompt)
  handle_read_errors do
    if coolline_available?
      input.completion_proc = proc do |cool|
        completions = @pry.complete cool.completed_word
        completions.compact
      end
    elsif input.respond_to? :completion_proc=
      input.completion_proc = proc do |inp|
        @pry.complete inp
      end
    end

    if reline_available?
      Reline.output_modifier_proc = lambda do |text, _|
        if pry.color
          SyntaxHighlighter.highlight(text)
        else
          text
        end
      end

      if pry.config.auto_indent
        Reline.auto_indent_proc = lambda do |lines, line_index, _byte_ptr, _newline|
          if line_index == 0
            0
          else
            pry_indentation = Pry::Indent.new
            pry_indentation.indent(lines.join("\n"))
            pry_indentation.last_indent_level.length
          end
        end
      end
    end

    if input_multiline?
      input_readmultiline(current_prompt, false)
    elsif readline_available?
      set_readline_output
      input_readline(current_prompt, false) # false since we'll add it manually
    elsif coolline_available?
      input_readline(current_prompt)
    elsif input.method(:readline).arity == 1
      input_readline(current_prompt)
    else
      input_readline
    end
  end
end

#readline_available?Boolean (private)

Returns:

  • (Boolean)


242
243
244
# File 'lib/pry/repl.rb', line 242

def readline_available?
  defined?(Readline) && input == Readline
end

#reline_available?Boolean (private)

Returns:

  • (Boolean)


238
239
240
# File 'lib/pry/repl.rb', line 238

def reline_available?
  defined?(Reline) && input == Reline
end

#replObject? (private)

The actual read-eval-print loop.

The Pry::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.

Returns:

  • (Object?)

    If the session throws :breakout, return the value thrown with it.

Raises:

  • (Exception)

    If the session throws :raise_up, raise the exception thrown with it.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/pry/repl.rb', line 66

def repl
  loop do
    case val = read
    when :control_c
      output.puts ""
      pry.reset_eval_string
    when :no_more_input
      output.puts "" if output.tty?
      break
    else
      output.puts "" if val.nil? && output.tty?
      return pry.exit_value unless pry.eval(val)
    end
  end
end

#set_readline_output (private)

This method returns an undefined value.



279
280
281
282
283
# File 'lib/pry/repl.rb', line 279

def set_readline_output
  return if @readline_output

  @readline_output = (Readline.output = Pry.config.output) if piping?
end

#startObject?

Start the read-eval-print loop.

Returns:

  • (Object?)

    If the session throws :breakout, return the value thrown with it.

Raises:

  • (Exception)

    If the session throws :raise_up, raise the exception thrown with it.



36
37
38
39
40
41
# File 'lib/pry/repl.rb', line 36

def start
  prologue
  Pry::InputLock.for(:all).with_ownership { repl }
ensure
  epilogue
end