2018-10-27 17:04:13 -04:00
|
|
|
class Pry
|
2018-11-03 09:58:16 -04:00
|
|
|
# Prompt represents the Pry prompt, which can be used with Readline-like
|
|
|
|
# libraries. It defines a few default prompts (default prompt, simple prompt,
|
2018-11-10 15:20:45 -05:00
|
|
|
# etc) and also provides an API for adding and implementing custom prompts.
|
2018-10-28 04:12:39 -04:00
|
|
|
#
|
2018-11-10 15:20:45 -05:00
|
|
|
# @example Registering a new Pry prompt
|
2018-11-03 09:58:16 -04:00
|
|
|
# Pry::Prompt.add(
|
|
|
|
# :ipython,
|
|
|
|
# 'IPython-like prompt', [':', '...:']
|
|
|
|
# ) do |_context, _nesting, _pry_, sep|
|
|
|
|
# sep == ':' ? "In [#{_pry_.input_ring.count}]: " : ' ...: '
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Produces:
|
|
|
|
# # In [3]: def foo
|
|
|
|
# # ...: puts 'foo'
|
|
|
|
# # ...: end
|
|
|
|
# # => :foo
|
|
|
|
# # In [4]:
|
2018-11-10 15:20:45 -05:00
|
|
|
#
|
|
|
|
# @example Manually instantiating the Prompt class
|
|
|
|
# prompt_procs = [
|
|
|
|
# proc { '#{rand(1)}>" },
|
|
|
|
# proc { "#{('a'..'z').to_a.sample}*" }
|
|
|
|
# ]
|
|
|
|
# prompt = Pry::Prompt.new(
|
|
|
|
# :random,
|
|
|
|
# 'Random number or letter prompt.',
|
|
|
|
# prompt_procs
|
|
|
|
# )
|
|
|
|
# prompt.wait_proc.call(...) #=>
|
|
|
|
# prompt.incomplete_proc.call(...)
|
|
|
|
#
|
2018-10-28 04:12:39 -04:00
|
|
|
# @since v0.11.0
|
2018-11-03 09:58:16 -04:00
|
|
|
# @api public
|
2018-11-10 15:20:45 -05:00
|
|
|
class Prompt
|
2018-10-28 04:12:39 -04:00
|
|
|
# @return [String]
|
2018-10-27 17:35:27 -04:00
|
|
|
DEFAULT_NAME = 'pry'.freeze
|
|
|
|
|
2018-10-28 04:12:39 -04:00
|
|
|
# @return [Array<Object>] the list of objects that are known to have a
|
|
|
|
# 1-line #inspect output suitable for prompt
|
2018-10-28 04:07:44 -04:00
|
|
|
SAFE_CONTEXTS = [String, Numeric, Symbol, nil, true, false].freeze
|
2018-10-27 17:35:27 -04:00
|
|
|
|
2018-11-03 09:58:16 -04:00
|
|
|
# A Hash that holds all prompts. The keys of the Hash are prompt
|
|
|
|
# names, the values are Hash instances of the format {:description, :value}.
|
|
|
|
@prompts = {}
|
2018-10-28 04:12:39 -04:00
|
|
|
|
|
|
|
class << self
|
2018-11-03 09:58:16 -04:00
|
|
|
# Retrieves a prompt.
|
|
|
|
#
|
|
|
|
# @example
|
2018-11-10 15:20:45 -05:00
|
|
|
# Prompt[:my_prompt]
|
2018-11-03 09:58:16 -04:00
|
|
|
#
|
2018-11-15 15:17:55 -05:00
|
|
|
# @param [Symbol] name The name of the prompt you want to access
|
2018-11-03 09:58:16 -04:00
|
|
|
# @return [Hash{Symbol=>Object}]
|
|
|
|
# @since v0.12.0
|
2018-11-15 15:17:55 -05:00
|
|
|
def [](name)
|
|
|
|
@prompts[name.to_s]
|
2018-10-28 04:12:39 -04:00
|
|
|
end
|
|
|
|
|
2018-11-03 09:58:16 -04:00
|
|
|
# @return [Hash{Symbol=>Hash}] the duplicate of the internal prompts hash
|
|
|
|
# @note Use this for read-only operations
|
|
|
|
# @since v0.12.0
|
|
|
|
def all
|
|
|
|
@prompts.dup
|
2018-10-28 04:12:39 -04:00
|
|
|
end
|
|
|
|
|
2018-11-03 09:58:16 -04:00
|
|
|
# Adds a new prompt to the prompt hash.
|
|
|
|
#
|
2018-11-15 15:17:55 -05:00
|
|
|
# @param [Symbol] name
|
2018-11-03 09:58:16 -04:00
|
|
|
# @param [String] description
|
|
|
|
# @param [Array<String>] separators The separators to differentiate
|
|
|
|
# between prompt modes (default mode and class/method definition mode).
|
|
|
|
# The Array *must* have a size of 2.
|
|
|
|
# @yield [context, nesting, _pry_, sep]
|
|
|
|
# @yieldparam context [Object] the context where Pry is currently in
|
|
|
|
# @yieldparam nesting [Integer] whether the context is nested
|
|
|
|
# @yieldparam _pry_ [Pry] the Pry instance
|
|
|
|
# @yieldparam separator [String] separator string
|
|
|
|
# @return [nil]
|
|
|
|
# @raise [ArgumentError] if the size of `separators` is not 2
|
2018-11-15 15:17:55 -05:00
|
|
|
# @raise [ArgumentError] if `prompt_name` is already occupied
|
2018-11-03 09:58:16 -04:00
|
|
|
# @since v0.12.0
|
2018-11-15 15:17:55 -05:00
|
|
|
def add(name, description = '', separators = %w[> *])
|
|
|
|
name = name.to_s
|
|
|
|
|
2018-11-03 09:58:16 -04:00
|
|
|
unless separators.size == 2
|
|
|
|
raise ArgumentError, "separators size must be 2, given #{separators.size}"
|
2018-10-28 04:12:39 -04:00
|
|
|
end
|
2018-11-03 09:58:16 -04:00
|
|
|
|
2018-11-15 15:17:55 -05:00
|
|
|
if @prompts.key?(name)
|
|
|
|
raise ArgumentError, "the '#{name}' prompt was already added"
|
|
|
|
end
|
|
|
|
|
2018-11-10 15:20:45 -05:00
|
|
|
@prompts[name] = new(
|
|
|
|
name,
|
|
|
|
description,
|
|
|
|
separators.map do |sep|
|
2018-11-03 09:58:16 -04:00
|
|
|
proc { |context, nesting, _pry_| yield(context, nesting, _pry_, sep) }
|
|
|
|
end
|
2018-11-10 15:20:45 -05:00
|
|
|
)
|
2018-11-03 09:58:16 -04:00
|
|
|
|
|
|
|
nil
|
2018-10-28 04:12:39 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-10 15:20:45 -05:00
|
|
|
# @return [String]
|
|
|
|
attr_reader :name
|
|
|
|
|
|
|
|
# @return [String]
|
|
|
|
attr_reader :description
|
|
|
|
|
|
|
|
# @return [Array<Proc>] the array of procs that hold
|
|
|
|
# `[wait_proc, incomplete_proc]`
|
|
|
|
attr_reader :prompt_procs
|
|
|
|
|
|
|
|
# @param [String] name
|
|
|
|
# @param [String] description
|
|
|
|
# @param [Array<Proc>] prompt_procs
|
|
|
|
def initialize(name, description, prompt_procs)
|
|
|
|
@name = name
|
|
|
|
@description = description
|
|
|
|
@prompt_procs = prompt_procs
|
|
|
|
end
|
|
|
|
|
|
|
|
# @return [Proc] the proc which builds the wait prompt (`>`)
|
|
|
|
def wait_proc
|
|
|
|
@prompt_procs.first
|
|
|
|
end
|
|
|
|
|
|
|
|
# @return [Proc] the proc which builds the prompt when in the middle of an
|
|
|
|
# expression such as open method, etc. (`*`)
|
|
|
|
def incomplete_proc
|
|
|
|
@prompt_procs.last
|
|
|
|
end
|
|
|
|
|
|
|
|
# @deprecated Use a `Pry::Prompt` instance directly
|
|
|
|
def [](key)
|
|
|
|
key = key.to_s
|
|
|
|
loc = caller_locations(1..1).first
|
|
|
|
|
|
|
|
if %w[name description].include?(key)
|
|
|
|
warn(
|
|
|
|
"#{loc.path}:#{loc.lineno}: warning: `Pry::Prompt[:#{@name}][:#{key}]` " \
|
|
|
|
"is deprecated. Use `#{self.class}##{key}` instead"
|
|
|
|
)
|
|
|
|
public_send(key)
|
|
|
|
elsif key.to_s == 'value'
|
|
|
|
warn(
|
|
|
|
"#{loc.path}:#{loc.lineno}: warning: `#{self.class}[:#{@name}][:value]` " \
|
|
|
|
"is deprecated. Use `#{self.class}#prompt_procs` instead or an " \
|
|
|
|
"instance of `#{self.class}` directly"
|
|
|
|
)
|
|
|
|
@prompt_procs
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
add(
|
|
|
|
:default,
|
|
|
|
"The default Pry prompt. Includes information about the current expression \n" \
|
|
|
|
"number, evaluation context, and nesting level, plus a reminder that you're \n" \
|
|
|
|
'using Pry.'
|
|
|
|
) do |context, nesting, _pry_, sep|
|
2018-11-03 09:58:16 -04:00
|
|
|
format(
|
|
|
|
"[%<in_count>s] %<name>s(%<context>s)%<nesting>s%<separator>s ",
|
|
|
|
in_count: _pry_.input_ring.count,
|
2018-11-20 08:10:21 -05:00
|
|
|
name: _pry_.config.prompt_name,
|
2018-11-03 09:58:16 -04:00
|
|
|
context: Pry.view_clip(context),
|
|
|
|
nesting: (nesting > 0 ? ":#{nesting}" : ''),
|
|
|
|
separator: sep
|
|
|
|
)
|
|
|
|
end
|
2018-10-27 17:35:27 -04:00
|
|
|
|
2018-11-05 15:25:10 -05:00
|
|
|
add(
|
2018-11-10 15:20:45 -05:00
|
|
|
:simple,
|
2018-11-05 15:25:10 -05:00
|
|
|
"A simple `>>`.",
|
|
|
|
['>> ', ' | ']
|
|
|
|
) do |_, _, _, sep|
|
2018-11-03 09:58:16 -04:00
|
|
|
sep
|
|
|
|
end
|
2014-03-19 09:06:26 -04:00
|
|
|
|
2018-11-05 15:25:10 -05:00
|
|
|
add(
|
2018-11-10 15:20:45 -05:00
|
|
|
:nav,
|
2018-11-05 15:25:10 -05:00
|
|
|
"A prompt that displays the binding stack as a path and includes information \n" \
|
|
|
|
"about #{Helpers::Text.bold('_in_')} and #{Helpers::Text.bold('_out_')}.",
|
|
|
|
%w[> *]
|
2018-11-11 07:22:03 -05:00
|
|
|
) do |_context, _nesting, _pry_, sep|
|
2018-11-03 09:58:16 -04:00
|
|
|
tree = _pry_.binding_stack.map { |b| Pry.view_clip(b.eval('self')) }
|
|
|
|
format(
|
|
|
|
"[%<in_count>s] (%<name>s) %<tree>s: %<stack_size>s%<separator>s ",
|
|
|
|
in_count: _pry_.input_ring.count,
|
2018-11-20 08:10:21 -05:00
|
|
|
name: _pry_.config.prompt_name,
|
2018-11-03 09:58:16 -04:00
|
|
|
tree: tree.join(' / '),
|
|
|
|
stack_size: _pry_.binding_stack.size - 1,
|
|
|
|
separator: sep
|
|
|
|
)
|
|
|
|
end
|
2014-03-19 09:06:26 -04:00
|
|
|
|
2018-11-05 15:25:10 -05:00
|
|
|
add(
|
2018-11-10 15:20:45 -05:00
|
|
|
:shell,
|
2018-11-05 15:25:10 -05:00
|
|
|
'A prompt that displays `$PWD` as you change it.',
|
|
|
|
%w[$ *]
|
2018-11-11 07:22:03 -05:00
|
|
|
) do |context, _nesting, _pry_, sep|
|
2018-11-03 09:58:16 -04:00
|
|
|
format(
|
|
|
|
"%<name>s %<context>s:%<pwd>s %<separator>s ",
|
2018-11-20 08:10:21 -05:00
|
|
|
name: _pry_.config.prompt_name,
|
2018-11-03 09:58:16 -04:00
|
|
|
context: Pry.view_clip(context),
|
|
|
|
pwd: Dir.pwd,
|
|
|
|
separator: sep
|
|
|
|
)
|
|
|
|
end
|
2014-03-19 09:06:26 -04:00
|
|
|
|
2018-11-05 15:25:10 -05:00
|
|
|
add(
|
2018-11-10 15:20:45 -05:00
|
|
|
:none,
|
2018-11-05 15:25:10 -05:00
|
|
|
'Wave goodbye to the Pry prompt.',
|
|
|
|
Array.new(2)
|
|
|
|
) { '' }
|
2018-10-27 17:04:13 -04:00
|
|
|
end
|
2014-03-19 09:06:26 -04:00
|
|
|
end
|