This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
module Helpers
|
||||
module BaseHelpers
|
||||
extend self
|
||||
|
||||
def silence_warnings
|
||||
old_verbose = $VERBOSE
|
||||
$VERBOSE = nil
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
$VERBOSE = old_verbose
|
||||
end
|
||||
end
|
||||
|
||||
# Acts like send but ignores any methods defined below Object or Class in the
|
||||
# inheritance hierarchy.
|
||||
# This is required to introspect methods on objects like Net::HTTP::Get that
|
||||
# have overridden the `method` method.
|
||||
def safe_send(obj, method, *args, &block)
|
||||
(obj.is_a?(Module) ? Module : Object).instance_method(method)
|
||||
.bind(obj).call(*args, &block)
|
||||
end
|
||||
|
||||
def find_command(name, set = Pry::Commands)
|
||||
command_match = set.find do |_, command|
|
||||
(listing = command.options[:listing]) == name && !listing.nil?
|
||||
end
|
||||
command_match.last if command_match
|
||||
end
|
||||
|
||||
def not_a_real_file?(file)
|
||||
file =~ /^(\(.*\))$|^<.*>$/ || file =~ /__unknown__/ || file == "" || file == "-e"
|
||||
end
|
||||
|
||||
def use_ansi_codes?
|
||||
Pry::Helpers::Platform.windows_ansi? ||
|
||||
((term = Pry::Env['TERM']) && term != "dumb")
|
||||
end
|
||||
|
||||
def colorize_code(code)
|
||||
SyntaxHighlighter.highlight(code)
|
||||
end
|
||||
|
||||
def highlight(string, regexp, highlight_color = :bright_yellow)
|
||||
string.gsub(regexp) do |match|
|
||||
"<#{highlight_color}>#{match}</#{highlight_color}>"
|
||||
end
|
||||
end
|
||||
|
||||
# formatting
|
||||
def heading(text)
|
||||
text = "#{text}\n--"
|
||||
"\e[1m#{text}\e[0m"
|
||||
end
|
||||
|
||||
# Send the given text through the best available pager (if Pry.config.pager is
|
||||
# enabled). Infers where to send the output if used as a mixin.
|
||||
# DEPRECATED.
|
||||
def stagger_output(text, _out = nil)
|
||||
if defined?(pry_instance) && pry_instance
|
||||
pry_instance.pager.page text
|
||||
else
|
||||
Pry.new.pager.page text
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,146 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'tempfile'
|
||||
|
||||
class Pry
|
||||
module Helpers
|
||||
module CommandHelpers
|
||||
include OptionsHelpers
|
||||
|
||||
extend self
|
||||
|
||||
# Open a temp file and yield it to the block, closing it after
|
||||
# @return [String] The path of the temp file
|
||||
def temp_file(ext = '.rb')
|
||||
file = Tempfile.new(['pry', ext])
|
||||
yield(file)
|
||||
ensure
|
||||
file.close(true)
|
||||
end
|
||||
|
||||
def internal_binding?(context)
|
||||
method_name = context.eval("::Kernel.__method__").to_s
|
||||
# class_eval is here because of http://jira.codehaus.org/browse/JRUBY-6753
|
||||
%w[__binding__ __pry__ class_eval].include?(method_name)
|
||||
# TODO: codehaus is dead, there was no test for this and the
|
||||
# description for the commit doesn't exist. Probably a candidate for
|
||||
# removal so we have a chance to introduce a regression and document it
|
||||
# properly.
|
||||
end
|
||||
|
||||
def get_method_or_raise(method_name, context, opts = {})
|
||||
method = Pry::Method.from_str(method_name, context, opts)
|
||||
if !method && method_name
|
||||
raise Pry::MethodNotFound, "method '#{method_name}' could not be found."
|
||||
end
|
||||
|
||||
(opts[:super] || 0).times do
|
||||
if method.super
|
||||
method = method.super
|
||||
else
|
||||
raise Pry::MethodNotFound,
|
||||
"'#{method.name_with_owner}' has no super method"
|
||||
end
|
||||
end
|
||||
|
||||
if !method || (!method_name && internal_binding?(context))
|
||||
raise Pry::MethodNotFound,
|
||||
'no method name given, and context is not a method'
|
||||
end
|
||||
|
||||
set_file_and_dir_locals(method.source_file)
|
||||
method
|
||||
end
|
||||
|
||||
# Remove any common leading whitespace from every line in `text`. This
|
||||
# can be used to make a HEREDOC line up with the left margin, without
|
||||
# sacrificing the indentation level of the source code.
|
||||
#
|
||||
# @example
|
||||
# opt.banner(unindent(<<-USAGE))
|
||||
# Lorem ipsum dolor sit amet, consectetur adipisicing elit,
|
||||
# sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
# "Ut enim ad minim veniam."
|
||||
# USAGE
|
||||
#
|
||||
# @param [String] dirty_text The text from which to remove indentation
|
||||
# @return [String] the text with indentation stripped
|
||||
def unindent(dirty_text, left_padding = 0)
|
||||
text = dirty_text.sub(/\A[ \t]+\z/, '') # Empty blank lines.
|
||||
|
||||
# Find the longest common whitespace to all indented lines. Ignore lines
|
||||
# containing just -- or ++ as these seem to be used by comment authors
|
||||
# as delimiters.
|
||||
scanned_text = text.scan(/^[ \t]*(?!--\n|\+\+\n)(?=[^ \t\n])/)
|
||||
margin = scanned_text.inject do |current_margin, next_indent|
|
||||
if next_indent.start_with?(current_margin)
|
||||
current_margin
|
||||
elsif current_margin.start_with?(next_indent)
|
||||
next_indent
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
text.gsub(/^#{margin}/, ' ' * left_padding)
|
||||
end
|
||||
|
||||
# Restrict a string to the given range of lines (1-indexed)
|
||||
# @param [String] content The string.
|
||||
# @param [Range, Integer] lines The line(s) to restrict it to.
|
||||
# @return [String] The resulting string.
|
||||
def restrict_to_lines(content, lines)
|
||||
line_range = one_index_range_or_number(lines)
|
||||
Array(content.lines.to_a[line_range]).join
|
||||
end
|
||||
|
||||
def one_index_number(line_number)
|
||||
line_number > 0 ? line_number - 1 : line_number
|
||||
end
|
||||
|
||||
# convert a 1-index range to a 0-indexed one
|
||||
def one_index_range(range)
|
||||
Range.new(one_index_number(range.begin), one_index_number(range.end))
|
||||
end
|
||||
|
||||
def one_index_range_or_number(range_or_number)
|
||||
case range_or_number
|
||||
when Range
|
||||
one_index_range(range_or_number)
|
||||
else
|
||||
one_index_number(range_or_number)
|
||||
end
|
||||
end
|
||||
|
||||
def absolute_index_number(line_number, array_length)
|
||||
if line_number >= 0
|
||||
line_number
|
||||
else
|
||||
[array_length + line_number, 0].max
|
||||
end
|
||||
end
|
||||
|
||||
def absolute_index_range(range_or_number, array_length)
|
||||
case range_or_number
|
||||
when Range
|
||||
a = absolute_index_number(range_or_number.begin, array_length)
|
||||
b = absolute_index_number(range_or_number.end, array_length)
|
||||
else
|
||||
a = b = absolute_index_number(range_or_number, array_length)
|
||||
end
|
||||
|
||||
Range.new(a, b)
|
||||
end
|
||||
|
||||
def set_file_and_dir_locals(file_name, pry = pry_instance, ctx = target)
|
||||
return if !ctx || !file_name
|
||||
|
||||
pry.last_file = File.expand_path(file_name)
|
||||
pry.inject_local("_file_", pry.last_file, ctx)
|
||||
|
||||
pry.last_dir = File.dirname(pry.last_file)
|
||||
pry.inject_local("_dir_", pry.last_dir, ctx)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,84 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
module Helpers
|
||||
# This class contains methods useful for extracting
|
||||
# documentation from methods and classes.
|
||||
module DocumentationHelpers
|
||||
YARD_TAGS = %w[
|
||||
param return option yield attr attr_reader attr_writer deprecate example
|
||||
raise
|
||||
].freeze
|
||||
|
||||
module_function
|
||||
|
||||
def process_rdoc(comment)
|
||||
comment = comment.dup
|
||||
last_match_ruby = proc do
|
||||
SyntaxHighlighter.highlight(Regexp.last_match(1))
|
||||
end
|
||||
|
||||
comment.gsub(%r{<code>(?:\s*\n)?(.*?)\s*</code>}m, &last_match_ruby)
|
||||
.gsub(%r{<em>(?:\s*\n)?(.*?)\s*</em>}m) { "\e[1m#{Regexp.last_match(1)}\e[0m" }
|
||||
.gsub(%r{<i>(?:\s*\n)?(.*?)\s*</i>}m) { "\e[1m#{Regexp.last_match(1)}\e[0m" }
|
||||
.gsub(%r{<tt>(?:\s*\n)?(.*?)\s*</tt>}m, &last_match_ruby)
|
||||
.gsub(/\B\+(\w+?)\+\B/) { "\e[32m#{Regexp.last_match(1)}\e[0m" }
|
||||
.gsub(/((?:^[ \t]+(?:(?!.+\e\[)).+(?:\n+|\Z))+)/, &last_match_ruby)
|
||||
.gsub(/`(?:\s*\n)?([^\e]*?)\s*`/) { "`#{last_match_ruby.call}`" }
|
||||
end
|
||||
|
||||
def process_yardoc_tag(comment, tag)
|
||||
in_tag_block = nil
|
||||
comment.lines.map do |v|
|
||||
if in_tag_block && v !~ /^\S/
|
||||
Pry::Helpers::Text.strip_color Pry::Helpers::Text.strip_color(v)
|
||||
elsif in_tag_block
|
||||
in_tag_block = false
|
||||
v
|
||||
else
|
||||
in_tag_block = true if v =~ /^@#{tag}/
|
||||
v
|
||||
end
|
||||
end.join
|
||||
end
|
||||
|
||||
def process_yardoc(comment)
|
||||
(YARD_TAGS - %w[example])
|
||||
.inject(comment) { |a, v| process_yardoc_tag(a, v) }
|
||||
.gsub(/^@(#{YARD_TAGS.join("|")})/) { "\e[33m#{Regexp.last_match(1)}\e[0m" }
|
||||
end
|
||||
|
||||
def process_comment_markup(comment)
|
||||
process_yardoc process_rdoc(comment)
|
||||
end
|
||||
|
||||
# @param [String] code
|
||||
# @return [String]
|
||||
def strip_comments_from_c_code(code)
|
||||
code.sub(%r{\A\s*/\*.*?\*/\s*}m, '')
|
||||
end
|
||||
|
||||
# Given a string that makes up a comment in a source-code file parse out the content
|
||||
# that the user is intended to read. (i.e. without leading indentation, #-characters
|
||||
# or shebangs)
|
||||
#
|
||||
# @param [String] comment
|
||||
# @return [String]
|
||||
def get_comment_content(comment)
|
||||
comment = comment.dup
|
||||
# Remove #!/usr/bin/ruby
|
||||
comment.gsub!(/\A\#!.*$/, '')
|
||||
# Remove leading empty comment lines
|
||||
comment.gsub!(/\A\#+?$/, '')
|
||||
comment.gsub!(/^\s*#/, '')
|
||||
strip_leading_whitespace(comment)
|
||||
end
|
||||
|
||||
# @param [String] text
|
||||
# @return [String]
|
||||
def strip_leading_whitespace(text)
|
||||
Pry::Helpers::CommandHelpers.unindent(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
module Helpers
|
||||
module OptionsHelpers
|
||||
module_function
|
||||
|
||||
# Add method options to the Pry::Slop instance
|
||||
def method_options(opt)
|
||||
@method_target = target
|
||||
opt.on :M, "instance-methods", "Operate on instance methods."
|
||||
opt.on :m, :methods, "Operate on methods."
|
||||
opt.on :s, :super, "Select the 'super' method. Can be repeated to " \
|
||||
"traverse the ancestors.", as: :count
|
||||
opt.on(
|
||||
:c, :context, "Select object context to run under.", argument: true
|
||||
) do |context|
|
||||
@method_target = Pry.binding_for(target.eval(context))
|
||||
end
|
||||
end
|
||||
|
||||
# Get the method object parsed by the slop instance
|
||||
def method_object
|
||||
@method_object ||= get_method_or_raise(
|
||||
args.empty? ? nil : args.join(" "),
|
||||
@method_target,
|
||||
super: opts[:super],
|
||||
instance: opts.present?(:'instance-methods') && !opts.present?(:methods),
|
||||
methods: opts.present?(:methods) && !opts.present?(:'instance-methods')
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rbconfig'
|
||||
|
||||
class Pry
|
||||
module Helpers
|
||||
# Contains methods for querying the platform that Pry is running on
|
||||
# @api public
|
||||
# @since v0.12.0
|
||||
module Platform
|
||||
# @return [Boolean]
|
||||
def self.mac_osx?
|
||||
!!(RbConfig::CONFIG['host_os'] =~ /\Adarwin/i)
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def self.linux?
|
||||
!!(RbConfig::CONFIG['host_os'] =~ /linux/i)
|
||||
end
|
||||
|
||||
# @return [Boolean] true when Pry is running on Windows with ANSI support,
|
||||
# false otherwise
|
||||
def self.windows?
|
||||
!!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/)
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def self.windows_ansi?
|
||||
return false unless windows?
|
||||
|
||||
!!(defined?(Win32::Console) || Pry::Env['ANSICON'] || mri?)
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def self.jruby?
|
||||
RbConfig::CONFIG['ruby_install_name'] == 'jruby'
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def self.jruby_19?
|
||||
jruby? && RbConfig::CONFIG['ruby_version'] == '1.9'
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def self.mri?
|
||||
RbConfig::CONFIG['ruby_install_name'] == 'ruby'
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def self.mri_2?
|
||||
mri? && RUBY_VERSION.start_with?('2')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,121 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
module Helpers
|
||||
def self.tablify_or_one_line(heading, things, pry_instance = Pry.new)
|
||||
plain_heading = Pry::Helpers::Text.strip_color(heading)
|
||||
attempt = Table.new(things, column_count: things.size)
|
||||
if attempt.fits_on_line?(pry_instance.output.width - plain_heading.size - 2)
|
||||
"#{heading}: #{attempt}\n"
|
||||
else
|
||||
content = tablify_to_screen_width(things, { indent: ' ' }, pry_instance)
|
||||
"#{heading}: \n#{content}\n"
|
||||
end
|
||||
end
|
||||
|
||||
def self.tablify_to_screen_width(things, options, pry_instance = Pry.new)
|
||||
options ||= {}
|
||||
things = things.compact
|
||||
if (indent = options[:indent])
|
||||
usable_width = pry_instance.output.width - indent.size
|
||||
tablify(things, usable_width, pry_instance).to_s.gsub(/^/, indent)
|
||||
else
|
||||
tablify(things, pry_instance.output.width, pry_instance).to_s
|
||||
end
|
||||
end
|
||||
|
||||
def self.tablify(things, line_length, pry_instance = Pry.new)
|
||||
table = Table.new(things, { column_count: things.size }, pry_instance)
|
||||
until (table.column_count == 1) || table.fits_on_line?(line_length)
|
||||
table.column_count -= 1
|
||||
end
|
||||
table
|
||||
end
|
||||
|
||||
class Table
|
||||
attr_reader :items, :column_count
|
||||
def initialize(items, args, pry_instance = Pry.new)
|
||||
@column_count = args[:column_count]
|
||||
@config = pry_instance.config
|
||||
self.items = items
|
||||
end
|
||||
|
||||
def to_s
|
||||
rows_to_s.join("\n")
|
||||
end
|
||||
|
||||
def rows_to_s(style = :color_on)
|
||||
widths = columns.map { |e| _max_width(e) }
|
||||
@rows_without_colors.map do |r|
|
||||
padded = []
|
||||
r.each_with_index do |e, i|
|
||||
next unless e
|
||||
|
||||
item = e.ljust(widths[i])
|
||||
item.sub! e, _recall_color_for(e) if style == :color_on
|
||||
padded << item
|
||||
end
|
||||
padded.join(@config.ls.separator)
|
||||
end
|
||||
end
|
||||
|
||||
def items=(items)
|
||||
@items = items
|
||||
_rebuild_colorless_cache
|
||||
_recolumn
|
||||
end
|
||||
|
||||
def column_count=(count)
|
||||
@column_count = count
|
||||
_recolumn
|
||||
end
|
||||
|
||||
def fits_on_line?(line_length)
|
||||
_max_width(rows_to_s(:no_color)) <= line_length
|
||||
end
|
||||
|
||||
def columns
|
||||
@rows_without_colors.transpose
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
items == other.to_a
|
||||
end
|
||||
|
||||
def to_a
|
||||
items.to_a
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _max_width(things)
|
||||
things.compact.map(&:size).max || 0
|
||||
end
|
||||
|
||||
def _rebuild_colorless_cache
|
||||
@colorless_cache = {}
|
||||
@plain_items = []
|
||||
items.map do |e|
|
||||
plain = Pry::Helpers::Text.strip_color(e)
|
||||
@colorless_cache[plain] = e
|
||||
@plain_items << plain
|
||||
end
|
||||
end
|
||||
|
||||
def _recolumn
|
||||
@rows_without_colors = []
|
||||
return if items.size.zero?
|
||||
|
||||
row_count = (items.size.to_f / column_count).ceil
|
||||
row_count.times do |i|
|
||||
row_indices = (0...column_count).map { |e| row_count * e + i }
|
||||
@rows_without_colors << row_indices.map { |e| @plain_items[e] }
|
||||
end
|
||||
end
|
||||
|
||||
def _recall_color_for(thing)
|
||||
@colorless_cache[thing]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,118 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Pry
|
||||
module Helpers
|
||||
# The methods defined on {Text} are available to custom commands via
|
||||
# {Pry::Command#text}.
|
||||
module Text
|
||||
extend self
|
||||
|
||||
COLORS = {
|
||||
"black" => 0,
|
||||
"red" => 1,
|
||||
"green" => 2,
|
||||
"yellow" => 3,
|
||||
"blue" => 4,
|
||||
"purple" => 5,
|
||||
"magenta" => 5,
|
||||
"cyan" => 6,
|
||||
"white" => 7
|
||||
}.freeze
|
||||
|
||||
COLORS.each_pair do |color, value|
|
||||
define_method color do |text|
|
||||
"\033[0;#{30 + value}m#{text}\033[0m"
|
||||
end
|
||||
|
||||
define_method "bright_#{color}" do |text|
|
||||
"\033[1;#{30 + value}m#{text}\033[0m"
|
||||
end
|
||||
|
||||
COLORS.each_pair do |bg_color, bg_value|
|
||||
define_method "#{color}_on_#{bg_color}" do |text|
|
||||
"\033[0;#{30 + value};#{40 + bg_value}m#{text}\033[0m"
|
||||
end
|
||||
|
||||
define_method "bright_#{color}_on_#{bg_color}" do |text|
|
||||
"\033[1;#{30 + value};#{40 + bg_value}m#{text}\033[0m"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Remove any color codes from _text_.
|
||||
#
|
||||
# @param [String, #to_s] text
|
||||
# @return [String] _text_ stripped of any color codes.
|
||||
def strip_color(text)
|
||||
text.to_s.gsub(/(\001)?(\e\[(\d[;\d]?)*m)(\002)?/, '')
|
||||
end
|
||||
|
||||
# Returns _text_ as bold text for use on a terminal.
|
||||
#
|
||||
# @param [String, #to_s] text
|
||||
# @return [String] _text_
|
||||
def bold(text)
|
||||
"\e[1m#{text}\e[0m"
|
||||
end
|
||||
|
||||
# Returns `text` in the default foreground colour.
|
||||
# Use this instead of "black" or "white" when you mean absence of colour.
|
||||
#
|
||||
# @param [String, #to_s] text
|
||||
# @return [String]
|
||||
def default(text)
|
||||
text.to_s
|
||||
end
|
||||
|
||||
#
|
||||
# @yield
|
||||
# Yields a block with color turned off.
|
||||
#
|
||||
# @return [void]
|
||||
#
|
||||
def no_color
|
||||
boolean = Pry.config.color
|
||||
Pry.config.color = false
|
||||
yield
|
||||
ensure
|
||||
Pry.config.color = boolean
|
||||
end
|
||||
|
||||
#
|
||||
# @yield
|
||||
# Yields a block with paging turned off.
|
||||
#
|
||||
# @return [void]
|
||||
#
|
||||
def no_pager
|
||||
boolean = Pry.config.pager
|
||||
Pry.config.pager = false
|
||||
yield
|
||||
ensure
|
||||
Pry.config.pager = boolean
|
||||
end
|
||||
|
||||
# Returns _text_ in a numbered list, beginning at _offset_.
|
||||
#
|
||||
# @param [#each_line] text
|
||||
# @param [Fixnum] offset
|
||||
# @return [String]
|
||||
def with_line_numbers(text, offset, color = :blue)
|
||||
lines = text.each_line.to_a
|
||||
max_width = (offset + lines.count).to_s.length
|
||||
lines.each_with_index.map do |line, index|
|
||||
adjusted_index = (index + offset).to_s.rjust(max_width)
|
||||
"#{send(color, adjusted_index)}: #{line}"
|
||||
end.join
|
||||
end
|
||||
|
||||
# Returns _text_ indented by _chars_ spaces.
|
||||
#
|
||||
# @param [String] text
|
||||
# @param [Fixnum] chars
|
||||
def indent(text, chars)
|
||||
text.lines.map { |l| "#{' ' * chars}#{l}" }.join
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user