mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
188 lines
6.1 KiB
Ruby
188 lines
6.1 KiB
Ruby
require 'forwardable'
|
|
|
|
class Pry
|
|
class CommandProcessor
|
|
|
|
# Wraps the return result of process_commands, indicates if the
|
|
# result IS a command and what kind of command (e.g void)
|
|
class Result
|
|
attr_reader :retval
|
|
|
|
def initialize(is_command, keep_retval = false, retval = nil)
|
|
@is_command, @keep_retval, @retval = is_command, keep_retval, retval
|
|
end
|
|
|
|
# Is the result a command?
|
|
# @return [Boolean]
|
|
def command?
|
|
@is_command
|
|
end
|
|
|
|
# Is the result a command and if it is, is it a void command?
|
|
# (one that does not return a value)
|
|
# @return [Boolean]
|
|
def void_command?
|
|
(command? && !keep_retval?) || retval == Command::VOID_VALUE
|
|
end
|
|
|
|
# Is the return value kept for this command? (i.e :keep_retval => true)
|
|
# @return [Boolean]
|
|
def keep_retval?
|
|
@keep_retval
|
|
end
|
|
end
|
|
|
|
extend Forwardable
|
|
|
|
attr_accessor :pry_instance
|
|
|
|
def initialize(pry_instance)
|
|
@pry_instance = pry_instance
|
|
end
|
|
|
|
def_delegators :@pry_instance, :commands, :output
|
|
|
|
# Is the string a valid command?
|
|
# @param [String] val The string passed in from the Pry prompt.
|
|
# @param [Binding] target The context where the string should be
|
|
# interpolated in.
|
|
# @return [Boolean] Whether the string is a valid command.
|
|
def valid_command?(val, target=binding)
|
|
!!(command_matched(val, target)[0])
|
|
end
|
|
|
|
# Convert the object to a form that can be interpolated into a
|
|
# Regexp cleanly.
|
|
# @return [String] The string to interpolate into a Regexp
|
|
def convert_to_regex(obj)
|
|
case obj
|
|
when String
|
|
Regexp.escape(obj)
|
|
else
|
|
obj
|
|
end
|
|
end
|
|
|
|
# Revaluate the string (str) and perform interpolation.
|
|
# @param [String] str The string to reevaluate with interpolation.
|
|
# @param [Binding] target The context where the string should be
|
|
# interpolated in.
|
|
# @return [String] The reevaluated string with interpolations
|
|
# applied (if any).
|
|
def interpolate_string(str, target)
|
|
dumped_str = str.dump
|
|
dumped_str.gsub!(/\\\#\{/, '#{')
|
|
target.eval(dumped_str)
|
|
end
|
|
|
|
# Determine whether a Pry command was matched and return command data
|
|
# and argument string.
|
|
# This method should not need to be invoked directly.
|
|
# @param [String] val The line of input.
|
|
# @param [Binding] target The binding to perform string
|
|
# interpolation against.
|
|
# @return [Array] The command data and arg string pair
|
|
def command_matched(val, target)
|
|
_, cmd_data = commands.commands.find do |name, data|
|
|
prefix = convert_to_regex(Pry.config.command_prefix)
|
|
prefix = "(?:#{prefix})?" unless data.options[:use_prefix]
|
|
|
|
command_regex = /^#{prefix}#{convert_to_regex(name)}(?!\S)/
|
|
|
|
if command_regex =~ val
|
|
if data.options[:interpolate]
|
|
val.replace(interpolate_string(val, target))
|
|
command_regex =~ val # re-match with the interpolated string
|
|
end
|
|
true
|
|
end
|
|
end
|
|
|
|
[cmd_data, (Regexp.last_match ? Regexp.last_match.captures : nil), (Regexp.last_match ? Regexp.last_match.end(0) : nil)]
|
|
end
|
|
|
|
# Display a warning if a command collides with a local/method in
|
|
# the current scope.
|
|
# @param [String] command_name_match The name of the colliding command.
|
|
# @param [Binding] target The current binding context.
|
|
def check_for_command_name_collision(command_name_match, target)
|
|
if collision_type = target.eval("defined?(#{command_name_match})")
|
|
pry_instance.output.puts "#{Pry::Helpers::Text.bold('WARNING:')} Command name collision with a #{collision_type}: '#{command_name_match}'\n\n"
|
|
end
|
|
rescue Pry::RescuableException
|
|
end
|
|
|
|
# Process Pry commands. Pry commands are not Ruby methods and are evaluated
|
|
# prior to Ruby expressions.
|
|
# Commands can be modified/configured by the user: see `Pry::Commands`
|
|
# This method should not need to be invoked directly - it is called
|
|
# by `Pry#r`.
|
|
# @param [String] val The current line of input.
|
|
# @param [String] eval_string The cumulative lines of input for
|
|
# multi-line input.
|
|
# @param [Binding] target The receiver of the commands.
|
|
# @return [Pry::CommandProcessor::Result] A wrapper object
|
|
# containing info about the result of the command processing
|
|
# (indicating whether it is a command and if it is what kind of
|
|
# command it is.
|
|
def process_commands(val, eval_string, target)
|
|
|
|
command, captures, pos = command_matched(val, target)
|
|
|
|
# no command was matched, so return to caller
|
|
return Result.new(false) if !command
|
|
|
|
arg_string = val[pos..-1]
|
|
|
|
check_for_command_name_collision(val[0..pos].rstrip, target) if Pry.config.collision_warning
|
|
|
|
# remove the one leading space if it exists
|
|
arg_string.slice!(0) if arg_string.start_with?(" ")
|
|
|
|
if arg_string
|
|
args = command.options[:shellwords] ? Shellwords.shellwords(arg_string) : arg_string.split(" ")
|
|
else
|
|
args = []
|
|
end
|
|
|
|
context = {
|
|
:val => val,
|
|
:arg_string => arg_string,
|
|
:eval_string => eval_string,
|
|
:commands => commands.commands,
|
|
:captures => captures,
|
|
:pry_instance => @pry_instance,
|
|
:output => output,
|
|
:command_processor => self,
|
|
:command_set => commands,
|
|
:target => target
|
|
}
|
|
|
|
ret = execute_command(command, context, *(captures + args))
|
|
|
|
Result.new(true, command.options[:keep_retval], ret)
|
|
end
|
|
|
|
# Execute a Pry command.
|
|
# This method should not need to be invoked directly.
|
|
# @param [Binding] target The target of the Pry session.
|
|
# @param [Pry::CommandSet::Command] command The command object.
|
|
# @param [Hash] options The options to set on the Commands object.
|
|
# @param [Array] args The command arguments.
|
|
# @return [Object] The value returned by the command
|
|
def execute_command(command, context, *args)
|
|
ret = nil
|
|
|
|
instance = command.new(context)
|
|
|
|
catch(:command_done) do
|
|
ret = instance.call_safely(*args)
|
|
end
|
|
|
|
# FIXME: wtf?
|
|
context[:val].replace("")
|
|
|
|
ret
|
|
end
|
|
end
|
|
end
|