mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
Merge branch 'wip.refactor'
Conflicts: lib/pry/completion.rb lib/pry/pry_instance.rb spec/commands/play_spec.rb spec/pry_defaults_spec.rb
This commit is contained in:
commit
2c60f93b2d
37 changed files with 1317 additions and 1367 deletions
|
@ -1 +1,3 @@
|
|||
--markup markdown --private
|
||||
--markup markdown
|
||||
--private
|
||||
--hide-void-return
|
||||
|
|
10
lib/pry.rb
10
lib/pry.rb
|
@ -10,7 +10,7 @@ class Pry
|
|||
# The default hooks - display messages when beginning and ending Pry sessions.
|
||||
DEFAULT_HOOKS = Pry::Hooks.new.add_hook(:before_session, :default) do |out, target, _pry_|
|
||||
next if _pry_.quiet?
|
||||
_pry_.run_command("whereami --quiet", "", target)
|
||||
_pry_.run_command("whereami --quiet")
|
||||
end
|
||||
|
||||
# The default print
|
||||
|
@ -120,10 +120,9 @@ class Pry
|
|||
_pry_.binding_stack.clear
|
||||
throw(:breakout)
|
||||
else
|
||||
# Store the entire binding stack before popping. Useful for `cd -`.
|
||||
if _pry_.command_state['cd'].nil?
|
||||
_pry_.command_state['cd'] = OpenStruct.new
|
||||
end
|
||||
# Otherwise, saves current binding stack as old stack and pops last
|
||||
# binding out of binding stack (the old stack still has that binding).
|
||||
_pry_.command_state["cd"] ||= OpenStruct.new # FIXME
|
||||
_pry_.command_state['cd'].old_stack = _pry_.binding_stack.dup
|
||||
_pry_.binding_stack.pop
|
||||
end
|
||||
|
@ -246,6 +245,7 @@ rescue LoadError
|
|||
end
|
||||
|
||||
require 'pry/version'
|
||||
require 'pry/repl'
|
||||
require 'pry/rbx_method'
|
||||
require 'pry/rbx_path'
|
||||
require 'pry/code'
|
||||
|
|
|
@ -13,7 +13,6 @@ class Pry
|
|||
BANNER
|
||||
|
||||
def process
|
||||
Pry.save_history if Pry.config.history.should_save
|
||||
Kernel.exit target.eval(arg_string).to_i
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,8 @@ class Pry
|
|||
end
|
||||
|
||||
def process
|
||||
@history = Pry::Code(Pry.history.to_a)
|
||||
# The last value in history will be the 'hist' command itself
|
||||
@history = Pry::Code(Pry.history.to_a[0..-2])
|
||||
|
||||
if opts.present?(:show)
|
||||
@history = @history.between(opts[:show])
|
||||
|
@ -113,10 +114,9 @@ class Pry
|
|||
# further.
|
||||
check_for_juxtaposed_replay(replay_sequence)
|
||||
|
||||
_pry_.input_stack.push _pry_.input
|
||||
_pry_.input = StringIO.new(replay_sequence)
|
||||
# eval_string << "#{@history.raw}\n"
|
||||
# run "show-input" unless _pry_.complete_expression?(eval_string)
|
||||
replay_sequence.lines.each do |line|
|
||||
_pry_.eval line, :generated => true
|
||||
end
|
||||
end
|
||||
|
||||
# Checks +replay_sequence+ for the presence of neighboring replay calls.
|
||||
|
|
|
@ -16,8 +16,6 @@ class Pry
|
|||
else
|
||||
_pry_.push_prompt Pry::SHELL_PROMPT
|
||||
_pry_.custom_completions = Pry::FILE_COMPLETIONS
|
||||
Readline.completion_proc = Pry::InputCompleter.build_completion_proc target,
|
||||
_pry_.instance_eval(&Pry::FILE_COMPLETIONS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,37 +3,38 @@
|
|||
class Pry
|
||||
|
||||
module BondCompleter
|
||||
|
||||
def self.build_completion_proc(target, pry=nil, commands=[""])
|
||||
if !@started
|
||||
@started = true
|
||||
start
|
||||
end
|
||||
|
||||
Pry.current[:pry] = pry
|
||||
proc{ |*a| Bond.agent.call(*a) }
|
||||
def self.call(input, options)
|
||||
Pry.current[:pry] = options[:pry]
|
||||
Bond.agent.call(input)
|
||||
end
|
||||
|
||||
def self.start
|
||||
Bond.start(:eval_binding => lambda{ Pry.current[:pry].current_context })
|
||||
Bond.start(:eval_binding => lambda{ Pry.current[:pry] && Pry.current[:pry].current_context })
|
||||
Bond.complete(:on => /\A/) do |input|
|
||||
Pry.commands.complete(input.line,
|
||||
:pry_instance => Pry.current[:pry],
|
||||
:target => Pry.current[:pry].current_context,
|
||||
:command_set => Pry.current[:pry].commands)
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Implements tab completion for Readline in Pry
|
||||
module InputCompleter
|
||||
|
||||
if Readline.respond_to?("basic_word_break_characters=")
|
||||
Readline.basic_word_break_characters = " \t\n\"\\'`><=;|&{("
|
||||
def self.call(input, options)
|
||||
build_completion_proc(options[:target], options[:pry], options[:custom_completions]).call input
|
||||
end
|
||||
|
||||
Readline.completion_append_character = nil
|
||||
def self.start
|
||||
if Readline.respond_to?("basic_word_break_characters=")
|
||||
Readline.basic_word_break_characters = " \t\n\"\\'`><=;|&{("
|
||||
end
|
||||
|
||||
Readline.completion_append_character = nil
|
||||
self
|
||||
end
|
||||
|
||||
ReservedWords = [
|
||||
"BEGIN", "END",
|
||||
|
@ -63,199 +64,200 @@ class Pry
|
|||
# Return a new completion proc for use by Readline.
|
||||
# @param [Binding] target The current binding context.
|
||||
# @param [Array<String>] commands The array of Pry commands.
|
||||
def self.build_completion_proc(target, pry=nil, commands=[""])
|
||||
def self.call(input, options)
|
||||
|
||||
proc do |input|
|
||||
custom_completions = options[:custom_completions] || []
|
||||
|
||||
# if there are multiple contexts e.g. cd 1/2/3
|
||||
# get new target for 1/2 and find candidates for 3
|
||||
path, input = build_path(input)
|
||||
# if there are multiple contexts e.g. cd 1/2/3
|
||||
# get new target for 1/2 and find candidates for 3
|
||||
path, input = build_path(input)
|
||||
|
||||
unless path.call.empty?
|
||||
target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, pry)
|
||||
target = target.last
|
||||
end
|
||||
if path.call.empty?
|
||||
target = options[:target]
|
||||
else
|
||||
target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, options[:pry])
|
||||
target = target.last
|
||||
end
|
||||
|
||||
begin
|
||||
bind = target
|
||||
begin
|
||||
bind = target
|
||||
|
||||
case input
|
||||
case input
|
||||
|
||||
|
||||
# Complete stdlib symbols
|
||||
# Complete stdlib symbols
|
||||
|
||||
when /^(\/[^\/]*\/)\.([^.]*)$/
|
||||
# Regexp
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
when /^(\/[^\/]*\/)\.([^.]*)$/
|
||||
# Regexp
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
candidates = Regexp.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
candidates = Regexp.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^([^\]]*\])\.([^.]*)$/
|
||||
# Array
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
when /^([^\]]*\])\.([^.]*)$/
|
||||
# Array
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
candidates = Array.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
candidates = Array.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^([^\}]*\})\.([^.]*)$/
|
||||
# Proc or Hash
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
when /^([^\}]*\})\.([^.]*)$/
|
||||
# Proc or Hash
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
candidates = Proc.instance_methods.collect(&:to_s)
|
||||
candidates |= Hash.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
candidates = Proc.instance_methods.collect(&:to_s)
|
||||
candidates |= Hash.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^(:[^:.]*)$/
|
||||
# Symbol
|
||||
if Symbol.respond_to?(:all_symbols)
|
||||
sym = Regexp.quote($1)
|
||||
candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
|
||||
|
||||
candidates.grep(/^#{sym}/)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
when /^::([A-Z][^:\.\(]*)$/
|
||||
# Absolute Constant or class methods
|
||||
receiver = $1
|
||||
candidates = Object.constants.collect(&:to_s)
|
||||
candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
|
||||
|
||||
|
||||
# Complete target symbols
|
||||
|
||||
when /^([A-Z][A-Za-z0-9]*)$/
|
||||
# Constant
|
||||
message = $1
|
||||
|
||||
begin
|
||||
context = target.eval("self")
|
||||
context = context.class unless context.respond_to? :constants
|
||||
candidates = context.constants.collect(&:to_s)
|
||||
rescue
|
||||
candidates = []
|
||||
end
|
||||
candidates = candidates.grep(/^#{message}/).collect(&path)
|
||||
|
||||
when /^([A-Z].*)::([^:.]*)$/
|
||||
# Constant or class methods
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
begin
|
||||
candidates = eval("#{receiver}.constants.collect(&:to_s)", bind)
|
||||
candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
|
||||
|
||||
when /^(:[^:.]+)\.([^.]*)$/
|
||||
# Symbol
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
candidates = Symbol.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/
|
||||
# Numeric
|
||||
receiver = $1
|
||||
message = Regexp.quote($5)
|
||||
|
||||
begin
|
||||
candidates = eval(receiver, bind).methods.collect(&:to_s)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/#
|
||||
# Numeric(0xFFFF)
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
begin
|
||||
candidates = eval(receiver, bind).methods.collect(&:to_s)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^(\$[^.]*)$/
|
||||
# Global variables
|
||||
regmessage = Regexp.new(Regexp.quote($1))
|
||||
candidates = global_variables.collect(&:to_s).grep(regmessage)
|
||||
|
||||
when /^([^."].*)\.([^.]*)$/
|
||||
# Variable
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
gv = eval("global_variables", bind).collect(&:to_s)
|
||||
lv = eval("local_variables", bind).collect(&:to_s)
|
||||
cv = eval("self.class.constants", bind).collect(&:to_s)
|
||||
|
||||
if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
|
||||
# foo.func and foo is local var. OR
|
||||
# Foo::Bar.func
|
||||
begin
|
||||
candidates = eval("#{receiver}.methods", bind).collect(&:to_s)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
else
|
||||
# func1.func2
|
||||
candidates = []
|
||||
ObjectSpace.each_object(Module){|m|
|
||||
begin
|
||||
name = m.name.to_s
|
||||
rescue RescuableException
|
||||
name = ""
|
||||
end
|
||||
next if name != "IRB::Context" and
|
||||
/^(IRB|SLex|RubyLex|RubyToken)/ =~ name
|
||||
|
||||
# jruby doesn't always provide #instance_methods() on each
|
||||
# object.
|
||||
if m.respond_to?(:instance_methods)
|
||||
candidates.concat m.instance_methods(false).collect(&:to_s)
|
||||
end
|
||||
}
|
||||
candidates.sort!
|
||||
candidates.uniq!
|
||||
end
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^\.([^.]*)$/
|
||||
# Unknown(maybe String)
|
||||
receiver = ""
|
||||
message = Regexp.quote($1)
|
||||
|
||||
candidates = String.instance_methods(true).collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
when /^(:[^:.]*)$/
|
||||
# Symbol
|
||||
if Symbol.respond_to?(:all_symbols)
|
||||
sym = Regexp.quote($1)
|
||||
candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
|
||||
|
||||
candidates.grep(/^#{sym}/)
|
||||
else
|
||||
|
||||
candidates = eval(
|
||||
"methods | private_methods | local_variables | " \
|
||||
"self.class.constants | instance_variables",
|
||||
bind
|
||||
).collect(&:to_s)
|
||||
|
||||
if eval("respond_to?(:class_variables)", bind)
|
||||
candidates += eval("class_variables", bind).collect(&:to_s)
|
||||
end
|
||||
candidates = (candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/)
|
||||
candidates.collect(&path)
|
||||
[]
|
||||
end
|
||||
rescue RescuableException
|
||||
[]
|
||||
|
||||
when /^::([A-Z][^:\.\(]*)$/
|
||||
# Absolute Constant or class methods
|
||||
receiver = $1
|
||||
candidates = Object.constants.collect(&:to_s)
|
||||
candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
|
||||
|
||||
|
||||
# Complete target symbols
|
||||
|
||||
when /^([A-Z][A-Za-z0-9]*)$/
|
||||
# Constant
|
||||
message = $1
|
||||
|
||||
begin
|
||||
context = target.eval("self")
|
||||
context = context.class unless context.respond_to? :constants
|
||||
candidates = context.constants.collect(&:to_s)
|
||||
rescue
|
||||
candidates = []
|
||||
end
|
||||
candidates = candidates.grep(/^#{message}/).collect(&path)
|
||||
|
||||
when /^([A-Z].*)::([^:.]*)$/
|
||||
# Constant or class methods
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
begin
|
||||
candidates = eval("#{receiver}.constants.collect(&:to_s)", bind)
|
||||
candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
|
||||
|
||||
when /^(:[^:.]+)\.([^.]*)$/
|
||||
# Symbol
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
candidates = Symbol.instance_methods.collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/
|
||||
# Numeric
|
||||
receiver = $1
|
||||
message = Regexp.quote($5)
|
||||
|
||||
begin
|
||||
candidates = eval(receiver, bind).methods.collect(&:to_s)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/#
|
||||
# Numeric(0xFFFF)
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
begin
|
||||
candidates = eval(receiver, bind).methods.collect(&:to_s)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^(\$[^.]*)$/
|
||||
# Global variables
|
||||
regmessage = Regexp.new(Regexp.quote($1))
|
||||
candidates = global_variables.collect(&:to_s).grep(regmessage)
|
||||
|
||||
when /^([^."].*)\.([^.]*)$/
|
||||
# Variable
|
||||
receiver = $1
|
||||
message = Regexp.quote($2)
|
||||
|
||||
gv = eval("global_variables", bind).collect(&:to_s)
|
||||
lv = eval("local_variables", bind).collect(&:to_s)
|
||||
cv = eval("self.class.constants", bind).collect(&:to_s)
|
||||
|
||||
if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
|
||||
# foo.func and foo is local var. OR
|
||||
# Foo::Bar.func
|
||||
begin
|
||||
candidates = eval("#{receiver}.methods", bind).collect(&:to_s)
|
||||
rescue RescuableException
|
||||
candidates = []
|
||||
end
|
||||
else
|
||||
# func1.func2
|
||||
candidates = []
|
||||
ObjectSpace.each_object(Module){|m|
|
||||
begin
|
||||
name = m.name.to_s
|
||||
rescue RescuableException
|
||||
name = ""
|
||||
end
|
||||
next if name != "IRB::Context" and
|
||||
/^(IRB|SLex|RubyLex|RubyToken)/ =~ name
|
||||
|
||||
# jruby doesn't always provide #instance_methods() on each
|
||||
# object.
|
||||
if m.respond_to?(:instance_methods)
|
||||
candidates.concat m.instance_methods(false).collect(&:to_s)
|
||||
end
|
||||
}
|
||||
candidates.sort!
|
||||
candidates.uniq!
|
||||
end
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
when /^\.([^.]*)$/
|
||||
# Unknown(maybe String)
|
||||
receiver = ""
|
||||
message = Regexp.quote($1)
|
||||
|
||||
candidates = String.instance_methods(true).collect(&:to_s)
|
||||
select_message(path, receiver, message, candidates)
|
||||
|
||||
else
|
||||
|
||||
candidates = eval(
|
||||
"methods | private_methods | local_variables | " \
|
||||
"self.class.constants | instance_variables",
|
||||
bind
|
||||
).collect(&:to_s)
|
||||
|
||||
if eval("respond_to?(:class_variables)", bind)
|
||||
candidates += eval("class_variables", bind).collect(&:to_s)
|
||||
end
|
||||
candidates = (candidates|ReservedWords|custom_completions).grep(/^#{Regexp.quote(input)}/)
|
||||
candidates.collect(&path)
|
||||
end
|
||||
rescue RescuableException
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -94,13 +94,6 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
# Get/Set the stack of input objects that a Pry instance switches
|
||||
# to when its current input object encounters EOF.
|
||||
# @return [Array] The array of input objects.
|
||||
# @example
|
||||
# Pry.config.input_stack = [StringIO.new("puts 'hello world'\nexit")]
|
||||
attr_accessor :input_stack
|
||||
|
||||
# Get the array of Procs (or single Proc) to be used for the prompts by default by
|
||||
# all Pry instances.
|
||||
# Three parameters are passed into the prompt procs, (1) the
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
class Pry
|
||||
# The History class is responsible for maintaining the user's input history, both
|
||||
# internally and within Readline.
|
||||
# The History class is responsible for maintaining the user's input history,
|
||||
# both internally and within Readline.
|
||||
class History
|
||||
attr_accessor :loader, :saver, :pusher, :clearer
|
||||
|
||||
# @return [Fixnum] Number of lines in history when Pry first loaded.
|
||||
attr_reader :original_lines
|
||||
|
||||
def initialize
|
||||
def initialize(options={})
|
||||
@history = []
|
||||
@saved_lines = 0
|
||||
@original_lines = 0
|
||||
@file_path = options[:file_path]
|
||||
restore_default_behavior
|
||||
end
|
||||
|
||||
# Assign the default methods for loading, saving, pushing, and clearing.
|
||||
def restore_default_behavior
|
||||
@loader = method(:read_from_file)
|
||||
@saver = method(:write_to_file)
|
||||
@saver = method(:save_to_file)
|
||||
@pusher = method(:push_to_readline)
|
||||
@clearer = method(:clear_readline)
|
||||
end
|
||||
|
@ -29,16 +29,6 @@ class Pry
|
|||
@pusher.call(line.chomp)
|
||||
@history << line.chomp
|
||||
end
|
||||
@saved_lines = @original_lines = @history.length
|
||||
end
|
||||
|
||||
# Write this session's history using `History.saver`.
|
||||
# @return [Integer] The number of lines saved
|
||||
def save
|
||||
history_to_save = @history[@saved_lines..-1]
|
||||
@saver.call(history_to_save)
|
||||
@saved_lines = @history.length
|
||||
history_to_save.length
|
||||
end
|
||||
|
||||
# Add a line to the input history, ignoring blank and duplicate lines.
|
||||
|
@ -48,18 +38,17 @@ class Pry
|
|||
unless line.empty? || (@history.last && line == @history.last)
|
||||
@pusher.call(line)
|
||||
@history << line
|
||||
@saver.call(line) if Pry.config.history.should_save
|
||||
end
|
||||
line
|
||||
end
|
||||
alias << push
|
||||
|
||||
# Clear all history. Anything the user entered before this point won't be
|
||||
# saved, but anything they put in afterwards will still be appended to the
|
||||
# history file on exit.
|
||||
# Clear this session's history. This won't affect the contents of the
|
||||
# history file.
|
||||
def clear
|
||||
@clearer.call
|
||||
@history = []
|
||||
@saved_lines = 0
|
||||
end
|
||||
|
||||
# @return [Fixnum] The number of lines in history.
|
||||
|
@ -67,6 +56,7 @@ class Pry
|
|||
@history.count
|
||||
end
|
||||
|
||||
# @return [Fixnum] The number of lines in history from just this session.
|
||||
def session_line_count
|
||||
@history.count - @original_lines
|
||||
end
|
||||
|
@ -79,33 +69,16 @@ class Pry
|
|||
end
|
||||
|
||||
private
|
||||
|
||||
# The default loader. Yields lines from `Pry.history.config.file`.
|
||||
def read_from_file
|
||||
begin
|
||||
history_file = File.expand_path(Pry.config.history.file)
|
||||
if File.exists?(history_file)
|
||||
File.foreach(history_file) { |line| yield(line) }
|
||||
end
|
||||
rescue => error
|
||||
unless error.message.empty?
|
||||
warn "History file not loaded, received an error: #{error.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
filename = File.expand_path(Pry.config.history.file)
|
||||
|
||||
# The default saver. Appends the given lines to `Pry.history.config.file`.
|
||||
# @param [Array<String>] lines
|
||||
def write_to_file(lines)
|
||||
history_file = File.expand_path(Pry.config.history.file)
|
||||
|
||||
begin
|
||||
File.open(history_file, 'a') do |f|
|
||||
lines.each { |ln| f.puts ln }
|
||||
end
|
||||
rescue Errno::EACCES
|
||||
# We should probably create an option Pry.show_warnings?!?!?!
|
||||
warn 'Unable to write to your history file, history not saved'
|
||||
if File.exists?(filename)
|
||||
File.foreach(filename) { |line| yield(line) }
|
||||
end
|
||||
rescue => error
|
||||
warn "History file not loaded: #{error.message}"
|
||||
end
|
||||
|
||||
# The default pusher. Appends the given line to Readline::HISTORY.
|
||||
|
@ -118,5 +91,26 @@ class Pry
|
|||
def clear_readline
|
||||
Readline::HISTORY.shift until Readline::HISTORY.empty?
|
||||
end
|
||||
|
||||
# The default saver. Appends the given line to `Pry.history.config.file`.
|
||||
def save_to_file(line)
|
||||
history_file.puts line if history_file
|
||||
end
|
||||
|
||||
# The history file, opened for appending.
|
||||
def history_file
|
||||
if defined?(@history_file)
|
||||
@history_file
|
||||
else
|
||||
@history_file = File.open(file_path, 'a').tap { |f| f.sync = true }
|
||||
end
|
||||
rescue Errno::EACCES
|
||||
warn 'History not saved; unable to open your history file for writing.'
|
||||
@history_file = false
|
||||
end
|
||||
|
||||
def file_path
|
||||
@file_path || Pry.config.history.file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -407,5 +407,10 @@ class Pry
|
|||
|
||||
"#{move_up}#{prompt}#{colorize_code(code)}#{whitespace}#{move_down}"
|
||||
end
|
||||
|
||||
# Given the current Pry environment, should we try to correct indentation?
|
||||
def should_correct_indentation?
|
||||
Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,7 +59,7 @@ class Pry
|
|||
def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins
|
||||
|
||||
delegate_accessors :@config, :input, :output, :commands, :prompt, :print, :exception_handler,
|
||||
:hooks, :color, :pager, :editor, :memory_size, :input_stack, :extra_sticky_locals
|
||||
:hooks, :color, :pager, :editor, :memory_size, :extra_sticky_locals
|
||||
end
|
||||
|
||||
# Load the given file in the context of `Pry.toplevel_binding`
|
||||
|
@ -119,7 +119,7 @@ class Pry
|
|||
end
|
||||
|
||||
# Start a Pry REPL.
|
||||
# This method also loads the ~/.pryrc and ./.pryrc as necessary
|
||||
# This method also loads `~/.pryrc` and `./.pryrc` as necessary the
|
||||
# first time it is invoked.
|
||||
# @param [Object, Binding] target The receiver of the Pry session
|
||||
# @param [Hash] options
|
||||
|
@ -135,35 +135,24 @@ class Pry
|
|||
return
|
||||
end
|
||||
|
||||
target = Pry.binding_for(target || toplevel_binding)
|
||||
options[:target] = Pry.binding_for(target || toplevel_binding)
|
||||
|
||||
initial_session_setup
|
||||
|
||||
# create the Pry instance to manage the session
|
||||
pry_instance = new(options)
|
||||
# Unless we were given a backtrace, save the current one
|
||||
if options[:backtrace].nil?
|
||||
options[:backtrace] = caller
|
||||
|
||||
# save backtrace
|
||||
pry_instance.backtrace = caller
|
||||
|
||||
# if Pry was started via binding.pry, elide that from the backtrace.
|
||||
pry_instance.backtrace.shift if pry_instance.backtrace.first =~ /pry.*core_extensions.*pry/
|
||||
|
||||
# yield the binding_stack to the hook for modification
|
||||
pry_instance.exec_hook(:when_started, target, options, pry_instance)
|
||||
|
||||
if !pry_instance.binding_stack.empty?
|
||||
head = pry_instance.binding_stack.pop
|
||||
else
|
||||
head = target
|
||||
# If Pry was started via `binding.pry`, elide that from the backtrace
|
||||
if options[:backtrace].first =~ /pry.*core_extensions.*pry/
|
||||
options[:backtrace].shift
|
||||
end
|
||||
end
|
||||
|
||||
# Clear the line before starting Pry. This fixes the issue discussed here:
|
||||
# https://github.com/pry/pry/issues/566
|
||||
if Pry.config.auto_indent
|
||||
Kernel.print Pry::Helpers::BaseHelpers.windows_ansi? ? "\e[0F" : "\e[0G"
|
||||
end
|
||||
driver = options[:driver] || Pry::REPL
|
||||
|
||||
# Enter the matrix
|
||||
pry_instance.repl(head)
|
||||
driver.start(options)
|
||||
rescue Pry::TooSafeException
|
||||
puts "ERROR: Pry cannot work with $SAFE > 0"
|
||||
raise
|
||||
|
@ -202,11 +191,6 @@ class Pry
|
|||
Pry.history.load
|
||||
end
|
||||
|
||||
# Save new lines of Readline history if required.
|
||||
def self.save_history
|
||||
Pry.history.save
|
||||
end
|
||||
|
||||
# @return [Boolean] Whether this is the first time a Pry session has
|
||||
# been started since loading the Pry class.
|
||||
def self.initial_session?
|
||||
|
@ -276,7 +260,6 @@ class Pry
|
|||
config.exception_whitelist = DEFAULT_EXCEPTION_WHITELIST
|
||||
config.default_window_size = 5
|
||||
config.hooks = DEFAULT_HOOKS
|
||||
config.input_stack = []
|
||||
config.color = Helpers::BaseHelpers.use_ansi_codes?
|
||||
config.pager = true
|
||||
config.system = DEFAULT_SYSTEM
|
||||
|
@ -292,9 +275,9 @@ class Pry
|
|||
config.output_prefix = "=> "
|
||||
|
||||
if defined?(Bond) && Readline::VERSION !~ /editline/i
|
||||
config.completer = Pry::BondCompleter
|
||||
config.completer = Pry::BondCompleter.start
|
||||
else
|
||||
config.completer = Pry::InputCompleter
|
||||
config.completer = Pry::InputCompleter.start
|
||||
end
|
||||
|
||||
config.gist ||= OpenStruct.new
|
||||
|
|
|
@ -22,19 +22,18 @@ require "pry/indent"
|
|||
# * the IRC channel, which is #pry on the Freenode network
|
||||
#
|
||||
class Pry
|
||||
|
||||
attr_accessor :input
|
||||
attr_accessor :output
|
||||
attr_accessor :commands
|
||||
attr_accessor :print
|
||||
attr_accessor :exception_handler
|
||||
attr_accessor :input_stack
|
||||
attr_accessor :quiet
|
||||
alias :quiet? :quiet
|
||||
|
||||
attr_accessor :custom_completions
|
||||
|
||||
attr_accessor :binding_stack
|
||||
attr_accessor :eval_string
|
||||
|
||||
attr_accessor :last_result
|
||||
attr_accessor :last_file
|
||||
|
@ -54,45 +53,66 @@ class Pry
|
|||
# This is exposed via Pry::Command#state.
|
||||
attr_reader :command_state
|
||||
|
||||
# Special treatment for hooks as we want to alert people of the
|
||||
# changed API
|
||||
attr_reader :hooks
|
||||
attr_reader :exit_value
|
||||
|
||||
# FIXME:
|
||||
# This is a hack to alert people of the new API.
|
||||
# @param [Pry::Hooks] v Only accept `Pry::Hooks` now!
|
||||
def hooks=(v)
|
||||
if v.is_a?(Hash)
|
||||
warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
|
||||
@hooks = Pry::Hooks.from_hash(v)
|
||||
attr_reader :hooks # Special treatment as we want to alert people of the
|
||||
# changed API.
|
||||
|
||||
# FIXME: This is a hack to alert people of the new API.
|
||||
# @param [Pry::Hooks] hooks
|
||||
def hooks=(hooks)
|
||||
if hooks.is_a?(Hash)
|
||||
warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object " \
|
||||
"instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
|
||||
@hooks = Pry::Hooks.from_hash(hooks)
|
||||
else
|
||||
@hooks = v
|
||||
@hooks = hooks
|
||||
end
|
||||
end
|
||||
|
||||
# Create a new `Pry` object.
|
||||
# @param [Hash] options The optional configuration parameters.
|
||||
# @option options [#readline] :input The object to use for input.
|
||||
# @option options [#puts] :output The object to use for output.
|
||||
# @option options [Pry::CommandBase] :commands The object to use for commands.
|
||||
# @option options [Hash] :hooks The defined hook Procs
|
||||
# @option options [Array<Proc>] :prompt The array of Procs to use for the prompts.
|
||||
# @option options [Proc] :print The Proc to use for the 'print'
|
||||
# @option options [Boolean] :quiet If true, omit the whereami banner when starting.
|
||||
# component of the REPL. (see print.rb)
|
||||
# Create a new {Pry} instance.
|
||||
# @param [Hash] options
|
||||
# @option options [#readline] :input
|
||||
# The object to use for input.
|
||||
# @option options [#puts] :output
|
||||
# The object to use for output.
|
||||
# @option options [Pry::CommandBase] :commands
|
||||
# The object to use for commands.
|
||||
# @option options [Hash] :hooks
|
||||
# The defined hook Procs.
|
||||
# @option options [Array<Proc>] :prompt
|
||||
# The array of Procs to use for prompts.
|
||||
# @option options [Proc] :print
|
||||
# The Proc to use for printing return values.
|
||||
# @option options [Boolean] :quiet
|
||||
# Omit the `whereami` banner when starting.
|
||||
# @option options [Array<String>] :backtrace
|
||||
# The backtrace of the session's `binding.pry` line, if applicable.
|
||||
# @option options [Object] :target
|
||||
# The initial context for this session.
|
||||
def initialize(options={})
|
||||
refresh(options)
|
||||
|
||||
@binding_stack = []
|
||||
@indent = Pry::Indent.new
|
||||
@command_state = {}
|
||||
@eval_string = ""
|
||||
@backtrace = options[:backtrace] || caller
|
||||
|
||||
refresh_config(options)
|
||||
|
||||
push_initial_binding(options[:target])
|
||||
|
||||
set_last_result nil
|
||||
@input_array << nil # add empty input so _in_ and _out_ match
|
||||
|
||||
# yield the binding_stack to the hook for modification
|
||||
exec_hook(:when_started, options[:target], options, self)
|
||||
end
|
||||
|
||||
# Refresh the Pry instance settings from the Pry class.
|
||||
# Allows options to be specified to override settings from Pry class.
|
||||
# @param [Hash] options The options to override Pry class settings
|
||||
# for this instance.
|
||||
def refresh(options={})
|
||||
def refresh_config(options={})
|
||||
defaults = {}
|
||||
attributes = [
|
||||
:input, :output, :commands, :print, :quiet,
|
||||
|
@ -104,8 +124,6 @@ class Pry
|
|||
defaults[attribute] = Pry.send attribute
|
||||
end
|
||||
|
||||
defaults[:input_stack] = Pry.input_stack.dup
|
||||
|
||||
defaults.merge!(options).each do |key, value|
|
||||
send("#{key}=", value) if respond_to?("#{key}=")
|
||||
end
|
||||
|
@ -113,11 +131,25 @@ class Pry
|
|||
true
|
||||
end
|
||||
|
||||
# Initialize this instance by pushing its initial context into the binding
|
||||
# stack. If no target is given, start at the top level.
|
||||
def push_initial_binding(target=nil)
|
||||
push_binding(target || Pry.toplevel_binding)
|
||||
end
|
||||
|
||||
# The currently active `Binding`.
|
||||
# @return [Binding] The currently active `Binding` for the session.
|
||||
def current_context
|
||||
def current_binding
|
||||
binding_stack.last
|
||||
end
|
||||
alias current_context current_binding # support previous API
|
||||
|
||||
# Push a binding for the given object onto the stack. If this instance is
|
||||
# currently stopped, mark it as usable again.
|
||||
def push_binding(object)
|
||||
@stopped = false
|
||||
binding_stack << Pry.binding_for(object)
|
||||
end
|
||||
|
||||
# The current prompt.
|
||||
# This is the prompt at the top of the prompt stack.
|
||||
|
@ -139,6 +171,17 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
# Generate completions.
|
||||
# @param [String] input What the user has typed so far
|
||||
# @return [Array<String>] Possible completions
|
||||
def complete(input)
|
||||
Pry.critical_section do
|
||||
Pry.config.completer.call(input, :target => current_binding,
|
||||
:pry => self,
|
||||
:custom_completions => instance_eval(&custom_completions))
|
||||
end
|
||||
end
|
||||
|
||||
# Injects a local variable into the provided binding.
|
||||
# @param [String] name The name of the local to inject.
|
||||
# @param [Object] value The value to set the local to.
|
||||
|
@ -162,11 +205,10 @@ class Pry
|
|||
@output_array = Pry::HistoryArray.new(size)
|
||||
end
|
||||
|
||||
# Inject all the sticky locals into the `target` binding.
|
||||
# @param [Binding] target
|
||||
def inject_sticky_locals(target)
|
||||
# Inject all the sticky locals into the current binding.
|
||||
def inject_sticky_locals!
|
||||
sticky_locals.each_pair do |name, value|
|
||||
inject_local(name, value, target)
|
||||
inject_local(name, value, current_binding)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -193,139 +235,139 @@ class Pry
|
|||
}.merge(extra_sticky_locals)
|
||||
end
|
||||
|
||||
# Initialize the repl session.
|
||||
# @param [Binding] target The target binding for the session.
|
||||
def repl_prologue(target)
|
||||
exec_hook :before_session, output, target, self
|
||||
set_last_result(nil, target)
|
||||
|
||||
@input_array << nil # add empty input so _in_ and _out_ match
|
||||
|
||||
binding_stack.push target
|
||||
# Reset the current eval string. If the user has entered part of a multiline
|
||||
# expression, this discards that input.
|
||||
def reset_eval_string
|
||||
@eval_string = ""
|
||||
end
|
||||
|
||||
# Clean-up after the repl session.
|
||||
# @param [Binding] target The target binding for the session.
|
||||
def repl_epilogue(target)
|
||||
exec_hook :after_session, output, target, self
|
||||
# Pass a line of input to Pry.
|
||||
#
|
||||
# This is the equivalent of `Binding#eval` but with extra Pry!
|
||||
#
|
||||
# In particular:
|
||||
# 1. Pry commands will be executed immediately if the line matches.
|
||||
# 2. Partial lines of input will be queued up until a complete expression has
|
||||
# been accepted.
|
||||
# 3. Output is written to {#output} in pretty colours, not returned.
|
||||
#
|
||||
# Once this method has raised an exception or returned false, this instance
|
||||
# is no longer usable. {#exit_value} will return the session's breakout
|
||||
# value if applicable.
|
||||
#
|
||||
# @param [String?] line The line of input; `nil` if the user types `<Ctrl-D>`
|
||||
# @option options [Boolean] :generated Whether this line was generated automatically.
|
||||
# Generated lines are not stored in history.
|
||||
# @return [Boolean] Is Pry ready to accept more input?
|
||||
# @raise [Exception] If the user uses the `raise-up` command, this method
|
||||
# will raise that exception.
|
||||
def eval(line, options={})
|
||||
return false if @stopped
|
||||
|
||||
binding_stack.pop
|
||||
Pry.save_history if Pry.config.history.should_save
|
||||
end
|
||||
|
||||
# Start a read-eval-print-loop.
|
||||
# If no parameter is given, default to top-level (main).
|
||||
# @param [Object, Binding] target The receiver of the Pry session
|
||||
# @return [Object] The target of the Pry session or an explictly given
|
||||
# return value. If given return value is `nil` or no return value
|
||||
# is specified then `target` will be returned.
|
||||
# @example
|
||||
# Pry.new.repl(Object.new)
|
||||
def repl(target=TOPLEVEL_BINDING)
|
||||
target = Pry.binding_for(target)
|
||||
|
||||
repl_prologue(target)
|
||||
|
||||
break_data = nil
|
||||
exit_value = nil
|
||||
exception = catch(:raise_up) do
|
||||
break_data = catch(:breakout) do
|
||||
loop do
|
||||
throw(:breakout) if binding_stack.empty?
|
||||
rep(binding_stack.last)
|
||||
end
|
||||
exit_value = catch(:breakout) do
|
||||
handle_line(line, options)
|
||||
# We use 'return !@stopped' here instead of 'return true' so that if
|
||||
# handle_line has stopped this pry instance (e.g. by opening _pry_.repl and
|
||||
# then popping all the bindings) we still exit immediately.
|
||||
return !@stopped
|
||||
end
|
||||
exception = false
|
||||
end
|
||||
|
||||
@stopped = true
|
||||
@exit_value = exit_value
|
||||
|
||||
# TODO: make this configurable?
|
||||
raise exception if exception
|
||||
|
||||
break_data
|
||||
ensure
|
||||
repl_epilogue(target)
|
||||
return false
|
||||
end
|
||||
|
||||
# Perform a read-eval-print.
|
||||
# If no parameter is given, default to top-level (main).
|
||||
# @param [Object, Binding] target The receiver of the read-eval-print
|
||||
# @example
|
||||
# Pry.new.rep(Object.new)
|
||||
def rep(target=TOPLEVEL_BINDING)
|
||||
target = Pry.binding_for(target)
|
||||
result = re(target)
|
||||
|
||||
Pry.critical_section do
|
||||
show_result(result)
|
||||
def handle_line(line, options)
|
||||
if line.nil?
|
||||
Pry.config.control_d_handler.call(@eval_string, self)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# Perform a read-eval
|
||||
# If no parameter is given, default to top-level (main).
|
||||
# @param [Object, Binding] target The receiver of the read-eval-print
|
||||
# @return [Object] The result of the eval or an `Exception` object in case of
|
||||
# error. In the latter case, you can check whether the exception was raised
|
||||
# or is just the result of the expression using #last_result_is_exception?
|
||||
# @example
|
||||
# Pry.new.re(Object.new)
|
||||
def re(target=TOPLEVEL_BINDING)
|
||||
target = Pry.binding_for(target)
|
||||
ensure_correct_encoding!(line)
|
||||
Pry.history << line unless options[:generated]
|
||||
|
||||
# It's not actually redundant to inject them continually as we may have
|
||||
# moved into the scope of a new Binding (e.g the user typed `cd`).
|
||||
inject_sticky_locals(target)
|
||||
|
||||
code = r(target)
|
||||
|
||||
evaluate_ruby(code, target)
|
||||
rescue RescuableException => e
|
||||
self.last_exception = e
|
||||
e
|
||||
end
|
||||
|
||||
# Perform a read.
|
||||
# If no parameter is given, default to top-level (main).
|
||||
# This is a multi-line read; so the read continues until a valid
|
||||
# Ruby expression is received.
|
||||
# Pry commands are also accepted here and operate on the target.
|
||||
# @param [Object, Binding] target The receiver of the read.
|
||||
# @param [String] eval_string Optionally Prime `eval_string` with a start value.
|
||||
# @return [String] The Ruby expression.
|
||||
# @example
|
||||
# Pry.new.r(Object.new)
|
||||
def r(target=TOPLEVEL_BINDING, eval_string="")
|
||||
target = Pry.binding_for(target)
|
||||
@suppress_output = false
|
||||
|
||||
loop do
|
||||
begin
|
||||
# eval_string will probably be mutated by this method
|
||||
retrieve_line(eval_string, target)
|
||||
rescue CommandError, Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
|
||||
output.puts "Error: #{e.message}"
|
||||
inject_sticky_locals!
|
||||
begin
|
||||
if !process_command_safely(line.lstrip)
|
||||
@eval_string << "#{line.chomp}\n" unless line.empty?
|
||||
end
|
||||
rescue RescuableException => e
|
||||
self.last_exception = e
|
||||
result = e
|
||||
|
||||
begin
|
||||
break if Pry::Code.complete_expression?(eval_string)
|
||||
rescue SyntaxError => e
|
||||
exception_handler.call(output, e.extend(UserError), self)
|
||||
eval_string = ""
|
||||
Pry.critical_section do
|
||||
show_result(result)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if eval_string =~ /;\Z/ || eval_string.empty? || eval_string =~ /\A *#.*\n\z/
|
||||
@suppress_output = true
|
||||
end
|
||||
|
||||
# This hook is supposed to be executed after each line of ruby code
|
||||
# has been read (regardless of whether eval_string is yet a complete expression)
|
||||
exec_hook :after_read, eval_string, self
|
||||
eval_string
|
||||
|
||||
begin
|
||||
complete_expr = Pry::Code.complete_expression?(@eval_string)
|
||||
rescue SyntaxError => e
|
||||
output.puts "SyntaxError: #{e.message.sub(/.*syntax error, */m, '')}"
|
||||
reset_eval_string
|
||||
end
|
||||
|
||||
if complete_expr
|
||||
if @eval_string =~ /;\Z/ || @eval_string.empty? || @eval_string =~ /\A *#.*\n\z/
|
||||
@suppress_output = true
|
||||
end
|
||||
|
||||
begin
|
||||
# Reset eval string, in case we're evaluating Ruby that does something
|
||||
# like open a nested REPL on this instance.
|
||||
eval_string = @eval_string
|
||||
reset_eval_string
|
||||
|
||||
result = evaluate_ruby(eval_string)
|
||||
rescue RescuableException => e
|
||||
self.last_exception = e
|
||||
result = e
|
||||
end
|
||||
|
||||
Pry.critical_section do
|
||||
show_result(result)
|
||||
end
|
||||
end
|
||||
|
||||
throw(:breakout) if current_binding.nil?
|
||||
end
|
||||
private :handle_line
|
||||
|
||||
# @deprecated Use `Pry::REPL.new(pry, :target => target).start` instead.
|
||||
def repl(target = nil)
|
||||
@@repl_warning ||= (warn Pry::Helpers::CommandHelpers.unindent(<<-S); true)
|
||||
DEPRECATION: Pry#repl is deprecated. Instead, use
|
||||
|
||||
Pry::REPL.new(pry, :target => target).start
|
||||
|
||||
where pry is the Pry instance you called #repl on and target is the
|
||||
optional target parameter of #repl.
|
||||
|
||||
Call stack:
|
||||
#{caller.join("\n" + (' ' * 8))}
|
||||
S
|
||||
Pry::REPL.new(self, :target => target).start
|
||||
end
|
||||
|
||||
def evaluate_ruby(code, target = binding_stack.last)
|
||||
target = Pry.binding_for(target)
|
||||
inject_sticky_locals(target)
|
||||
def evaluate_ruby(code)
|
||||
inject_sticky_locals!
|
||||
exec_hook :before_eval, code, self
|
||||
|
||||
result = target.eval(code, Pry.eval_path, Pry.current_line)
|
||||
set_last_result(result, target, code)
|
||||
result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
|
||||
set_last_result(result, code)
|
||||
ensure
|
||||
update_input_history(code)
|
||||
exec_hook :after_eval, result, self
|
||||
|
@ -355,96 +397,28 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
def should_force_encoding?(eval_string, val)
|
||||
eval_string.empty? && val.respond_to?(:encoding) && val.encoding != eval_string.encoding
|
||||
end
|
||||
private :should_force_encoding?
|
||||
|
||||
# Read and process a line of input -- check for ^D, determine which prompt to
|
||||
# use, rewrite the indentation if `Pry.config.auto_indent` is enabled, and,
|
||||
# if the line is a command, process it and alter the eval_string accordingly.
|
||||
# This method should not need to be invoked directly.
|
||||
#
|
||||
# @param [String] eval_string The cumulative lines of input.
|
||||
# @param [Binding] target The target of the session.
|
||||
# @return [String] The line received.
|
||||
def retrieve_line(eval_string, target)
|
||||
@indent.reset if eval_string.empty?
|
||||
|
||||
current_prompt = select_prompt(eval_string, target)
|
||||
completion_proc = Pry.config.completer.build_completion_proc(target, self,
|
||||
instance_eval(&custom_completions))
|
||||
|
||||
safe_completion_proc = proc{ |*a| Pry.critical_section{ completion_proc.call(*a) } }
|
||||
|
||||
indentation = Pry.config.auto_indent ? @indent.current_prefix : ''
|
||||
|
||||
begin
|
||||
val = readline("#{current_prompt}#{indentation}", safe_completion_proc)
|
||||
|
||||
# Handle <Ctrl+C> like Bash, empty the current input buffer but do not quit.
|
||||
# This is only for ruby-1.9; other versions of ruby do not let you send Interrupt
|
||||
# from within Readline.
|
||||
rescue Interrupt
|
||||
output.puts ""
|
||||
eval_string.replace("")
|
||||
return
|
||||
end
|
||||
|
||||
# invoke handler if we receive EOF character (^D)
|
||||
if !val
|
||||
output.puts ""
|
||||
Pry.config.control_d_handler.call(eval_string, self)
|
||||
return
|
||||
end
|
||||
|
||||
# Change the eval_string into the input encoding (Issue 284)
|
||||
# TODO: This wouldn't be necessary if the eval_string was constructed from
|
||||
# input strings only.
|
||||
if should_force_encoding?(eval_string, val)
|
||||
eval_string.force_encoding(val.encoding)
|
||||
end
|
||||
|
||||
if Pry.config.auto_indent && !input.is_a?(StringIO)
|
||||
original_val = "#{indentation}#{val}"
|
||||
indented_val = @indent.indent(val)
|
||||
|
||||
if output.tty? && Pry::Helpers::BaseHelpers.use_ansi_codes? && Pry.config.correct_indent
|
||||
output.print @indent.correct_indentation(current_prompt, indented_val, original_val.length - indented_val.length)
|
||||
output.flush
|
||||
end
|
||||
else
|
||||
indented_val = val
|
||||
end
|
||||
|
||||
# Check this before processing the line, because a command might change
|
||||
# Pry's input.
|
||||
interactive = !input.is_a?(StringIO)
|
||||
|
||||
begin
|
||||
if !process_command(val, eval_string, target)
|
||||
eval_string << "#{indented_val.chomp}\n" unless val.empty?
|
||||
end
|
||||
ensure
|
||||
Pry.history << indented_val if interactive
|
||||
# Force `eval_string` into the encoding of `val`. [Issue #284]
|
||||
def ensure_correct_encoding!(val)
|
||||
if @eval_string.empty? &&
|
||||
val.respond_to?(:encoding) &&
|
||||
val.encoding != @eval_string.encoding
|
||||
@eval_string.force_encoding(val.encoding)
|
||||
end
|
||||
end
|
||||
private :ensure_correct_encoding!
|
||||
|
||||
# If the given line is a valid command, process it in the context of the
|
||||
# current `eval_string` and context.
|
||||
# This method should not need to be invoked directly.
|
||||
# current `eval_string` and binding.
|
||||
# @param [String] val The line to process.
|
||||
# @param [String] eval_string The cumulative lines of input.
|
||||
# @param [Binding] target The target of the Pry session.
|
||||
# @return [Boolean] `true` if `val` is a command, `false` otherwise
|
||||
def process_command(val, eval_string = '', target = binding_stack.last)
|
||||
def process_command(val)
|
||||
val = val.chomp
|
||||
result = commands.process_line(val, {
|
||||
:target => target,
|
||||
result = commands.process_line(val,
|
||||
:target => current_binding,
|
||||
:output => output,
|
||||
:eval_string => eval_string,
|
||||
:eval_string => @eval_string,
|
||||
:pry_instance => self
|
||||
})
|
||||
)
|
||||
|
||||
# set a temporary (just so we can inject the value we want into eval_string)
|
||||
Pry.current[:pry_cmd_result] = result
|
||||
|
@ -457,7 +431,7 @@ class Pry
|
|||
# the command that was invoked was non-void (had a return value) and so we make
|
||||
# the value of the current expression equal to the return value
|
||||
# of the command.
|
||||
eval_string.replace "::Pry.current[:pry_cmd_result].retval\n"
|
||||
@eval_string.replace "::Pry.current[:pry_cmd_result].retval\n"
|
||||
end
|
||||
true
|
||||
else
|
||||
|
@ -465,17 +439,26 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
# Same as process_command, but outputs exceptions to {#output} instead of
|
||||
# raising.
|
||||
# @param [String] val The line to process.
|
||||
# @return [Boolean] `true` if `val` is a command, `false` otherwise
|
||||
def process_command_safely(val)
|
||||
process_command(val)
|
||||
rescue CommandError, Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
|
||||
output.puts "Error: #{e.message}"
|
||||
true
|
||||
end
|
||||
|
||||
# Run the specified command.
|
||||
# @param [String] val The command (and its params) to execute.
|
||||
# @param [String] eval_string The current input buffer.
|
||||
# @param [Binding] target The binding to use..
|
||||
# @return [Pry::Command::VOID_VALUE]
|
||||
# @example
|
||||
# pry_instance.run_command("ls -m")
|
||||
def run_command(val, eval_string = "", target = binding_stack.last)
|
||||
def run_command(val)
|
||||
commands.process_line(val,
|
||||
:eval_string => eval_string,
|
||||
:target => target,
|
||||
:eval_string => @eval_string,
|
||||
:target => current_binding,
|
||||
:pry_instance => self,
|
||||
:output => output
|
||||
)
|
||||
|
@ -504,9 +487,8 @@ class Pry
|
|||
# Set the last result of an eval.
|
||||
# This method should not need to be invoked directly.
|
||||
# @param [Object] result The result.
|
||||
# @param [Binding] target The binding to set `_` on.
|
||||
# @param [String] code The code that was run.
|
||||
def set_last_result(result, target, code="")
|
||||
def set_last_result(result, code="")
|
||||
@last_result_is_exception = false
|
||||
@output_array << result
|
||||
|
||||
|
@ -555,81 +537,6 @@ class Pry
|
|||
@last_result_is_exception
|
||||
end
|
||||
|
||||
# Manage switching of input objects on encountering EOFErrors
|
||||
def handle_read_errors
|
||||
should_retry = true
|
||||
exception_count = 0
|
||||
begin
|
||||
yield
|
||||
rescue EOFError
|
||||
if input_stack.empty?
|
||||
self.input = Pry.config.input
|
||||
if !should_retry
|
||||
output.puts "Error: Pry ran out of things to read from! Attempting to break out of REPL."
|
||||
throw(:breakout)
|
||||
end
|
||||
should_retry = false
|
||||
else
|
||||
self.input = input_stack.pop
|
||||
end
|
||||
|
||||
retry
|
||||
|
||||
# Interrupts are handled in r() because they need to tweak eval_string
|
||||
# TODO: Refactor this baby.
|
||||
rescue Interrupt
|
||||
raise
|
||||
|
||||
# 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"
|
||||
throw(:breakout)
|
||||
end
|
||||
end
|
||||
private :handle_read_errors
|
||||
|
||||
# Returns the next line of input to be used by the pry instance.
|
||||
# This method should not need to be invoked directly.
|
||||
# @param [String] current_prompt The prompt to use for input.
|
||||
# @return [String] The next line of input.
|
||||
def readline(current_prompt="> ", completion_proc=nil)
|
||||
handle_read_errors do
|
||||
|
||||
if defined? Coolline and input.is_a? Coolline
|
||||
input.completion_proc = proc do |cool|
|
||||
completions = completion_proc.call cool.completed_word
|
||||
completions.compact
|
||||
end
|
||||
elsif input.respond_to? :completion_proc=
|
||||
input.completion_proc = completion_proc
|
||||
end
|
||||
|
||||
if input == Readline
|
||||
input.readline(current_prompt, false) # false since we'll add it manually
|
||||
elsif defined? Coolline and input.is_a? Coolline
|
||||
input.readline(current_prompt)
|
||||
else
|
||||
if input.method(:readline).arity == 1
|
||||
input.readline(current_prompt)
|
||||
else
|
||||
input.readline
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Whether the print proc should be invoked.
|
||||
# Currently only invoked if the output is not suppressed.
|
||||
# @return [Boolean] Whether the print proc should be invoked.
|
||||
|
@ -638,18 +545,15 @@ class Pry
|
|||
end
|
||||
|
||||
# Returns the appropriate prompt to use.
|
||||
# This method should not need to be invoked directly.
|
||||
# @param [String] eval_string The current input buffer.
|
||||
# @param [Binding] target The target Binding of the Pry session.
|
||||
# @return [String] The prompt.
|
||||
def select_prompt(eval_string, target)
|
||||
target_self = target.eval('self')
|
||||
def select_prompt
|
||||
object = current_binding.eval('self')
|
||||
|
||||
open_token = @indent.open_delimiters.any? ? @indent.open_delimiters.last :
|
||||
@indent.stack.last
|
||||
|
||||
c = OpenStruct.new(
|
||||
:object => target_self,
|
||||
:object => object,
|
||||
:nesting_level => binding_stack.size - 1,
|
||||
:open_token => open_token,
|
||||
:session_line => Pry.history.session_line_count + 1,
|
||||
|
@ -658,8 +562,8 @@ class Pry
|
|||
:_pry_ => self,
|
||||
:binding_stack => binding_stack,
|
||||
:input_array => input_array,
|
||||
:eval_string => eval_string,
|
||||
:cont => !eval_string.empty?)
|
||||
:eval_string => @eval_string,
|
||||
:cont => !@eval_string.empty?)
|
||||
|
||||
Pry.critical_section do
|
||||
# If input buffer is empty then use normal prompt
|
||||
|
|
203
lib/pry/repl.rb
Normal file
203
lib/pry/repl.rb
Normal file
|
@ -0,0 +1,203 @@
|
|||
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 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
|
||||
|
||||
# Clean up after the repl session.
|
||||
# @return [void]
|
||||
def epilogue
|
||||
pry.exec_hook :after_session, pry.output, pry.current_binding, pry
|
||||
end
|
||||
|
||||
# Read a line of input from the user.
|
||||
# @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
|
||||
@indent.reset if pry.eval_string.empty?
|
||||
|
||||
current_prompt = pry.select_prompt
|
||||
|
||||
indentation = Pry.config.auto_indent ? @indent.current_prefix : ''
|
||||
|
||||
begin
|
||||
val = read_line("#{current_prompt}#{indentation}")
|
||||
|
||||
# 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.
|
||||
rescue Interrupt
|
||||
val = :control_c
|
||||
end
|
||||
|
||||
# Return nil for EOF, :no_more_input for error, or :control_c for <Ctrl-C>
|
||||
return val unless String === val
|
||||
|
||||
if Pry.config.auto_indent
|
||||
original_val = "#{indentation}#{val}"
|
||||
indented_val = @indent.indent(val)
|
||||
|
||||
if output.tty? && @indent.should_correct_indentation?
|
||||
output.print @indent.correct_indentation(
|
||||
current_prompt, indented_val,
|
||||
original_val.length - indented_val.length
|
||||
)
|
||||
output.flush
|
||||
end
|
||||
else
|
||||
indented_val = val
|
||||
end
|
||||
|
||||
indented_val
|
||||
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 handle_read_errors
|
||||
should_retry = true
|
||||
exception_count = 0
|
||||
|
||||
begin
|
||||
yield
|
||||
rescue EOFError
|
||||
pry.input = Pry.config.input
|
||||
if !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
|
||||
|
||||
rescue Interrupt
|
||||
raise
|
||||
|
||||
# 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
|
||||
|
||||
# Returns the next line of input to be sent to the {Pry} instance.
|
||||
# @param [String] current_prompt The prompt to use for input.
|
||||
# @return [String?] The next line of input, or `nil` on <Ctrl-D>.
|
||||
def read_line(current_prompt)
|
||||
handle_read_errors do
|
||||
if defined? Coolline and input.is_a? Coolline
|
||||
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 |input|
|
||||
@pry.complete input
|
||||
end
|
||||
end
|
||||
|
||||
if input == Readline
|
||||
input.readline(current_prompt, false) # false since we'll add it manually
|
||||
elsif defined? Coolline and input.is_a? Coolline
|
||||
input.readline(current_prompt)
|
||||
else
|
||||
if input.method(:readline).arity == 1
|
||||
input.readline(current_prompt)
|
||||
else
|
||||
input.readline
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,7 +15,8 @@ class Pry
|
|||
full_name = File.expand_path(file_name)
|
||||
raise RuntimeError, "No such file: #{full_name}" if !File.exists?(full_name)
|
||||
|
||||
@content = StringIO.new(File.read(full_name))
|
||||
define_additional_commands
|
||||
@content = File.read(full_name)
|
||||
end
|
||||
|
||||
# Switch to interactive mode, i.e take input from the user
|
||||
|
@ -25,19 +26,24 @@ class Pry
|
|||
_pry_.input = Pry.config.input
|
||||
_pry_.print = Pry.config.print
|
||||
_pry_.exception_handler = Pry.config.exception_handler
|
||||
Pry::REPL.new(_pry_).start
|
||||
end
|
||||
|
||||
# Switch to non-interactive mode. Essentially
|
||||
# this means there is no result output
|
||||
# and that the session becomes interactive when an exception is encountered.
|
||||
# @param [Pry] _pry_ the Pry instance to make non-interactive.
|
||||
def non_interactive_mode(_pry_)
|
||||
def non_interactive_mode(_pry_, content)
|
||||
_pry_.print = proc {}
|
||||
_pry_.exception_handler = proc do |o, e, _pry_|
|
||||
_pry_.run_command "cat --ex"
|
||||
_pry_.exception_handler = proc do |o, e, _p_|
|
||||
_p_.run_command "cat --ex"
|
||||
o.puts "...exception encountered, going interactive!"
|
||||
interactive_mode(_pry_)
|
||||
end
|
||||
|
||||
content.lines.each do |line|
|
||||
break unless _pry_.eval line, :generated => true
|
||||
end
|
||||
end
|
||||
|
||||
# Define a few extra commands useful for flipping back & forth
|
||||
|
@ -46,35 +52,18 @@ class Pry
|
|||
s = self
|
||||
|
||||
Pry::Commands.command "make-interactive", "Make the session interactive" do
|
||||
_pry_.input_stack.push _pry_.input
|
||||
s.interactive_mode(_pry_)
|
||||
end
|
||||
|
||||
Pry::Commands.command "make-non-interactive", "Make the session non-interactive" do
|
||||
_pry_.input = _pry_.input_stack.pop
|
||||
s.non_interactive_mode(_pry_)
|
||||
end
|
||||
|
||||
Pry::Commands.command "load-file", "Load another file through the repl" do |file_name|
|
||||
content = StringIO.new(File.read(File.expand_path(file_name)))
|
||||
_pry_.input_stack.push(_pry_.input)
|
||||
_pry_.input = content
|
||||
s.non_interactive_mode(_pry_, File.read(File.expand_path(file_name)))
|
||||
end
|
||||
end
|
||||
|
||||
# Actually load the file through the REPL by setting file content
|
||||
# as the REPL input stream.
|
||||
def load
|
||||
Pry.initial_session_setup
|
||||
define_additional_commands
|
||||
|
||||
Pry.config.hooks.add_hook(:when_started, :start_non_interactively) do |o, t, _pry_|
|
||||
non_interactive_mode(_pry_)
|
||||
end
|
||||
|
||||
Pry.start(Pry.toplevel_binding,
|
||||
:input => @content,
|
||||
:input_stack => [StringIO.new("exit-all\n")])
|
||||
non_interactive_mode(Pry.new, @content)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ class << Pry
|
|||
Pry.config.should_load_plugins = false
|
||||
Pry.config.history.should_load = false
|
||||
Pry.config.history.should_save = false
|
||||
Pry.config.auto_indent = false
|
||||
Pry.config.correct_indent = false
|
||||
Pry.config.hooks = Pry::Hooks.new
|
||||
Pry.config.collision_warning = false
|
||||
end
|
||||
|
@ -101,18 +101,17 @@ def pry_eval(*eval_strs)
|
|||
end
|
||||
|
||||
class PryTester
|
||||
extend Forwardable
|
||||
|
||||
attr_reader :pry, :out
|
||||
|
||||
def initialize(context = TOPLEVEL_BINDING, options = {})
|
||||
@pry = Pry.new(options)
|
||||
def_delegators :@pry, :eval_string, :eval_string=
|
||||
|
||||
if context
|
||||
target = Pry.binding_for(context)
|
||||
@pry.binding_stack << target
|
||||
@pry.inject_sticky_locals(target)
|
||||
end
|
||||
def initialize(target = TOPLEVEL_BINDING, options = {})
|
||||
@pry = Pry.new(options.merge(:target => target))
|
||||
@history = options[:history]
|
||||
|
||||
@pry.input_array << nil # TODO: shouldn't need this
|
||||
@pry.inject_sticky_locals!
|
||||
reset_output
|
||||
end
|
||||
|
||||
|
@ -122,6 +121,8 @@ class PryTester
|
|||
|
||||
strs.flatten.each do |str|
|
||||
str = "#{str.strip}\n"
|
||||
@history.push str if @history
|
||||
|
||||
if @pry.process_command(str)
|
||||
result = last_command_result_or_output
|
||||
else
|
||||
|
@ -132,43 +133,32 @@ class PryTester
|
|||
result
|
||||
end
|
||||
|
||||
def context=(context)
|
||||
@pry.binding_stack << Pry.binding_for(context)
|
||||
def push(*lines)
|
||||
Array(lines).flatten.each do |line|
|
||||
@pry.eval(line)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: eliminate duplication with Pry#repl
|
||||
def simulate_repl
|
||||
didnt_exit = nil
|
||||
break_data = nil
|
||||
|
||||
didnt_exit = catch(:didnt_exit) do
|
||||
break_data = catch(:breakout) do
|
||||
yield self
|
||||
throw(:didnt_exit, true)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
raise "Failed to exit REPL" if didnt_exit
|
||||
break_data
|
||||
def push_binding(context)
|
||||
@pry.push_binding context
|
||||
end
|
||||
|
||||
def last_output
|
||||
@out.string if @out
|
||||
end
|
||||
|
||||
def process_command(command_str, eval_str = '')
|
||||
@pry.process_command(command_str, eval_str) or raise "Not a valid command"
|
||||
def process_command(command_str)
|
||||
@pry.process_command(command_str) or raise "Not a valid command"
|
||||
last_command_result_or_output
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def last_command_result
|
||||
result = Pry.current[:pry_cmd_result]
|
||||
result.retval if result
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def last_command_result_or_output
|
||||
result = last_command_result
|
||||
if result != Pry::Command::VOID_VALUE
|
||||
|
|
|
@ -28,6 +28,7 @@ describe "commands" do
|
|||
|
||||
after do
|
||||
Pad.clear
|
||||
Pry.reset_defaults
|
||||
end
|
||||
|
||||
describe "alias_command" do
|
||||
|
@ -38,15 +39,10 @@ describe "commands" do
|
|||
end
|
||||
alias_command "test-alias", "test-command"
|
||||
end
|
||||
redirect_pry_io(InputTester.new("test-alias"), out1 = StringIO.new) do
|
||||
Pry.start self, :commands => set
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("test-command"), out2 = StringIO.new) do
|
||||
Pry.start self, :commands => set
|
||||
pry_tester(:commands => set).tap do |t|
|
||||
t.eval('test-command').should == t.eval('test-alias')
|
||||
end
|
||||
|
||||
out1.string.should == out2.string
|
||||
end
|
||||
|
||||
it 'should pass on arguments to original' do
|
||||
|
@ -181,9 +177,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => InputTester.new("run_v"), :output => @str_output, :commands => klass).rep
|
||||
|
||||
@str_output.string.should =~ /v command/
|
||||
pry_tester(:commands => klass).eval('run_v').should =~ /v command/
|
||||
end
|
||||
|
||||
it 'should run a regex command from within a command' do
|
||||
|
@ -197,11 +191,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("run_v"), @str_output) do
|
||||
Pry.new(:commands => klass).rep
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /v baby/
|
||||
pry_tester(:commands => klass).eval('run_v').should =~ /v baby/
|
||||
end
|
||||
|
||||
it 'should run a command from within a command with arguments' do
|
||||
|
@ -220,33 +210,22 @@ describe "commands" do
|
|||
end
|
||||
|
||||
["run_v_explicit_parameter", "run_v_embedded_parameter"].each do |cmd|
|
||||
redirect_pry_io(InputTester.new(cmd), @str_output) do
|
||||
Pry.new(:commands => klass).rep
|
||||
end
|
||||
@str_output.string.should =~ /v baby param/
|
||||
pry_tester(:commands => klass).eval(cmd).should =~ /v baby param/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Pry#run_command" do
|
||||
it 'should run a command in a specified context' do
|
||||
b = Pry.binding_for('seven')
|
||||
p = Pry.new(:output => @str_output)
|
||||
p.run_command("ls -m", "", b)
|
||||
p.output.string.should =~ /downcase/
|
||||
end
|
||||
|
||||
it 'should run a command that modifies the passed in eval_string' do
|
||||
b = Pry.binding_for(7)
|
||||
p = Pry.new(:output => @str_output)
|
||||
eval_string = "def hello\npeter pan\n"
|
||||
p.run_command("amend-line !", eval_string, b)
|
||||
eval_string.should =~ /def hello/
|
||||
eval_string.should.not =~ /peter pan/
|
||||
p.eval "def hello\npeter pan\n"
|
||||
p.run_command "amend-line !"
|
||||
p.eval_string.should =~ /def hello/
|
||||
p.eval_string.should.not =~ /peter pan/
|
||||
end
|
||||
|
||||
it 'should run a command in the context of a session' do
|
||||
pry_tester.tap do |t|
|
||||
pry_tester(Object.new).tap do |t|
|
||||
t.eval "@session_ivar = 10", "_pry_.run_command('ls')"
|
||||
t.last_output.should =~ /@session_ivar/
|
||||
end
|
||||
|
@ -260,10 +239,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
str_input = StringIO.new('hello #{Pad.bong}')
|
||||
Pry.new(:input => str_input, :output => @str_output, :commands => set).rep
|
||||
|
||||
@str_output.string.should =~ /bong/
|
||||
pry_tester(:commands => set).eval('hello #{Pad.bong}').should =~ /bong/
|
||||
end
|
||||
|
||||
# bug fix for https://github.com/pry/pry/issues/170
|
||||
|
@ -282,39 +258,32 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
str_input = StringIO.new('hello #{Pad.bong}')
|
||||
Pry.new(:input => str_input, :output => @str_output, :commands => set).rep
|
||||
|
||||
@str_output.string.should =~ /Pad\.bong/
|
||||
pry_tester(:commands => set).eval('hello #{Pad.bong}').
|
||||
should =~ /Pad\.bong/
|
||||
end
|
||||
|
||||
it 'should NOT try to interpolate pure ruby code (no commands) ' do
|
||||
Pry.new(:input => StringIO.new('format \'#{aggy}\''), :output => @str_output).rep
|
||||
@str_output.string.should.not =~ /NameError/
|
||||
# These should raise RuntimeError instead of NameError
|
||||
proc {
|
||||
pry_eval 'raise \'#{aggy}\''
|
||||
}.should.raise(RuntimeError)
|
||||
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:input => StringIO.new('format #{aggy}'), :output => @str_output).rep
|
||||
@str_output.string.should.not =~ /NameError/
|
||||
proc {
|
||||
pry_eval 'raise #{aggy}'
|
||||
}.should.raise(RuntimeError)
|
||||
|
||||
@str_output = StringIO.new
|
||||
Pad.interp = "bong"
|
||||
Pry.new(:input => StringIO.new('format \'#{Pad.interp}\''), :output => @str_output).rep
|
||||
|
||||
@str_output.string.should.not =~ /bong/
|
||||
pry_eval('format \'#{my_var}\'').should == "\#{my_var}"
|
||||
end
|
||||
|
||||
it 'should create a command with a space in its name' do
|
||||
it 'should create a command with a space in its name zzz' do
|
||||
set = Pry::CommandSet.new do
|
||||
command "hello baby", "" do
|
||||
output.puts "hello baby command"
|
||||
end
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("hello baby", "exit-all"), @str_output) do
|
||||
Pry.new(:commands => set).rep
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /hello baby command/
|
||||
pry_tester(:commands => set).eval('hello baby').
|
||||
should =~ /hello baby command/
|
||||
end
|
||||
|
||||
it 'should create a command with a space in its name and pass an argument' do
|
||||
|
@ -324,11 +293,8 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("hello baby john"), @str_output) do
|
||||
Pry.new(:commands => set).rep
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /hello baby command john/
|
||||
pry_tester(:commands => set).eval('hello baby john').
|
||||
should =~ /hello baby command john/
|
||||
end
|
||||
|
||||
it 'should create a regex command and be able to invoke it' do
|
||||
|
@ -339,11 +305,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("hello1"), @str_output) do
|
||||
Pry.new(:commands => set).rep
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /hello1/
|
||||
pry_tester(:commands => set).eval('hello1').should =~ /hello1/
|
||||
end
|
||||
|
||||
it 'should create a regex command and pass captures into the args list before regular arguments' do
|
||||
|
@ -353,11 +315,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("hello1 baby"), @str_output) do
|
||||
Pry.new(:commands => set).rep
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /hello 1 baby/
|
||||
pry_tester(:commands => set).eval('hello1 baby').should =~ /hello 1 baby/
|
||||
end
|
||||
|
||||
it 'should create a regex command and interpolate the captures' do
|
||||
|
@ -367,11 +325,9 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new('hello #{Pad.bong}'), @str_output) do
|
||||
Pry.new(:commands => set).rep
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /hello bong/
|
||||
bong = "bong"
|
||||
pry_tester(binding, :commands => set).eval('hello #{bong}').
|
||||
should =~ /hello bong/
|
||||
end
|
||||
|
||||
it 'should create a regex command and arg_string should be interpolated' do
|
||||
|
@ -381,14 +337,13 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pad.bing = "bing"
|
||||
Pad.bang = "bang"
|
||||
redirect_pry_io(InputTester.new('hellojohn #{Pad.bing} #{Pad.bong} #{Pad.bang}'),
|
||||
@str_output) do
|
||||
Pry.new(:commands => set).rep
|
||||
end
|
||||
bing = 'bing'
|
||||
bong = 'bong'
|
||||
bang = 'bang'
|
||||
|
||||
@str_output.string.should =~ /hello john bing bong bang/
|
||||
pry_tester(binding, :commands => set).
|
||||
eval('hellojohn #{bing} #{bong} #{bang}').
|
||||
should =~ /hello john bing bong bang/
|
||||
end
|
||||
|
||||
it 'if a regex capture is missing it should be nil' do
|
||||
|
@ -398,20 +353,17 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
redirect_pry_io(InputTester.new("hello baby"), @str_output) do
|
||||
Pry.new(:commands => set).rep
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /hello nil baby/
|
||||
pry_tester(:commands => set).eval('hello baby').should =~ /hello nil baby/
|
||||
end
|
||||
|
||||
it 'should create a command in a nested context and that command should be accessible from the parent' do
|
||||
x = "@x=nil\ncd 7\n_pry_.commands.instance_eval {\ncommand('bing') { |arg| run arg }\n}\ncd ..\nbing ls\nexit-all"
|
||||
redirect_pry_io(StringIO.new("@x=nil\ncd 7\n_pry_.commands.instance_eval {\ncommand('bing') { |arg| run arg }\n}\ncd ..\nbing ls\nexit-all"), @str_output) do
|
||||
Pry.new.repl('0')
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /@x/
|
||||
pry_tester(Object.new).eval(*(<<-RUBY.split("\n"))).should =~ /instance variables:\s+@x/m
|
||||
@x = nil
|
||||
cd 7
|
||||
_pry_.commands.instance_eval { command('bing') { |arg| run arg } }
|
||||
cd ..
|
||||
bing ls
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'should define a command that keeps its return value' do
|
||||
|
@ -421,9 +373,9 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
|
||||
@str_output.string.should =~ /:kept_hello/
|
||||
@str_output.string.should =~ /=>/
|
||||
t = pry_tester(:commands => klass)
|
||||
t.eval("hello\n")
|
||||
t.last_command_result.should == :kept_hello
|
||||
end
|
||||
|
||||
it 'should define a command that does NOT keep its return value' do
|
||||
|
@ -433,9 +385,9 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
|
||||
(@str_output.string =~ /:kept_hello/).should == nil
|
||||
@str_output.string !~ /=>/
|
||||
t = pry_tester(:commands => klass)
|
||||
t.eval("hello\n").should == ''
|
||||
t.last_command_result.should == Pry::Command::VOID_VALUE
|
||||
end
|
||||
|
||||
it 'should define a command that keeps its return value even when nil' do
|
||||
|
@ -445,10 +397,9 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
|
||||
|
||||
@str_output.string.should =~ /nil/
|
||||
@str_output.string.should =~ /=>/
|
||||
t = pry_tester(:commands => klass)
|
||||
t.eval("hello\n")
|
||||
t.last_command_result.should == nil
|
||||
end
|
||||
|
||||
it 'should define a command that keeps its return value but does not return when value is void' do
|
||||
|
@ -458,8 +409,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => StringIO.new("hello\n"), :output => @str_output, :commands => klass).rep
|
||||
@str_output.string.empty?.should == true
|
||||
pry_tester(:commands => klass).eval("hello\n").empty?.should == true
|
||||
end
|
||||
|
||||
it 'a command (with :keep_retval => false) that replaces eval_string with a valid expression should not have the expression value suppressed' do
|
||||
|
@ -469,8 +419,13 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => @str_output, :commands => klass).rep
|
||||
@str_output.string.should =~ /6/
|
||||
output = StringIO.new
|
||||
|
||||
redirect_pry_io(InputTester.new('def yo', 'hello'), output) do
|
||||
Pry.start self, :commands => klass
|
||||
end
|
||||
|
||||
output.string.should =~ /6/
|
||||
end
|
||||
|
||||
it 'a command (with :keep_retval => true) that replaces eval_string with a valid expression should overwrite the eval_string with the return value' do
|
||||
|
@ -481,10 +436,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => @str_output, :commands => klass).rep
|
||||
|
||||
@str_output.string.should =~ /7/
|
||||
@str_output.string.should.not =~ /6/
|
||||
pry_tester(:commands => klass).eval("def yo\nhello\n").should == 7
|
||||
end
|
||||
|
||||
it 'a command that return a value in a multi-line expression should clear the expression and return the value' do
|
||||
|
@ -494,9 +446,7 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.new(:input => StringIO.new("def yo\nhello\n"), :output => @str_output, :commands => klass).rep
|
||||
|
||||
@str_output.string.should =~ /5/
|
||||
pry_tester(:commands => klass).eval("def yo\nhello\n").should == 5
|
||||
end
|
||||
|
||||
it 'should set the commands default, and the default should be overridable' do
|
||||
|
@ -506,21 +456,15 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
Pry.commands = klass
|
||||
|
||||
Pry.new(:input => InputTester.new("hello"), :output => @str_output).rep
|
||||
@str_output.string.should =~ /hello world/
|
||||
|
||||
other_klass = Pry::CommandSet.new do
|
||||
command "goodbye", "" do
|
||||
output.puts "goodbye world"
|
||||
end
|
||||
end
|
||||
|
||||
@str_output = StringIO.new
|
||||
|
||||
Pry.new(:input => InputTester.new("goodbye"), :output => @str_output, :commands => other_klass).rep
|
||||
@str_output.string.should =~ /goodbye world/
|
||||
Pry.commands = klass
|
||||
pry_tester.eval("hello").should == "hello world\n"
|
||||
pry_tester(:commands => other_klass).eval("goodbye").should == "goodbye world\n"
|
||||
end
|
||||
|
||||
it 'should inherit commands from Pry::Commands' do
|
||||
|
@ -557,10 +501,10 @@ describe "commands" do
|
|||
child_klass = Pry::CommandSet.new klass do
|
||||
end
|
||||
|
||||
Pry.new(:print => proc {}, :input => InputTester.new("v"),
|
||||
:output => @str_output, :commands => child_klass).rep("john")
|
||||
mock_pry(Pry.binding_for('john'), "v", :print => proc {}, :commands => child_klass,
|
||||
:output => @str_output)
|
||||
|
||||
@str_output.string.rstrip.should == "john"
|
||||
@str_output.string.should == "john\n"
|
||||
end
|
||||
|
||||
it 'should import commands from another command object' do
|
||||
|
@ -601,44 +545,18 @@ describe "commands" do
|
|||
end
|
||||
end
|
||||
|
||||
# suppress evaluation output
|
||||
Pry.print = proc {}
|
||||
|
||||
Pry.new(:input => InputTester.new("jump-to"), :output => @str_output, :commands => klass).rep
|
||||
@str_output.string.rstrip.should == "jump-to the music"
|
||||
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:input => InputTester.new("help"), :output => @str_output, :commands => klass).rep
|
||||
@str_output.string.should == "help to the music\n"
|
||||
|
||||
|
||||
Pry.reset_defaults
|
||||
Pry.color = false
|
||||
t = pry_tester(:commands => klass)
|
||||
t.eval('jump-to').should == "jump-to the music\n"
|
||||
t.eval('help').should == "help to the music\n"
|
||||
end
|
||||
|
||||
it 'should run a command with no parameter' do
|
||||
pry_tester = Pry.new
|
||||
pry_tester.commands = @command_tester
|
||||
pry_tester.input = InputTester.new("command1", "exit-all")
|
||||
pry_tester.commands = @command_tester
|
||||
|
||||
pry_tester.output = @str_output
|
||||
|
||||
pry_tester.rep
|
||||
|
||||
@str_output.string.should =~ /command1/
|
||||
pry_tester(:commands => @command_tester).eval('command1').
|
||||
should == "command1\n"
|
||||
end
|
||||
|
||||
it 'should run a command with one parameter' do
|
||||
pry_tester = Pry.new
|
||||
pry_tester.commands = @command_tester
|
||||
pry_tester.input = InputTester.new("command2 horsey", "exit-all")
|
||||
pry_tester.commands = @command_tester
|
||||
|
||||
pry_tester.output = @str_output
|
||||
|
||||
pry_tester.rep
|
||||
|
||||
@str_output.string.should =~ /horsey/
|
||||
pry_tester(:commands => @command_tester).eval('command2 horsey').
|
||||
should == "horsey\n"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,29 +6,29 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should amend the last line of input when no line number specified' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line puts :blah', eval_str
|
||||
@t.process_command 'amend-line puts :blah'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :blah
|
||||
STR
|
||||
end
|
||||
|
||||
it 'should amend the specified line of input when line number given' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 1 def goodbye', eval_str
|
||||
@t.process_command 'amend-line 1 def goodbye'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def goodbye
|
||||
puts :bing
|
||||
puts :bang
|
||||
|
@ -36,15 +36,15 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should amend the first line of input when 0 given as line number' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 0 def goodbye', eval_str
|
||||
@t.process_command 'amend-line 0 def goodbye'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def goodbye
|
||||
puts :bing
|
||||
puts :bang
|
||||
|
@ -52,23 +52,23 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should amend a specified line when negative number given' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line -1 puts :bink', eval_str
|
||||
@t.process_command 'amend-line -1 puts :bink'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bink
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line -2 puts :bink', eval_str
|
||||
@t.process_command 'amend-line -2 puts :bink'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :bink
|
||||
puts :bink
|
||||
|
@ -76,16 +76,16 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should amend a range of lines of input when negative numbers given' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
puts :boat
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line -3..-2 puts :bink', eval_str
|
||||
@t.process_command 'amend-line -3..-2 puts :bink'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :bink
|
||||
puts :boat
|
||||
|
@ -93,15 +93,15 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should correctly amend the specified line with interpolated text' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line puts "#{goodbye}"', eval_str
|
||||
@t.process_command 'amend-line puts "#{goodbye}"'
|
||||
|
||||
eval_str.should == unindent(<<-'STR')
|
||||
@t.eval_string.should == unindent(<<-'STR')
|
||||
def hello
|
||||
puts :bing
|
||||
puts "#{goodbye}"
|
||||
|
@ -122,16 +122,16 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should correctly amend the specified range of lines' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
puts :heart
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 2..3 puts :bong', eval_str
|
||||
@t.process_command 'amend-line 2..3 puts :bong'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :bong
|
||||
puts :heart
|
||||
|
@ -139,7 +139,7 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should correctly delete a specific line using the ! for content' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
|
@ -147,9 +147,9 @@ describe "amend-line" do
|
|||
puts :heart
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 3 !', eval_str
|
||||
@t.process_command 'amend-line 3 !'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :bing
|
||||
puts :boast
|
||||
|
@ -158,7 +158,7 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should correctly delete a range of lines using the ! for content' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
|
@ -166,16 +166,16 @@ describe "amend-line" do
|
|||
puts :heart
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 2..4 !', eval_str
|
||||
@t.process_command 'amend-line 2..4 !'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :heart
|
||||
STR
|
||||
end
|
||||
|
||||
it 'should correctly delete the previous line using the ! for content' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
|
@ -183,9 +183,9 @@ describe "amend-line" do
|
|||
puts :heart
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line !', eval_str
|
||||
@t.process_command 'amend-line !'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
|
@ -194,7 +194,7 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should amend the specified range of lines, with numbers < 0 in range' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
|
@ -202,9 +202,9 @@ describe "amend-line" do
|
|||
puts :heart
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 2..-2 puts :bong', eval_str
|
||||
@t.process_command 'amend-line 2..-2 puts :bong'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :bong
|
||||
puts :heart
|
||||
|
@ -212,15 +212,15 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should correctly insert a line before a specified line using >' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 2 > puts :inserted', eval_str
|
||||
@t.process_command 'amend-line 2 > puts :inserted'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :inserted
|
||||
puts :bing
|
||||
|
@ -229,15 +229,15 @@ describe "amend-line" do
|
|||
end
|
||||
|
||||
it 'should ignore second value of range with > syntax' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
puts :bang
|
||||
STR
|
||||
|
||||
@t.process_command 'amend-line 2..21 > puts :inserted', eval_str
|
||||
@t.process_command 'amend-line 2..21 > puts :inserted'
|
||||
|
||||
eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def hello
|
||||
puts :inserted
|
||||
puts :bing
|
||||
|
|
|
@ -6,14 +6,13 @@ describe "!" do
|
|||
end
|
||||
|
||||
it 'should correctly clear the input buffer ' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push unindent(<<-STR)
|
||||
def hello
|
||||
puts :bing
|
||||
STR
|
||||
|
||||
@t.process_command '!', eval_str
|
||||
@t.process_command '!'
|
||||
@t.last_output.should =~ /Input buffer cleared!/
|
||||
|
||||
eval_str.should == ''
|
||||
@t.eval_string.should == ''
|
||||
end
|
||||
end
|
||||
|
|
|
@ -283,8 +283,8 @@ describe "edit" do
|
|||
end
|
||||
|
||||
it "should edit the current expression if it's incomplete" do
|
||||
eval_str = 'def a'
|
||||
@t.process_command 'edit', eval_str
|
||||
@t.push 'def a'
|
||||
@t.process_command 'edit'
|
||||
@contents.should == "def a\n"
|
||||
end
|
||||
|
||||
|
@ -299,14 +299,14 @@ describe "edit" do
|
|||
end
|
||||
|
||||
it "should use a blank file if -t given, even during an expression" do
|
||||
eval_str = 'def a;'
|
||||
@t.process_command 'edit -t', eval_str
|
||||
@t.push 'def a;'
|
||||
@t.process_command 'edit -t'
|
||||
@contents.should == "\n"
|
||||
end
|
||||
|
||||
it "should position the cursor at the end of the expression" do
|
||||
eval_str = "def a; 2;\nend"
|
||||
@t.process_command 'edit', eval_str
|
||||
@t.eval "def a; 2;\nend"
|
||||
@t.process_command 'edit'
|
||||
@line.should == 2
|
||||
end
|
||||
|
||||
|
@ -315,9 +315,8 @@ describe "edit" do
|
|||
File.open(file, 'w'){|f| f << "'FOO'\n" }
|
||||
nil
|
||||
}
|
||||
eval_str = ''
|
||||
@t.process_command 'edit', eval_str
|
||||
eval_str.should == "'FOO'\n"
|
||||
@t.process_command 'edit'
|
||||
@t.eval_string.should == "'FOO'\n"
|
||||
end
|
||||
|
||||
it "should not evaluate the expression with -n" do
|
||||
|
@ -325,9 +324,8 @@ describe "edit" do
|
|||
File.open(file, 'w'){|f| f << "'FOO'\n" }
|
||||
nil
|
||||
}
|
||||
eval_str = ''
|
||||
@t.process_command 'edit -n', eval_str
|
||||
eval_str.should == ''
|
||||
@t.process_command 'edit -n'
|
||||
@t.eval_string.should == ''
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,34 +1,27 @@
|
|||
require 'helper'
|
||||
|
||||
describe "exit-all" do
|
||||
it 'should break out of the repl loop of Pry instance and return nil' do
|
||||
pry_tester(0).simulate_repl do |t|
|
||||
t.eval 'exit-all'
|
||||
end.should == nil
|
||||
before { @pry = Pry.new }
|
||||
|
||||
it "should break out of the repl and return nil" do
|
||||
@pry.eval("exit-all").should.be.false
|
||||
@pry.exit_value.should.be.nil
|
||||
end
|
||||
|
||||
it 'should break out of the repl loop of Pry instance wth a user specified value' do
|
||||
pry_tester(0).simulate_repl do |t|
|
||||
t.eval "exit-all 'message'"
|
||||
end.should == 'message'
|
||||
it "should break out of the repl wth a user specified value" do
|
||||
@pry.eval("exit-all 'message'").should.be.false
|
||||
@pry.exit_value.should == "message"
|
||||
end
|
||||
|
||||
it 'should break of the repl loop even if multiple bindings still on stack' do
|
||||
pry_tester(0).simulate_repl do |t|
|
||||
t.eval 'cd 1', 'cd 2', "exit-all 'message'"
|
||||
end.should == 'message'
|
||||
it "should break out of the repl even if multiple bindings still on stack" do
|
||||
["cd 1", "cd 2"].each { |line| @pry.eval(line).should.be.true }
|
||||
@pry.eval("exit-all 'message'").should.be.false
|
||||
@pry.exit_value.should == "message"
|
||||
end
|
||||
|
||||
it 'binding_stack should be empty after breaking out of the repl loop' do
|
||||
t = pry_tester(0) do
|
||||
def binding_stack
|
||||
@pry.binding_stack
|
||||
end
|
||||
end
|
||||
|
||||
t.simulate_repl do |t|
|
||||
t.eval 'cd 1', 'cd 2', 'exit-all'
|
||||
end
|
||||
t.binding_stack.empty?.should == true
|
||||
it "should have empty binding_stack after breaking out of the repl" do
|
||||
["cd 1", "cd 2"].each { |line| @pry.eval(line).should.be.true }
|
||||
@pry.eval("exit-all").should.be.false
|
||||
@pry.binding_stack.should.be.empty
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,34 +1,28 @@
|
|||
require 'helper'
|
||||
|
||||
describe "exit" do
|
||||
it 'should pop a binding with exit' do
|
||||
pry_tester(:outer).simulate_repl do |t|
|
||||
t.eval 'cd :inner'
|
||||
t.eval('self').should == :inner
|
||||
t.eval 'exit'
|
||||
t.eval('self').should == :outer
|
||||
t.eval 'exit-all'
|
||||
end
|
||||
before { @pry = Pry.new(:target => :outer, :output => StringIO.new) }
|
||||
|
||||
it "should pop a binding" do
|
||||
@pry.eval "cd :inner"
|
||||
@pry.evaluate_ruby("self").should == :inner
|
||||
@pry.eval "exit"
|
||||
@pry.evaluate_ruby("self").should == :outer
|
||||
end
|
||||
|
||||
it 'should break out of the repl loop of Pry instance when binding_stack has only one binding with exit' do
|
||||
pry_tester(0).simulate_repl do |t|
|
||||
t.eval 'exit'
|
||||
end.should == nil
|
||||
it "should break out of the repl when binding_stack has only one binding" do
|
||||
@pry.eval("exit").should.be.false
|
||||
@pry.exit_value.should.be.nil
|
||||
end
|
||||
|
||||
it 'should break out of the repl loop of Pry instance when binding_stack has only one binding with exit, and return user-given value' do
|
||||
pry_tester(0).simulate_repl do |t|
|
||||
t.eval 'exit :john'
|
||||
end.should == :john
|
||||
it "should break out of the repl and return user-given value" do
|
||||
@pry.eval("exit :john").should.be.false
|
||||
@pry.exit_value.should == :john
|
||||
end
|
||||
|
||||
it 'should break out the repl loop of Pry instance even after an exception in user-given value' do
|
||||
pry_tester(0).simulate_repl do |t|
|
||||
proc {
|
||||
t.eval 'exit = 42'
|
||||
}.should.raise(SyntaxError)
|
||||
t.eval 'exit'
|
||||
end.should == nil
|
||||
it "should break out of the repl even after an exception" do
|
||||
@pry.eval "exit = 42"
|
||||
@pry.output.string.should =~ /^SyntaxError/
|
||||
@pry.eval("exit").should.be.false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ describe "hist" do
|
|||
@hist = Pry.history
|
||||
|
||||
@str_output = StringIO.new
|
||||
@t = pry_tester do
|
||||
@t = pry_tester :history => @hist do
|
||||
# For looking at what hist pushes into the input stack. The
|
||||
# implementation of this helper will definitely have to change at some
|
||||
# point.
|
||||
|
@ -29,10 +29,10 @@ describe "hist" do
|
|||
@hist.push "@y = 20"
|
||||
@hist.push "@z = 30"
|
||||
|
||||
@t.context = o
|
||||
@t.push_binding o
|
||||
@t.eval 'hist --replay -1'
|
||||
|
||||
@t.next_input.should == "@z = 30\n"
|
||||
o.instance_variable_get(:@z).should == 30
|
||||
end
|
||||
|
||||
it 'should replay a range of history correctly (range of items)' do
|
||||
|
@ -40,10 +40,9 @@ describe "hist" do
|
|||
@hist.push "@x = 10"
|
||||
@hist.push "@y = 20"
|
||||
|
||||
@t.context = o
|
||||
@t.push_binding o
|
||||
@t.eval 'hist --replay 0..2'
|
||||
|
||||
@t.next_input.should == "@x = 10\n@y = 20\n"
|
||||
@t.eval('[@x, @y]').should == [10, 20]
|
||||
end
|
||||
|
||||
# this is to prevent a regression where input redirection is
|
||||
|
@ -52,12 +51,10 @@ describe "hist" do
|
|||
o = Object.new
|
||||
@hist.push "cd 1"
|
||||
@hist.push "cd 2"
|
||||
redirect_pry_io(InputTester.new("hist --replay 0..2", "Pad.stack = _pry_.binding_stack.dup", "exit-all")) do
|
||||
o.pry
|
||||
end
|
||||
o = Pad.stack[-2..-1].map { |v| v.eval('self') }
|
||||
o.should == [1, 2]
|
||||
Pad.clear
|
||||
|
||||
@t.eval("hist --replay 0..2")
|
||||
stack = @t.eval("Pad.stack = _pry_.binding_stack.dup")
|
||||
stack.map{ |b| b.eval("self") }.should == [TOPLEVEL_BINDING.eval("self"), 1, 2]
|
||||
end
|
||||
|
||||
it 'should grep for correct lines in history' do
|
||||
|
@ -139,43 +136,37 @@ describe "hist" do
|
|||
end
|
||||
|
||||
it "should store a call with `--replay` flag" do
|
||||
redirect_pry_io(InputTester.new(":banzai", "hist --replay 1",
|
||||
"hist", "exit-all"), @str_output) do
|
||||
Pry.start
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /hist --replay 1/
|
||||
@t.eval ":banzai"
|
||||
@t.eval "hist --replay 1"
|
||||
@t.eval("hist").should =~ /hist --replay 1/
|
||||
end
|
||||
|
||||
it "should not contain lines produced by `--replay` flag" do
|
||||
redirect_pry_io(InputTester.new(":banzai", ":geronimo", ":huzzah",
|
||||
"hist --replay 1..3", "hist",
|
||||
"exit-all"), @str_output) do
|
||||
Pry.start
|
||||
end
|
||||
@t.eval ":banzai"
|
||||
@t.eval ":geronimo"
|
||||
@t.eval ":huzzah"
|
||||
@t.eval("hist --replay 1..3")
|
||||
|
||||
@str_output.string.each_line.to_a.reject { |line| line.start_with?("=>") }.size.should == 4
|
||||
@str_output.string.each_line.to_a.last.should =~ /hist --replay 1\.\.3/
|
||||
@str_output.string.each_line.to_a[-2].should =~ /:huzzah/
|
||||
output = @t.eval("hist")
|
||||
output.should == "1: :banzai\n2: :geronimo\n3: :huzzah\n4: hist --replay 1..3\n"
|
||||
end
|
||||
|
||||
it "should raise CommandError when index of `--replay` points out to another `hist --replay`" do
|
||||
redirect_pry_io(InputTester.new(":banzai", "hist --replay 1",
|
||||
"hist --replay 2", "exit-all"), @str_output) do
|
||||
Pry.start
|
||||
end
|
||||
|
||||
@str_output.string.should =~ /Replay index 2 points out to another replay call: `hist --replay 1`/
|
||||
@t.eval ":banzai"
|
||||
@t.eval "hist --replay 1"
|
||||
lambda do
|
||||
@t.eval "hist --replay 2"
|
||||
end.should.raise(Pry::CommandError, /Replay index 4 points out to another replay call: `hist --replay 1`/)
|
||||
end
|
||||
|
||||
it "should disallow execution of `--replay <i>` when CommandError raised" do
|
||||
redirect_pry_io(InputTester.new("a = 0", "a += 1", "hist --replay 2",
|
||||
"hist --replay 3", "'a is ' + a.to_s",
|
||||
"hist", "exit-all"), @str_output) do
|
||||
Pry.start
|
||||
end
|
||||
|
||||
@str_output.string.each_line.to_a.reject { |line| line !~ /\A\d/ }.size.should == 5
|
||||
@str_output.string.should =~ /a is 2/
|
||||
@t.eval "a = 0"
|
||||
@t.eval "a += 1"
|
||||
@t.eval "hist --replay 2"
|
||||
lambda{
|
||||
@t.eval "hist --replay 3"
|
||||
}.should.raise(Pry::CommandError)
|
||||
@t.eval("a").should == 2
|
||||
@t.eval("hist").lines.to_a.size.should == 5
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,8 +6,8 @@ require 'helper'
|
|||
|
||||
describe "play" do
|
||||
before do
|
||||
@t = pry_tester
|
||||
@eval_str = ''
|
||||
@o = Object.new
|
||||
@t = pry_tester(@o)
|
||||
end
|
||||
|
||||
describe "with an argument" do
|
||||
|
@ -38,7 +38,6 @@ describe "play" do
|
|||
|
||||
describe "whatever" do
|
||||
before do
|
||||
@o = Object.new
|
||||
def @o.test_method
|
||||
:test_method_content
|
||||
end
|
||||
|
@ -51,9 +50,8 @@ describe "play" do
|
|||
:test_method_content
|
||||
end
|
||||
|
||||
pry_tester(@o).process_command 'play -d test_method', @eval_str
|
||||
|
||||
@eval_str.should == unindent(<<-STR)
|
||||
@t.process_command 'play -d test_method'
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
@v = 10
|
||||
@y = 20
|
||||
STR
|
||||
|
@ -68,9 +66,8 @@ describe "play" do
|
|||
:test_method_content
|
||||
end
|
||||
|
||||
pry_tester(@o).process_command 'play -d test_method --lines 2..3', @eval_str
|
||||
|
||||
@eval_str.should == unindent(<<-STR)
|
||||
@t.process_command 'play -d test_method --lines 2..3'
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
@v = 10
|
||||
@y = 20
|
||||
STR
|
||||
|
@ -81,8 +78,8 @@ describe "play" do
|
|||
end
|
||||
|
||||
it 'should play a method (a single line)' do
|
||||
pry_tester(@o).process_command 'play test_method --lines 2', @eval_str
|
||||
@eval_str.should == ":test_method_content\n"
|
||||
@t.process_command 'play test_method --lines 2'
|
||||
@t.eval_string.should == ":test_method_content\n"
|
||||
end
|
||||
|
||||
it 'should properly reindent lines' do
|
||||
|
@ -90,18 +87,18 @@ describe "play" do
|
|||
'hello world'
|
||||
end
|
||||
|
||||
pry_tester(@o).process_command 'play test_method --lines 2', @eval_str
|
||||
@eval_str.should == "'hello world'\n"
|
||||
@t.process_command 'play test_method --lines 2'
|
||||
@t.eval_string.should == "'hello world'\n"
|
||||
end
|
||||
|
||||
it 'should APPEND to the input buffer when playing a method line, not replace it' do
|
||||
@eval_str = unindent(<<-STR)
|
||||
@t.eval_string = unindent(<<-STR)
|
||||
def another_test_method
|
||||
STR
|
||||
|
||||
pry_tester(@o).process_command 'play test_method --lines 2', @eval_str
|
||||
@t.process_command 'play test_method --lines 2'
|
||||
|
||||
@eval_str.should == unindent(<<-STR)
|
||||
@t.eval_string.should == unindent(<<-STR)
|
||||
def another_test_method
|
||||
:test_method_content
|
||||
STR
|
||||
|
@ -115,9 +112,8 @@ describe "play" do
|
|||
@var3 = 40
|
||||
end
|
||||
|
||||
pry_tester(@o).process_command 'play test_method --lines 3..4', @eval_str
|
||||
|
||||
@eval_str.should == unindent(<<-STR, 0)
|
||||
@t.process_command 'play test_method --lines 3..4'
|
||||
@t.eval_string.should == unindent(<<-STR, 0)
|
||||
@var1 = 20
|
||||
@var2 = 30
|
||||
STR
|
||||
|
|
|
@ -13,13 +13,13 @@ describe "raise-up" do
|
|||
|
||||
it "should raise the exception with raise-up" do
|
||||
redirect_pry_io(InputTester.new("raise NoMethodError", "raise-up NoMethodError")) do
|
||||
lambda { Pry.new.repl(0) }.should.raise NoMethodError
|
||||
lambda { Object.new.pry }.should.raise NoMethodError
|
||||
end
|
||||
end
|
||||
|
||||
it "should raise an unamed exception with raise-up" do
|
||||
redirect_pry_io(InputTester.new("raise 'stop'","raise-up 'noreally'")) do
|
||||
lambda { Pry.new.repl(0) }.should.raise RuntimeError, "noreally"
|
||||
lambda { Object.new.pry }.should.raise RuntimeError, "noreally"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ describe "show-input" do
|
|||
end
|
||||
|
||||
it 'should correctly show the current lines in the input buffer' do
|
||||
eval_str = unindent(<<-STR)
|
||||
@t.push *unindent(<<-STR).split("\n")
|
||||
def hello
|
||||
puts :bing
|
||||
STR
|
||||
|
||||
@t.process_command 'show-input', eval_str
|
||||
@t.process_command 'show-input'
|
||||
@t.last_output.should =~ /\A\d+: def hello\n\d+: puts :bing/
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
require 'helper'
|
||||
|
||||
def new_completer(bind, pry=nil)
|
||||
Pry::InputCompleter.build_completion_proc(Pry.binding_for(bind), pry)
|
||||
end
|
||||
|
||||
def completer_test(bind, pry=nil, assert_flag=true)
|
||||
completer = new_completer(bind, pry)
|
||||
test = proc {|symbol| completer.call(symbol[0..-2]).include?(symbol).should == assert_flag}
|
||||
test = proc {|symbol|
|
||||
Pry::InputCompleter.call(symbol[0..-2], :target => Pry.binding_for(bind), :pry => pry).include?(symbol).should == assert_flag}
|
||||
return proc {|*symbols| symbols.each(&test) }
|
||||
end
|
||||
|
||||
|
@ -40,16 +36,12 @@ describe Pry::InputCompleter do
|
|||
# another jruby hack :((
|
||||
if !Pry::Helpers::BaseHelpers.jruby?
|
||||
it "should not crash if there's a Module that has a symbolic name." do
|
||||
completer = Pry::InputCompleter.build_completion_proc(Pry.binding_for(Object.new))
|
||||
lambda{ completer.call "a.to_s." }.should.not.raise Exception
|
||||
lambda{ Pry::InputCompleter.call "a.to_s.", :target => Pry.binding_for(Object.new) }.should.not.raise Exception
|
||||
end
|
||||
end
|
||||
|
||||
it 'should take parenthesis and other characters into account for symbols' do
|
||||
b = Pry.binding_for(Object.new)
|
||||
completer = Pry::InputCompleter.build_completion_proc(b)
|
||||
|
||||
lambda { completer.call(":class)") }.should.not.raise(RegexpError)
|
||||
lambda { Pry::InputCompleter.call(":class)", :target => Pry.binding_for(Object.new)) }.should.not.raise(RegexpError)
|
||||
end
|
||||
|
||||
it 'should complete instance variables' do
|
||||
|
@ -128,7 +120,7 @@ describe Pry::InputCompleter do
|
|||
completer_test(binding).call('o.foo')
|
||||
|
||||
# trailing slash
|
||||
new_completer(Mod).call('Mod2/').include?('Mod2/').should == true
|
||||
Pry::InputCompleter.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true
|
||||
end
|
||||
|
||||
it 'should complete for arbitrary scopes' do
|
||||
|
@ -142,10 +134,8 @@ describe Pry::InputCompleter do
|
|||
Con = :constant
|
||||
end
|
||||
|
||||
pry = Pry.new()
|
||||
stack = pry.binding_stack
|
||||
stack.push(Pry.binding_for(Baz))
|
||||
stack.push(Pry.binding_for(Bar))
|
||||
pry = Pry.new(:target => Baz)
|
||||
pry.push_binding(Bar)
|
||||
|
||||
b = Pry.binding_for(Bar)
|
||||
completer_test(b, pry).call("../@bazvar")
|
||||
|
@ -201,7 +191,7 @@ describe Pry::InputCompleter do
|
|||
completer_test(binding).call('o.foo')
|
||||
|
||||
# trailing slash
|
||||
new_completer(Mod).call('Mod2/').include?('Mod2/').should == true
|
||||
Pry::InputCompleter.call('Mod2/', :target => Pry.binding_for(Mod)).include?('Mod2/').should == true
|
||||
|
||||
end
|
||||
|
||||
|
@ -216,10 +206,8 @@ describe Pry::InputCompleter do
|
|||
Con = :constant
|
||||
end
|
||||
|
||||
pry = Pry.new()
|
||||
stack = pry.binding_stack
|
||||
stack.push(Pry.binding_for(Baz))
|
||||
stack.push(Pry.binding_for(Bar))
|
||||
pry = Pry.new(:target => Baz)
|
||||
pry.push_binding(Bar)
|
||||
|
||||
b = Pry.binding_for(Bar)
|
||||
completer_test(b, pry).call("../@bazvar")
|
||||
|
@ -228,6 +216,6 @@ describe Pry::InputCompleter do
|
|||
|
||||
it 'should not return nil in its output' do
|
||||
pry = Pry.new
|
||||
new_completer(binding, pry).call("pry.").should.not.include nil
|
||||
Pry::InputCompleter.call("pry.", :target => binding).should.not.include nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,38 +17,42 @@ describe Pry::DEFAULT_CONTROL_D_HANDLER do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
describe "at top-level session" do
|
||||
it "breaks out of a REPL" do
|
||||
pry_tester(0).simulate_repl do |t|
|
||||
t.eval @control_d
|
||||
end.should == nil
|
||||
describe 'at top-level session' do
|
||||
it 'should break out of a REPL loop' do
|
||||
instance = Pry.new
|
||||
instance.binding_stack.should.not.be.empty
|
||||
instance.eval(nil).should.be.false
|
||||
instance.binding_stack.should.be.empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a nested session" do
|
||||
it "pops last binding from the binding stack" do
|
||||
pry_tester(0).simulate_repl { |t|
|
||||
t.eval 'cd :foo'
|
||||
t.eval('_pry_.binding_stack.size').should == 2
|
||||
t.eval(@control_d)
|
||||
t.eval('_pry_.binding_stack.size').should == 1
|
||||
t.eval 'exit-all'
|
||||
}
|
||||
describe 'in a nested session' do
|
||||
it 'should pop last binding from the binding stack' do
|
||||
t = pry_tester
|
||||
t.eval "cd Object.new"
|
||||
t.eval("_pry_.binding_stack.size").should == 2
|
||||
t.eval("_pry_.eval(nil)").should.be.true
|
||||
t.eval("_pry_.binding_stack.size").should == 1
|
||||
end
|
||||
|
||||
it "breaks out of the parent session" do
|
||||
pry_tester(:outer).simulate_repl do |o|
|
||||
o.context = :inner
|
||||
o.simulate_repl { |i|
|
||||
i.eval('_pry_.current_context.eval("self")').should == :inner
|
||||
i.eval('_pry_.binding_stack.size').should == 2
|
||||
i.eval @control_d
|
||||
i.eval('_pry_.binding_stack.size').should == 1
|
||||
i.eval('_pry_.current_context.eval("self")').should == :outer
|
||||
i.eval 'throw :breakout'
|
||||
}
|
||||
o.eval 'exit-all'
|
||||
ReplTester.start do
|
||||
input 'Pry::REPL.new(_pry_, :target => 10).start'
|
||||
output ''
|
||||
prompt(/10.*> $/)
|
||||
|
||||
input 'self'
|
||||
output '=> 10'
|
||||
|
||||
input nil # Ctrl-D
|
||||
output ''
|
||||
|
||||
input 'self'
|
||||
output '=> main'
|
||||
|
||||
input nil # Ctrl-D
|
||||
output '=> nil' # Exit value of nested REPL.
|
||||
assert_exited
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ require 'mocha/api'
|
|||
require 'pry/test/helper'
|
||||
require 'helpers/bacon'
|
||||
require 'helpers/mock_pry'
|
||||
require 'helpers/repl_tester'
|
||||
|
||||
class Module
|
||||
public :remove_const
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
def mock_pry(*args)
|
||||
args.flatten!
|
||||
binding = args.first.is_a?(Binding) ? args.shift : binding()
|
||||
options = args.last.is_a?(Hash) ? args.pop : {}
|
||||
|
||||
input = InputTester.new(*args)
|
||||
output = StringIO.new
|
||||
|
||||
redirect_pry_io(input, output) do
|
||||
binding.pry
|
||||
binding.pry(options)
|
||||
end
|
||||
|
||||
output.string
|
||||
|
|
112
spec/helpers/repl_tester.rb
Normal file
112
spec/helpers/repl_tester.rb
Normal file
|
@ -0,0 +1,112 @@
|
|||
# This is for super-high-level integration testing.
|
||||
|
||||
require 'thread'
|
||||
require 'delegate'
|
||||
|
||||
class ReplTester
|
||||
class Input
|
||||
def initialize(tester_mailbox)
|
||||
@tester_mailbox = tester_mailbox
|
||||
end
|
||||
|
||||
def readline(prompt)
|
||||
@tester_mailbox.push prompt
|
||||
mailbox.pop
|
||||
end
|
||||
|
||||
def mailbox
|
||||
Thread.current[:mailbox]
|
||||
end
|
||||
end
|
||||
|
||||
class Output < SimpleDelegator
|
||||
def clear
|
||||
__setobj__(StringIO.new)
|
||||
end
|
||||
end
|
||||
|
||||
def self.start(options = {}, &block)
|
||||
Thread.current[:mailbox] = Queue.new
|
||||
instance = nil
|
||||
input = Input.new(Thread.current[:mailbox])
|
||||
output = Output.new(StringIO.new)
|
||||
|
||||
redirect_pry_io input, output do
|
||||
instance = new(options)
|
||||
instance.instance_eval(&block)
|
||||
instance.ensure_exit
|
||||
end
|
||||
ensure
|
||||
if instance && instance.thread && instance.thread.alive?
|
||||
instance.thread.kill
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :thread, :mailbox, :last_prompt
|
||||
|
||||
def initialize(options = {})
|
||||
@pry = Pry.new(options)
|
||||
@repl = Pry::REPL.new(@pry)
|
||||
@mailbox = Thread.current[:mailbox]
|
||||
|
||||
@thread = Thread.new do
|
||||
begin
|
||||
Thread.current[:mailbox] = Queue.new
|
||||
@repl.start
|
||||
ensure
|
||||
Thread.current[:session_ended] = true
|
||||
mailbox.push nil
|
||||
end
|
||||
end
|
||||
|
||||
wait # wait until the instance reaches its first readline
|
||||
end
|
||||
|
||||
# Accept a line of input, as if entered by a user.
|
||||
def input(input)
|
||||
reset_output
|
||||
repl_mailbox.push input
|
||||
wait
|
||||
Pry.output.string
|
||||
end
|
||||
|
||||
# Assert that the current prompt matches the given string or regex.
|
||||
def prompt(match)
|
||||
match.should === last_prompt
|
||||
end
|
||||
|
||||
# Assert that the most recent output (since the last time input was called)
|
||||
# matches the given string or regex.
|
||||
def output(match)
|
||||
match.should === Pry.output.string.chomp
|
||||
end
|
||||
|
||||
# Assert that the Pry session ended naturally after the last input.
|
||||
def assert_exited
|
||||
@should_exit_naturally = true
|
||||
end
|
||||
|
||||
# @private
|
||||
def ensure_exit
|
||||
if @should_exit_naturally
|
||||
@thread[:session_ended].should.be.true
|
||||
else
|
||||
input "exit-all"
|
||||
raise "REPL didn't die" unless @thread[:session_ended]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reset_output
|
||||
Pry.output.clear
|
||||
end
|
||||
|
||||
def repl_mailbox
|
||||
@thread[:mailbox]
|
||||
end
|
||||
|
||||
def wait
|
||||
@last_prompt = mailbox.pop
|
||||
end
|
||||
end
|
|
@ -1,90 +0,0 @@
|
|||
# coding: utf-8
|
||||
require 'helper'
|
||||
|
||||
describe "Pry#input_stack" do
|
||||
before do
|
||||
@str_output = StringIO.new
|
||||
end
|
||||
|
||||
it 'should accept :input_stack as a config option' do
|
||||
stack = [StringIO.new("test")]
|
||||
Pry.new(:input_stack => stack).input_stack.should == stack
|
||||
end
|
||||
|
||||
it 'should use defaults from Pry.config' do
|
||||
Pry.config.input_stack = [StringIO.new("exit")]
|
||||
Pry.new.input_stack.should == Pry.config.input_stack
|
||||
Pry.config.input_stack = []
|
||||
end
|
||||
|
||||
it 'should read from all input objects on stack and exit session (usingn repl)' do
|
||||
stack = [b = StringIO.new(":cloister\nexit\n"), c = StringIO.new(":baron\n")]
|
||||
instance = Pry.new(:input => StringIO.new(":alex\n"),
|
||||
:output => @str_output,
|
||||
:input_stack => stack)
|
||||
|
||||
instance.repl
|
||||
@str_output.string.should =~ /:alex/
|
||||
@str_output.string.should =~ /:baron/
|
||||
@str_output.string.should =~ /:cloister/
|
||||
end
|
||||
|
||||
it 'input objects should be popped off stack as they are used up' do
|
||||
stack = [StringIO.new(":cloister\n"), StringIO.new(":baron\n")]
|
||||
instance = Pry.new(:input => StringIO.new(":alex\n"),
|
||||
:output => @str_output,
|
||||
:input_stack => stack)
|
||||
|
||||
stack.size.should == 2
|
||||
|
||||
instance.rep
|
||||
@str_output.string.should =~ /:alex/
|
||||
instance.rep
|
||||
@str_output.string.should =~ /:baron/
|
||||
stack.size.should == 1
|
||||
instance.rep
|
||||
@str_output.string.should =~ /:cloister/
|
||||
stack.size.should == 0
|
||||
end
|
||||
|
||||
it 'should revert to Pry.config.input when it runs out of input objects in input_stack' do
|
||||
redirect_pry_io(StringIO.new(":rimbaud\nexit\n"), StringIO.new) do
|
||||
stack = [StringIO.new(":cloister\n"), StringIO.new(":baron\n")]
|
||||
instance = Pry.new(:input => StringIO.new(":alex\n"),
|
||||
:output => @str_output,
|
||||
:input_stack => stack)
|
||||
|
||||
instance.repl
|
||||
@str_output.string.should =~ /:alex/
|
||||
@str_output.string.should =~ /:baron/
|
||||
@str_output.string.should =~ /:cloister/
|
||||
@str_output.string.should =~ /:rimbaud/
|
||||
end
|
||||
end
|
||||
|
||||
it 'should display error and throw(:breakout) if at end of input after using up input_stack objects' do
|
||||
catch(:breakout) do
|
||||
redirect_pry_io(StringIO.new(":rimbaud\n"), @str_output) do
|
||||
Pry.new(:input_stack => [StringIO.new(":a\n"), StringIO.new(":b\n")]).repl
|
||||
end
|
||||
end
|
||||
@str_output.string.should =~ /Error: Pry ran out of things to read/
|
||||
end
|
||||
|
||||
if "".respond_to?(:encoding)
|
||||
after do
|
||||
Pry.line_buffer = [""]
|
||||
Pry.current_line = 1
|
||||
end
|
||||
it "should pass strings to Pry in the right encoding" do
|
||||
input1 = "'f。。'.encoding.name" # utf-8, see coding declaration
|
||||
input2 = input1.encode('Shift_JIS')
|
||||
|
||||
mock_pry(input1, input2).should == %{=> "UTF-8"\n=> "Shift_JIS"\n\n}
|
||||
end
|
||||
|
||||
it "should be able to use unicode regexes on a UTF-8 terminal" do
|
||||
mock_pry('":-Þ" =~ /þ/i').should == %{=> 2\n\n}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -23,6 +23,7 @@ describe "Prompts" do
|
|||
redirect_pry_io(InputTester.new("def hello", "exit-all")) do
|
||||
Pry.start(self, :prompt => proc { |v| config = v })
|
||||
end
|
||||
|
||||
config.eval_string.should =~ /def hello/
|
||||
config.nesting_level.should == 0
|
||||
config.expr_number.should == 1
|
||||
|
|
|
@ -20,12 +20,12 @@ describe "test Pry defaults" do
|
|||
|
||||
it 'should set the input default, and the default should be overridable' do
|
||||
Pry.input = InputTester.new("5")
|
||||
|
||||
Pry.output = @str_output
|
||||
Pry.new.rep
|
||||
Object.new.pry
|
||||
@str_output.string.should =~ /5/
|
||||
|
||||
Pry.new(:input => InputTester.new("6")).rep
|
||||
Pry.output = @str_output
|
||||
Object.new.pry :input => InputTester.new("6")
|
||||
@str_output.string.should =~ /6/
|
||||
end
|
||||
|
||||
|
@ -75,18 +75,19 @@ describe "test Pry defaults" do
|
|||
end
|
||||
|
||||
it 'should set the output default, and the default should be overridable' do
|
||||
Pry.input = InputTester.new("5", "6", "7")
|
||||
|
||||
Pry.output = @str_output
|
||||
|
||||
Pry.new.rep
|
||||
Pry.input = InputTester.new("5")
|
||||
Object.new.pry
|
||||
@str_output.string.should =~ /5/
|
||||
|
||||
Pry.new.rep
|
||||
Pry.input = InputTester.new("6")
|
||||
Object.new.pry
|
||||
@str_output.string.should =~ /5\n.*6/
|
||||
|
||||
Pry.input = InputTester.new("7")
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:output => @str_output).rep
|
||||
Object.new.pry :output => @str_output
|
||||
@str_output.string.should.not =~ /5\n.*6/
|
||||
@str_output.string.should =~ /7/
|
||||
end
|
||||
|
@ -96,17 +97,17 @@ describe "test Pry defaults" do
|
|||
Pry.print = new_print
|
||||
|
||||
Pry.new.print.should == Pry.print
|
||||
Pry.new(:input => InputTester.new("\"test\""), :output => @str_output).rep
|
||||
Object.new.pry :input => InputTester.new("\"test\""), :output => @str_output
|
||||
@str_output.string.should == "=> LOL\n"
|
||||
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:input => InputTester.new("\"test\""), :output => @str_output,
|
||||
:print => proc { |out, value| out.puts value.reverse }).rep
|
||||
Object.new.pry :input => InputTester.new("\"test\""), :output => @str_output,
|
||||
:print => proc { |out, value| out.puts value.reverse }
|
||||
@str_output.string.should == "tset\n"
|
||||
|
||||
Pry.new.print.should == Pry.print
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:input => InputTester.new("\"test\""), :output => @str_output).rep
|
||||
Object.new.pry :input => InputTester.new("\"test\""), :output => @str_output
|
||||
@str_output.string.should == "=> LOL\n"
|
||||
end
|
||||
|
||||
|
@ -134,47 +135,51 @@ describe "test Pry defaults" do
|
|||
|
||||
describe "prompts" do
|
||||
before do
|
||||
@empty_input_buffer = ""
|
||||
@non_empty_input_buffer = "def hello"
|
||||
@context = Pry.binding_for(0)
|
||||
Pry.output = StringIO.new
|
||||
end
|
||||
|
||||
def get_prompts(pry)
|
||||
a = pry.select_prompt
|
||||
pry.eval "["
|
||||
b = pry.select_prompt
|
||||
pry.eval "]"
|
||||
[a, b]
|
||||
end
|
||||
|
||||
it 'should set the prompt default, and the default should be overridable (single prompt)' do
|
||||
new_prompt = proc { "test prompt> " }
|
||||
Pry.prompt = new_prompt
|
||||
|
||||
Pry.new.prompt.should == Pry.prompt
|
||||
Pry.new.select_prompt(@empty_input_buffer, @context).should == "test prompt> "
|
||||
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt> "
|
||||
|
||||
Pry.prompt = proc { "test prompt> " }
|
||||
new_prompt = proc { "A" }
|
||||
pry_tester = Pry.new(:prompt => new_prompt)
|
||||
pry_tester.prompt.should == new_prompt
|
||||
pry_tester.select_prompt(@empty_input_buffer, @context).should == "A"
|
||||
pry_tester.select_prompt(@non_empty_input_buffer, @context).should == "A"
|
||||
|
||||
Pry.new.prompt.should == Pry.prompt
|
||||
Pry.new.select_prompt(@empty_input_buffer, @context).should == "test prompt> "
|
||||
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt> "
|
||||
pry = Pry.new
|
||||
pry.prompt.should == Pry.prompt
|
||||
get_prompts(pry).should == ["test prompt> ", "test prompt> "]
|
||||
|
||||
|
||||
pry = Pry.new(:prompt => new_prompt)
|
||||
pry.prompt.should == new_prompt
|
||||
get_prompts(pry).should == ["A", "A"]
|
||||
|
||||
pry = Pry.new
|
||||
pry.prompt.should == Pry.prompt
|
||||
get_prompts(pry).should == ["test prompt> ", "test prompt> "]
|
||||
end
|
||||
|
||||
it 'should set the prompt default, and the default should be overridable (multi prompt)' do
|
||||
new_prompt = [proc { "test prompt> " }, proc { "test prompt* " }]
|
||||
Pry.prompt = new_prompt
|
||||
|
||||
Pry.new.prompt.should == Pry.prompt
|
||||
Pry.new.select_prompt(@empty_input_buffer, @context).should == "test prompt> "
|
||||
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt* "
|
||||
|
||||
Pry.prompt = [proc { "test prompt> " }, proc { "test prompt* " }]
|
||||
new_prompt = [proc { "A" }, proc { "B" }]
|
||||
pry_tester = Pry.new(:prompt => new_prompt)
|
||||
pry_tester.prompt.should == new_prompt
|
||||
pry_tester.select_prompt(@empty_input_buffer, @context).should == "A"
|
||||
pry_tester.select_prompt(@non_empty_input_buffer, @context).should == "B"
|
||||
|
||||
Pry.new.prompt.should == Pry.prompt
|
||||
Pry.new.select_prompt(@empty_input_buffer, @context).should == "test prompt> "
|
||||
Pry.new.select_prompt(@non_empty_input_buffer, @context).should == "test prompt* "
|
||||
pry = Pry.new
|
||||
pry.prompt.should == Pry.prompt
|
||||
get_prompts(pry).should == ["test prompt> ", "test prompt* "]
|
||||
|
||||
|
||||
pry = Pry.new(:prompt => new_prompt)
|
||||
pry.prompt.should == new_prompt
|
||||
get_prompts(pry).should == ["A", "B"]
|
||||
|
||||
pry = Pry.new
|
||||
pry.prompt.should == Pry.prompt
|
||||
get_prompts(pry).should == ["test prompt> ", "test prompt* "]
|
||||
end
|
||||
|
||||
describe 'storing and restoring the prompt' do
|
||||
|
@ -198,13 +203,12 @@ describe "test Pry defaults" do
|
|||
end
|
||||
|
||||
it 'should restore overridden prompts when returning from file-mode' do
|
||||
pry = Pry.new :input => InputTester.new('shell-mode', 'shell-mode'),
|
||||
:prompt => [ proc { 'P>' } ] * 2
|
||||
pry.select_prompt(@empty_input_buffer, @context).should == "P>"
|
||||
pry.re
|
||||
pry.select_prompt(@empty_input_buffer, @context).should =~ /\Apry .* \$ \z/
|
||||
pry.re
|
||||
pry.select_prompt(@empty_input_buffer, @context).should == "P>"
|
||||
pry = Pry.new(:prompt => [ proc { 'P>' } ] * 2)
|
||||
pry.select_prompt.should == "P>"
|
||||
pry.process_command('shell-mode')
|
||||
pry.select_prompt.should =~ /\Apry .* \$ \z/
|
||||
pry.process_command('shell-mode')
|
||||
pry.select_prompt.should == "P>"
|
||||
end
|
||||
|
||||
it '#pop_prompt should return the popped prompt' do
|
||||
|
@ -378,18 +382,17 @@ describe "test Pry defaults" do
|
|||
add_hook(:before_session, :my_name) { |out,_,_| out.puts "HELLO" }.
|
||||
add_hook(:after_session, :my_name) { |out,_,_| out.puts "BYE" }
|
||||
|
||||
Pry.new(:output => @str_output).repl
|
||||
Object.new.pry :output => @str_output
|
||||
@str_output.string.should =~ /HELLO/
|
||||
@str_output.string.should =~ /BYE/
|
||||
|
||||
Pry.input.rewind
|
||||
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:output => @str_output,
|
||||
:hooks => Pry::Hooks.new.
|
||||
add_hook( :before_session, :my_name) { |out,_,_| out.puts "MORNING" }.
|
||||
add_hook(:after_session, :my_name) { |out,_,_| out.puts "EVENING" }
|
||||
).repl
|
||||
Object.new.pry :output => @str_output,
|
||||
:hooks => Pry::Hooks.new.
|
||||
add_hook( :before_session, :my_name) { |out,_,_| out.puts "MORNING" }.
|
||||
add_hook(:after_session, :my_name) { |out,_,_| out.puts "EVENING" }
|
||||
|
||||
@str_output.string.should =~ /MORNING/
|
||||
@str_output.string.should =~ /EVENING/
|
||||
|
@ -397,19 +400,17 @@ describe "test Pry defaults" do
|
|||
# try below with just defining one hook
|
||||
Pry.input.rewind
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:output => @str_output,
|
||||
:hooks => Pry::Hooks.new.
|
||||
add_hook(:before_session, :my_name) { |out,_,_| out.puts "OPEN" }
|
||||
).repl
|
||||
Object.new.pry :output => @str_output,
|
||||
:hooks => Pry::Hooks.new.
|
||||
add_hook(:before_session, :my_name) { |out,_,_| out.puts "OPEN" }
|
||||
|
||||
@str_output.string.should =~ /OPEN/
|
||||
|
||||
Pry.input.rewind
|
||||
@str_output = StringIO.new
|
||||
Pry.new(:output => @str_output,
|
||||
:hooks => Pry::Hooks.new.
|
||||
add_hook(:after_session, :my_name) { |out,_,_| out.puts "CLOSE" }
|
||||
).repl
|
||||
Object.new.pry :output => @str_output,
|
||||
:hooks => Pry::Hooks.new.
|
||||
add_hook(:after_session, :my_name) { |out,_,_| out.puts "CLOSE" }
|
||||
|
||||
@str_output.string.should =~ /CLOSE/
|
||||
|
||||
|
|
|
@ -11,10 +11,6 @@ describe Pry do
|
|||
@saved_history.lines.each { |l| blk.call(l) }
|
||||
end
|
||||
|
||||
Pry.history.saver = proc do |lines|
|
||||
@saved_history << lines.map { |l| "#{l}\n" }.join
|
||||
end
|
||||
|
||||
Pry.load_history
|
||||
end
|
||||
|
||||
|
@ -44,56 +40,30 @@ describe Pry do
|
|||
end
|
||||
end
|
||||
|
||||
describe ".save_history" do
|
||||
it "should include a trailing newline" do
|
||||
Pry.history << "4"
|
||||
Pry.save_history
|
||||
@saved_history.should =~ /4\n\z/
|
||||
describe "saving to a file" do
|
||||
before do
|
||||
@histfile = Tempfile.new(["pryhistory", "txt"])
|
||||
@history = Pry::History.new(:file_path => @histfile.path)
|
||||
Pry.config.history.should_save = true
|
||||
@history.pusher = proc{ }
|
||||
end
|
||||
|
||||
it "should not change anything if history is not changed" do
|
||||
@saved_history = "4\n5\n6\n"
|
||||
Pry.save_history
|
||||
@saved_history.should == "4\n5\n6\n"
|
||||
after do
|
||||
@histfile.close(true)
|
||||
Pry.config.history.should_save = false
|
||||
end
|
||||
|
||||
it "should append new lines to the file" do
|
||||
Pry.history << "4"
|
||||
Pry.save_history
|
||||
@saved_history.should == "1\n2\n3\n4\n"
|
||||
it "should save lines to a file as they are written" do
|
||||
@history.push "5"
|
||||
File.read(@histfile.path).should == "5\n"
|
||||
end
|
||||
|
||||
it "should not clobber lines written by other Pry's in the meantime" do
|
||||
Pry.history << "5"
|
||||
@saved_history << "4\n"
|
||||
Pry.save_history
|
||||
it "should interleave lines from many places" do
|
||||
@history.push "5"
|
||||
File.open(@histfile.path, 'a'){ |f| f.puts "6" }
|
||||
@history.push "7"
|
||||
|
||||
Pry.history.to_a[-3..-1].should == ["2", "3", "5"]
|
||||
@saved_history.should == "1\n2\n3\n4\n5\n"
|
||||
end
|
||||
|
||||
it "should not delete lines from the file if this session's history was cleared" do
|
||||
Pry.history.clear
|
||||
Pry.save_history
|
||||
@saved_history.should == "1\n2\n3\n"
|
||||
end
|
||||
|
||||
it "should save new lines that are added after the history was cleared" do
|
||||
Pry.history.clear
|
||||
Pry.history << "4"
|
||||
Pry.save_history
|
||||
@saved_history.should =~ /1\n2\n3\n4\n/
|
||||
end
|
||||
|
||||
it "should only append new lines the second time it is saved" do
|
||||
Pry.history << "4"
|
||||
Pry.save_history
|
||||
@saved_history << "5\n"
|
||||
Pry.history << "6"
|
||||
Pry.save_history
|
||||
|
||||
Pry.history.to_a[-4..-1].should == ["2", "3", "4", "6"]
|
||||
@saved_history.should == "1\n2\n3\n4\n5\n6\n"
|
||||
File.read(@histfile.path).should == "5\n6\n7\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,19 +77,19 @@ describe Pry do
|
|||
@t = pry_tester
|
||||
end
|
||||
it "should normally output the result" do
|
||||
mock_pry("1 + 2").should == "=> 3\n\n"
|
||||
mock_pry("1 + 2").should == "=> 3\n"
|
||||
end
|
||||
|
||||
it "should not output anything if the input ends with a semicolon" do
|
||||
mock_pry("1 + 2;").should == "\n"
|
||||
mock_pry("1 + 2;").should == ""
|
||||
end
|
||||
|
||||
it "should output something if the input ends with a comment" do
|
||||
mock_pry("1 + 2 # basic addition").should == "=> 3\n\n"
|
||||
mock_pry("1 + 2 # basic addition").should == "=> 3\n"
|
||||
end
|
||||
|
||||
it "should not output something if the input is only a comment" do
|
||||
mock_pry("# basic addition").should == "\n"
|
||||
mock_pry("# basic addition").should == ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
86
spec/pry_repl_spec.rb
Normal file
86
spec/pry_repl_spec.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
require 'helper'
|
||||
|
||||
describe "The whole thing" do
|
||||
it "should let you run commands in the middle of multiline expressions" do
|
||||
ReplTester.start do
|
||||
input 'def a'
|
||||
input '!'
|
||||
output /^Input buffer cleared/
|
||||
|
||||
input '5'
|
||||
output '=> 5'
|
||||
end
|
||||
end
|
||||
|
||||
describe "eval_string and binding_stack" do
|
||||
it "shouldn't break if we start a nested REPL" do
|
||||
ReplTester.start do
|
||||
input 'Pry::REPL.new(_pry_, :target => 10).start'
|
||||
output ''
|
||||
prompt /10.*> $/
|
||||
|
||||
input 'self'
|
||||
output '=> 10'
|
||||
|
||||
input nil # Ctrl-D
|
||||
output ''
|
||||
|
||||
input 'self'
|
||||
output '=> main'
|
||||
end
|
||||
end
|
||||
|
||||
it "shouldn't break if we start a nested instance" do
|
||||
ReplTester.start do
|
||||
input 'Pry.start(10)'
|
||||
output ''
|
||||
prompt /10.*> $/
|
||||
|
||||
input 'self'
|
||||
output '=> 10'
|
||||
|
||||
input nil # Ctrl-D
|
||||
output '=> nil' # return value of Pry session
|
||||
|
||||
input 'self'
|
||||
output '=> main'
|
||||
end
|
||||
end
|
||||
|
||||
it "shouldn't break if we pop bindings in Ruby" do
|
||||
ReplTester.start do
|
||||
input 'cd 10'
|
||||
output ''
|
||||
prompt /10.*> $/
|
||||
|
||||
input '_pry_.binding_stack.pop'
|
||||
output /^=> #<Binding/
|
||||
prompt /main.*> $/
|
||||
|
||||
input '_pry_.binding_stack.pop'
|
||||
output /^=> #<Binding/
|
||||
assert_exited
|
||||
end
|
||||
end
|
||||
|
||||
it "should immediately evaluate eval_string after cmd if complete" do
|
||||
set = Pry::CommandSet.new do
|
||||
import Pry::Commands
|
||||
|
||||
command 'hello!' do
|
||||
eval_string.replace('"hello"')
|
||||
end
|
||||
end
|
||||
|
||||
ReplTester.start(:commands => set) do
|
||||
input 'def x'
|
||||
output ''
|
||||
prompt /\* $/
|
||||
|
||||
input 'hello!'
|
||||
output '=> "hello"'
|
||||
prompt /> $/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
171
spec/pry_spec.rb
171
spec/pry_spec.rb
|
@ -10,11 +10,9 @@ describe Pry do
|
|||
# regression test for exotic object support
|
||||
it "Should not error when return value is a BasicObject instance" do
|
||||
|
||||
lambda do
|
||||
redirect_pry_io(InputTester.new("BasicObject.new", "exit-all"), StringIO.new) do
|
||||
Pry.start
|
||||
end
|
||||
end.should.not.raise NoMethodError
|
||||
ReplTester.start do
|
||||
input('BasicObject.new').should =~ /^=> #<BasicObject:/
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
@ -81,111 +79,66 @@ describe Pry do
|
|||
|
||||
# bug fix for https://github.com/banister/pry/issues/93
|
||||
it 'should not leak pry constants into Object namespace' do
|
||||
input_string = "Command"
|
||||
o = Object.new
|
||||
pry_tester = Pry.new(:input => StringIO.new(input_string),
|
||||
:output => @str_output,
|
||||
:exception_handler => proc { |_, exception, _pry_| @excep = exception },
|
||||
:print => proc {}
|
||||
).rep(o)
|
||||
|
||||
@excep.is_a?(NameError).should == true
|
||||
lambda{
|
||||
pry_eval(Object.new, "Command")
|
||||
}.should.raise(NameError)
|
||||
end
|
||||
|
||||
if defined?(BasicObject)
|
||||
it 'should be able to operate inside the BasicObject class' do
|
||||
redirect_pry_io(InputTester.new(":foo", "Pad.obj = _", "exit-all")) do
|
||||
BasicObject.pry
|
||||
end
|
||||
|
||||
pry_eval(BasicObject, ":foo", "Pad.obj = _")
|
||||
Pad.obj.should == :foo
|
||||
end
|
||||
end
|
||||
|
||||
it 'should set an ivar on an object' do
|
||||
input_string = "@x = 10"
|
||||
input = InputTester.new(input_string)
|
||||
o = Object.new
|
||||
|
||||
pry_tester = Pry.new(:input => input, :output => StringIO.new)
|
||||
pry_tester.rep(o)
|
||||
pry_eval(o, "@x = 10")
|
||||
o.instance_variable_get(:@x).should == 10
|
||||
end
|
||||
|
||||
it 'should display error if Pry instance runs out of input' do
|
||||
redirect_pry_io(StringIO.new, @str_output) do
|
||||
Pry.new.repl
|
||||
Pry.start
|
||||
end
|
||||
@str_output.string.should =~ /Error: Pry ran out of things to read/
|
||||
end
|
||||
|
||||
it 'should make self evaluate to the receiver of the rep session' do
|
||||
o = :john
|
||||
|
||||
pry_tester = Pry.new(:input => InputTester.new("self"), :output => @str_output)
|
||||
pry_tester.rep(o)
|
||||
@str_output.string.should =~ /:john/
|
||||
pry_eval(o, "self").should == o
|
||||
end
|
||||
|
||||
it 'should work with multi-line input' do
|
||||
o = Object.new
|
||||
|
||||
pry_tester = Pry.new(:input => InputTester.new("x = ", "1 + 4"), :output => @str_output)
|
||||
pry_tester.rep(o)
|
||||
@str_output.string.should =~ /5/
|
||||
mock_pry("x = ", "1 + 4").should =~ /5/
|
||||
end
|
||||
|
||||
it 'should define a nested class under Hello and not on top-level or Pry' do
|
||||
pry_tester = Pry.new(:input => InputTester.new("class Nested", "end"), :output => StringIO.new)
|
||||
pry_tester.rep(Hello)
|
||||
mock_pry(Pry.binding_for(Hello), "class Nested", "end")
|
||||
Hello.const_defined?(:Nested).should == true
|
||||
end
|
||||
|
||||
it 'should suppress output if input ends in a ";" and is an Exception object (single line)' do
|
||||
o = Object.new
|
||||
|
||||
pry_tester = Pry.new(:input => InputTester.new("Exception.new;"), :output => @str_output)
|
||||
pry_tester.rep(o)
|
||||
@str_output.string.should == ""
|
||||
mock_pry("Exception.new;").should == ""
|
||||
end
|
||||
|
||||
it 'should suppress output if input ends in a ";" (single line)' do
|
||||
o = Object.new
|
||||
|
||||
pry_tester = Pry.new(:input => InputTester.new("x = 5;"), :output => @str_output)
|
||||
pry_tester.rep(o)
|
||||
@str_output.string.should == ""
|
||||
mock_pry("x = 5;").should == ""
|
||||
end
|
||||
|
||||
it 'should suppress output if input ends in a ";" (multi-line)' do
|
||||
o = Object.new
|
||||
|
||||
pry_tester = Pry.new(:input => InputTester.new("def self.blah", ":test", "end;"), :output => @str_output)
|
||||
pry_tester.rep(o)
|
||||
@str_output.string.should == ""
|
||||
mock_pry("def self.blah", ":test", "end;").should == ""
|
||||
end
|
||||
|
||||
it 'should be able to evaluate exceptions normally' do
|
||||
o = Exception.new
|
||||
|
||||
was_called = false
|
||||
pry_tester = Pry.new(:input => InputTester.new("self"),
|
||||
:output => @str_output,
|
||||
:exception_handler => proc { was_called = true })
|
||||
|
||||
pry_tester.rep(o)
|
||||
mock_pry("RuntimeError.new", :exception_handler => proc{ was_called = true })
|
||||
was_called.should == false
|
||||
end
|
||||
|
||||
it 'should notice when exceptions are raised' do
|
||||
o = Exception.new
|
||||
|
||||
was_called = false
|
||||
pry_tester = Pry.new(:input => InputTester.new("raise self"),
|
||||
:output => @str_output,
|
||||
:exception_handler => proc { was_called = true })
|
||||
|
||||
pry_tester.rep(o)
|
||||
mock_pry("raise RuntimeError", :exception_handler => proc{ was_called = true })
|
||||
was_called.should == true
|
||||
end
|
||||
|
||||
|
@ -221,62 +174,46 @@ describe Pry do
|
|||
|
||||
describe "history arrays" do
|
||||
it 'sets _ to the last result' do
|
||||
res = []
|
||||
input = InputTester.new *[":foo", "self << _", "42", "self << _"]
|
||||
pry = Pry.new(:input => input, :output => StringIO.new)
|
||||
pry.repl(res)
|
||||
|
||||
res.should == [:foo, 42]
|
||||
t = pry_tester
|
||||
t.eval ":foo"
|
||||
t.eval("_").should == :foo
|
||||
t.eval "42"
|
||||
t.eval("_").should == 42
|
||||
end
|
||||
|
||||
it 'sets out to an array with the result' do
|
||||
res = {}
|
||||
input = InputTester.new *[":foo", "42", "self[:res] = _out_"]
|
||||
pry = Pry.new(:input => input, :output => StringIO.new)
|
||||
pry.repl(res)
|
||||
t = pry_tester
|
||||
t.eval ":foo"
|
||||
t.eval "42"
|
||||
res = t.eval "_out_"
|
||||
|
||||
res[:res].should.be.kind_of Pry::HistoryArray
|
||||
res[:res][1..2].should == [:foo, 42]
|
||||
res.should.be.kind_of Pry::HistoryArray
|
||||
res[1..2].should == [:foo, 42]
|
||||
end
|
||||
|
||||
it 'sets _in_ to an array with the entered lines' do
|
||||
res = {}
|
||||
input = InputTester.new *[":foo", "42", "self[:res] = _in_"]
|
||||
pry = Pry.new(:input => input, :output => StringIO.new)
|
||||
pry.repl(res)
|
||||
t = pry_tester
|
||||
t.eval ":foo"
|
||||
t.eval "42"
|
||||
res = t.eval "_in_"
|
||||
|
||||
res[:res].should.be.kind_of Pry::HistoryArray
|
||||
res[:res][1..2].should == [":foo\n", "42\n"]
|
||||
res.should.be.kind_of Pry::HistoryArray
|
||||
res[1..2].should == [":foo\n", "42\n"]
|
||||
end
|
||||
|
||||
it 'uses 100 as the size of _in_ and _out_' do
|
||||
res = []
|
||||
input = InputTester.new *["self << _out_.max_size << _in_.max_size"]
|
||||
pry = Pry.new(:input => input, :output => StringIO.new)
|
||||
pry.repl(res)
|
||||
|
||||
res.should == [100, 100]
|
||||
pry_tester.eval("[_in_.max_size, _out_.max_size]").should == [100, 100]
|
||||
end
|
||||
|
||||
it 'can change the size of the history arrays' do
|
||||
res = []
|
||||
input = InputTester.new *["self << _out_.max_size << _in_.max_size"]
|
||||
pry = Pry.new(:input => input, :output => StringIO.new,
|
||||
:memory_size => 1000)
|
||||
pry.repl(res)
|
||||
|
||||
res.should == [1000, 1000]
|
||||
pry_tester(:memory_size => 1000).eval("[_out_, _in_].map(&:max_size)").should == [1000, 1000]
|
||||
end
|
||||
|
||||
it 'store exceptions' do
|
||||
res = []
|
||||
input = InputTester.new *["foo!","self << _in_[-1] << _out_[-1]"]
|
||||
pry = Pry.new(:input => input, :output => StringIO.new,
|
||||
:memory_size => 1000)
|
||||
pry.repl(res)
|
||||
mock_pry("foo!", "Pad.in = _in_[-1]; Pad.out = _out_[-1]")
|
||||
|
||||
res.first.should == "foo!\n"
|
||||
res.last.should.be.kind_of NoMethodError
|
||||
Pad.in.should == "foo!\n"
|
||||
Pad.out.should.be.kind_of NoMethodError
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -346,13 +283,6 @@ describe Pry do
|
|||
Object.const_defined?(:TEST_RC).should == false
|
||||
end
|
||||
|
||||
it "should not load the rc file if #repl method invoked" do
|
||||
Pry.config.should_load_rc = true
|
||||
Pry.new(:input => StringIO.new("exit-all\n"), :output => StringIO.new).repl(self)
|
||||
Object.const_defined?(:TEST_RC).should == false
|
||||
Pry.config.should_load_rc = false
|
||||
end
|
||||
|
||||
describe "that raise exceptions" do
|
||||
before do
|
||||
Pry::HOME_RC_FILE = "spec/fixtures/testrcbad"
|
||||
|
@ -417,24 +347,20 @@ describe Pry do
|
|||
describe "defining methods" do
|
||||
it 'should define a method on the singleton class of an object when performing "def meth;end" inside the object' do
|
||||
[Object.new, {}, []].each do |val|
|
||||
str_input = StringIO.new("def hello;end")
|
||||
Pry.new(:input => str_input, :output => StringIO.new).rep(val)
|
||||
|
||||
pry_eval(val, 'def hello; end')
|
||||
val.methods(false).map(&:to_sym).include?(:hello).should == true
|
||||
end
|
||||
end
|
||||
|
||||
it 'should define an instance method on the module when performing "def meth;end" inside the module' do
|
||||
str_input = StringIO.new("def hello;end")
|
||||
hello = Module.new
|
||||
Pry.new(:input => str_input, :output => StringIO.new).rep(hello)
|
||||
pry_eval(hello, "def hello; end")
|
||||
hello.instance_methods(false).map(&:to_sym).include?(:hello).should == true
|
||||
end
|
||||
|
||||
it 'should define an instance method on the class when performing "def meth;end" inside the class' do
|
||||
str_input = StringIO.new("def hello;end")
|
||||
hello = Class.new
|
||||
Pry.new(:input => str_input, :output => StringIO.new).rep(hello)
|
||||
pry_eval(hello, "def hello; end")
|
||||
hello.instance_methods(false).map(&:to_sym).include?(:hello).should == true
|
||||
end
|
||||
|
||||
|
@ -442,8 +368,7 @@ describe Pry do
|
|||
# should include float in here, but test fails for some reason
|
||||
# on 1.8.7, no idea why!
|
||||
[:test, 0, true, false, nil].each do |val|
|
||||
str_input = StringIO.new("def hello;end")
|
||||
Pry.new(:input => str_input, :output => StringIO.new).rep(val)
|
||||
pry_eval(val, "def hello; end");
|
||||
val.class.instance_methods(false).map(&:to_sym).include?(:hello).should == true
|
||||
end
|
||||
end
|
||||
|
@ -501,4 +426,14 @@ describe Pry do
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "a fresh instance" do
|
||||
it "should use `caller` as its backtrace" do
|
||||
location = "#{__FILE__}:#{__LINE__ + 1}"
|
||||
backtrace = Pry.new.backtrace
|
||||
|
||||
backtrace.should.not.be.nil
|
||||
backtrace.any? { |l| l.include?(location) }.should.be.true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -122,31 +122,21 @@ describe "Sticky locals (_file_ and friends)" do
|
|||
end
|
||||
|
||||
it 'should create a new sticky local' do
|
||||
o = Object.new
|
||||
pi = Pry.new
|
||||
pi.add_sticky_local(:test_local) { :test_value }
|
||||
pi.input, pi.output = InputTester.new("@value = test_local", "exit-all"), StringIO.new
|
||||
pi.repl(o)
|
||||
|
||||
o.instance_variable_get(:@value).should == :test_value
|
||||
t = pry_tester
|
||||
t.eval "_pry_.add_sticky_local(:test_local){ :test_value }"
|
||||
t.eval("test_local").should == :test_value
|
||||
end
|
||||
|
||||
it 'should still exist after cd-ing into new binding' do
|
||||
o = Object.new
|
||||
o2 = Object.new
|
||||
o.instance_variable_set(:@o2, o2)
|
||||
pi = Pry.new
|
||||
pi.add_sticky_local(:test_local) { :test_value }
|
||||
pi.input = InputTester.new("cd @o2\n",
|
||||
"@value = test_local", "exit-all")
|
||||
pi.output = StringIO.new
|
||||
pi.repl(o)
|
||||
|
||||
o2.instance_variable_get(:@value).should == :test_value
|
||||
t = pry_tester
|
||||
t.eval "_pry_.add_sticky_local(:test_local){ :test_value }"
|
||||
t.eval "cd Object.new"
|
||||
t.eval("test_local").should == :test_value
|
||||
end
|
||||
|
||||
it 'should provide different values for successive block invocations' do
|
||||
pry = Pry.new
|
||||
pry.push_binding binding
|
||||
pry.add_sticky_local(:test_local) { rand }
|
||||
value1 = pry.evaluate_ruby 'test_local'
|
||||
value2 = pry.evaluate_ruby 'test_local'
|
||||
|
|
Loading…
Add table
Reference in a new issue