1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00
pry--pry/lib/pry/pry_class.rb
2018-10-13 03:09:29 +08:00

408 lines
12 KiB
Ruby

require 'pry/config'
class Pry
HOME_RC_FILE =
if ENV.key?('PRYRC')
ENV['PRYRC']
elsif File.exist?(File.expand_path('~/.pryrc'))
'~/.pryrc'
elsif ENV.key?('XDG_CONFIG_HOME') && ENV['XDG_CONFIG_HOME'] != ''
# See XDG Base Directory Specification at
# https://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
ENV['XDG_CONFIG_HOME'] + '/pry/pryrc'
else
'~/.config/pry/pryrc'
end
LOCAL_RC_FILE = "./.pryrc"
class << self
extend Pry::Forwardable
attr_accessor :custom_completions
attr_accessor :current_line
attr_accessor :line_buffer
attr_accessor :eval_path
attr_accessor :cli
attr_accessor :quiet
attr_accessor :last_internal_error
attr_accessor :config
attr_writer :history
def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins
extend Pry::Config::Convenience
config_shortcut(*Pry::Config.shortcuts)
def prompt=(value)
config.prompt = value
end
def prompt
config.prompt
end
def history
@history ||= History.new
end
#
# @example
# Pry.configure do |config|
# config.eager_load! # optional
# config.input = # ..
# config.foo = 2
# end
#
# @yield [config]
# Yields a block with {Pry.config} as its argument.
#
def configure
yield config
end
end
#
# @return [main]
# returns the special instance of Object, "main".
#
def self.main
@main ||= TOPLEVEL_BINDING.eval "self"
end
#
# @return [Pry::Config]
# Returns a value store for an instance of Pry running on the current thread.
#
def self.current
Thread.current[:__pry__] ||= Pry::Config.from_hash({}, nil)
end
# Load the given file in the context of `Pry.toplevel_binding`
# @param [String] file The unexpanded file path.
def self.load_file_at_toplevel(file)
toplevel_binding.eval(File.read(file), file)
rescue RescuableException => e
puts "Error loading #{file}: #{e}\n#{e.backtrace.first}"
end
# Load HOME_RC_FILE and LOCAL_RC_FILE if appropriate
# This method can also be used to reload the files if they have changed.
def self.load_rc_files
rc_files_to_load.each do |file|
critical_section do
load_file_at_toplevel(file)
end
end
end
# Load the local RC file (./.pryrc)
def self.rc_files_to_load
files = []
files << HOME_RC_FILE if Pry.config.should_load_rc
files << LOCAL_RC_FILE if Pry.config.should_load_local_rc
files.map { |file| real_path_to(file) }.compact.uniq
end
# Expand a file to its canonical name (following symlinks as appropriate)
def self.real_path_to(file)
Pathname.new(File.expand_path(file)).realpath.to_s
rescue Errno::ENOENT, Errno::EACCES
nil
end
# Load any Ruby files specified with the -r flag on the command line.
def self.load_requires
Pry.config.requires.each do |file|
require file
end
end
# Trap interrupts on jruby, and make them behave like MRI so we can
# catch them.
def self.load_traps
trap('INT'){ raise Interrupt }
end
def self.load_win32console
begin
require 'win32console'
# The mswin and mingw versions of pry require win32console, so this should
# only fail on jruby (where win32console doesn't work).
# Instead we'll recommend ansicon, which does.
rescue LoadError
warn <<-WARNING if Pry.config.windows_console_warning
For a better Pry experience on Windows, please use ansicon:
https://github.com/adoxa/ansicon
If you use an alternative to ansicon and don't want to see this warning again,
you can add "Pry.config.windows_console_warning = false" to your pryrc.
WARNING
end
end
# Do basic setup for initial session including: loading pryrc, plugins,
# requires, and history.
def self.initial_session_setup
return unless initial_session?
@initial_session = false
# note these have to be loaded here rather than in pry_instance as
# we only want them loaded once per entire Pry lifetime.
load_rc_files
end
def self.final_session_setup
return if @session_finalized
@session_finalized = true
load_plugins if Pry.config.should_load_plugins
load_requires if Pry.config.should_load_requires
load_history if Pry.config.history.should_load
load_traps if Pry.config.should_trap_interrupts
load_win32console if Pry::Helpers::BaseHelpers.windows? && !Pry::Helpers::BaseHelpers.windows_ansi?
end
# Start a Pry REPL.
# This method also loads `pryrc` as necessary the first time it is invoked.
# @param [Object, Binding] target The receiver of the Pry session
# @param [Hash] options
# @option options (see Pry#initialize)
# @example
# Pry.start(Object.new, :input => MyInput.new)
def self.start(target=nil, options={})
return if ENV['DISABLE_PRY']
if ENV['FAIL_PRY']
raise 'You have FAIL_PRY set to true, which results in Pry calls failing'
end
options = options.to_hash
if in_critical_section?
output.puts "ERROR: Pry started inside Pry."
output.puts "This can happen if you have a binding.pry inside a #to_s or #inspect function."
return
end
options[:target] = Pry.binding_for(target || toplevel_binding)
initial_session_setup
final_session_setup
# Unless we were given a backtrace, save the current one
if options[:backtrace].nil?
options[:backtrace] = caller
# 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
driver = options[:driver] || Pry::REPL
# Enter the matrix
driver.start(options)
rescue Pry::TooSafeException
puts "ERROR: Pry cannot work with $SAFE > 0"
raise
end
# Execute the file through the REPL loop, non-interactively.
# @param [String] file_name File name to load through the REPL.
def self.load_file_through_repl(file_name)
require "pry/repl_file_loader"
REPLFileLoader.new(file_name).load
end
#
# An inspector that clips the output to `max_length` chars.
# In case of > `max_length` chars the `#<Object...> notation is used.
#
# @param [Object] obj
# The object to view.
#
# @param [Hash] options
# @option options [Integer] :max_length (60)
# The maximum number of chars before clipping occurs.
#
# @option options [Boolean] :id (false)
# Boolean to indicate whether or not a hex reprsentation of the object ID
# is attached to the return value when the length of inspect is greater than
# value of `:max_length`.
#
# @return [String]
# The string representation of `obj`.
#
def self.view_clip(obj, options = {})
max = options.fetch :max_length, 60
id = options.fetch :id, false
if obj.kind_of?(Module) && obj.name.to_s != "" && obj.name.to_s.length <= max
obj.name.to_s
elsif Pry.main == obj
# special-case to support jruby.
# fixed as of https://github.com/jruby/jruby/commit/d365ebd309cf9df3dde28f5eb36ea97056e0c039
# we can drop in the future.
obj.to_s
elsif Pry.config.prompt_safe_objects.any? { |v| v === obj } && obj.inspect.length <= max
obj.inspect
else
id == true ? "#<#{obj.class}:0x%x>" % (obj.object_id << 1) : "#<#{obj.class}>"
end
rescue RescuableException
"unknown"
end
# Load Readline history if required.
def self.load_history
Pry.history.load
end
# @return [Boolean] Whether this is the first time a Pry session has
# been started since loading the Pry class.
def self.initial_session?
@initial_session
end
# Run a Pry command from outside a session. The commands available are
# those referenced by `Pry.config.commands` (the default command set).
# @param [String] command_string The Pry command (including arguments,
# if any).
# @param [Hash] options Optional named parameters.
# @return [nil]
# @option options [Object, Binding] :target The object to run the
# command under. Defaults to `TOPLEVEL_BINDING` (main).
# @option options [Boolean] :show_output Whether to show command
# output. Defaults to true.
# @example Run at top-level with no output.
# Pry.run_command "ls"
# @example Run under Pry class, returning only public methods.
# Pry.run_command "ls -m", :target => Pry
# @example Display command output.
# Pry.run_command "ls -av", :show_output => true
def self.run_command(command_string, options={})
options = {
target: TOPLEVEL_BINDING,
show_output: true,
output: Pry.config.output,
commands: Pry.config.commands
}.merge!(options)
# :context for compatibility with <= 0.9.11.4
target = options[:context] || options[:target]
output = options[:show_output] ? options[:output] : StringIO.new
pry = Pry.new(output: output, target: target, commands: options[:commands])
pry.eval command_string
nil
end
def self.default_editor_for_platform
return ENV['VISUAL'] if ENV['VISUAL'] and not ENV['VISUAL'].empty?
return ENV['EDITOR'] if ENV['EDITOR'] and not ENV['EDITOR'].empty?
if Helpers::BaseHelpers.windows?
'notepad'
else
%w(editor nano vi).detect do |editor|
system("which #{editor} > /dev/null 2>&1")
end
end
end
def self.auto_resize!
Pry.config.input # by default, load Readline
if !defined?(Readline) || Pry.config.input != Readline
warn "Sorry, you must be using Readline for Pry.auto_resize! to work."
return
end
if Readline::VERSION =~ /edit/i
warn <<-EOT
Readline version #{Readline::VERSION} detected - will not auto_resize! correctly.
For the fix, use GNU Readline instead:
https://github.com/guard/guard/wiki/Add-proper-Readline-support-to-Ruby-on-Mac-OS-X
EOT
return
end
trap :WINCH do
begin
Readline.set_screen_size(*Terminal.size!)
rescue => e
warn "\nPry.auto_resize!'s Readline.set_screen_size failed: #{e}"
end
begin
Readline.refresh_line
rescue => e
warn "\nPry.auto_resize!'s Readline.refresh_line failed: #{e}"
end
end
end
# Set all the configurable options back to their default values
def self.reset_defaults
@initial_session = true
@session_finalized = nil
self.config = Pry::Config.new Pry::Config::Default.new
self.cli = false
self.current_line = 1
self.line_buffer = [""]
self.eval_path = "(pry)"
end
# Basic initialization.
def self.init
@plugin_manager ||= PluginManager.new
reset_defaults
locate_plugins
end
# Return a `Binding` object for `target` or return `target` if it is
# already a `Binding`.
# In the case where `target` is top-level then return `TOPLEVEL_BINDING`
# @param [Object] target The object to get a `Binding` object for.
# @return [Binding] The `Binding` object.
def self.binding_for(target)
if Binding === target
target
else
if Pry.main == target
TOPLEVEL_BINDING
else
target.__binding__
end
end
end
def self.toplevel_binding
unless defined?(@toplevel_binding) && @toplevel_binding
# Grab a copy of the TOPLEVEL_BINDING without any local variables.
# This binding has a default definee of Object, and new methods are
# private (just as in TOPLEVEL_BINDING).
TOPLEVEL_BINDING.eval <<-RUBY
def self.__pry__
binding
end
Pry.toplevel_binding = __pry__
class << self; undef __pry__; end
RUBY
end
@toplevel_binding.eval('private')
@toplevel_binding
end
def self.toplevel_binding=(binding)
@toplevel_binding = binding
end
def self.in_critical_section?
Thread.current[:pry_critical_section] ||= 0
Thread.current[:pry_critical_section] > 0
end
def self.critical_section(&block)
Thread.current[:pry_critical_section] ||= 0
Thread.current[:pry_critical_section] += 1
yield
ensure
Thread.current[:pry_critical_section] -= 1
end
end
Pry.init