1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

Moved helpers to their own namespaces, got rid of CommandBase

This commit is contained in:
Mon ouïe 2011-04-25 22:58:06 +02:00
parent 1dccc80f2d
commit b0f8b72da0
9 changed files with 344 additions and 548 deletions

View file

@ -6,6 +6,9 @@ require 'shellwords'
require "readline"
require "stringio"
require "coderay"
require "optparse"
require "slop"
require "rubygems/dependency_installer"
if RUBY_PLATFORM =~ /mswin/ || RUBY_PLATFORM =~ /mingw/
begin
@ -19,7 +22,7 @@ end
require "pry/version"
require "pry/hooks"
require "pry/print"
require "pry/command_base" # to be removed
require "pry/helpers"
require "pry/command_set"
require "pry/commands"
require "pry/command_context"

View file

@ -1,202 +0,0 @@
require 'rubygems/dependency_installer'
require "pry/command_base_helpers"
class Pry
# Basic command functionality. All user-defined commands must
# inherit from this class. It provides the `command` method.
class CommandBase
class << self
include CommandBaseHelpers
attr_accessor :commands
attr_accessor :opts, :output, :target
# private because we want to force function style invocation. We require
# that the location where the block is defined has the `opts`
# method in scope.
private
# Defines a new Pry command.
# @param [String, Array] names The name of the command (or array of
# command name aliases).
# @param [String] description A description of the command.
# @param [Hash] options The optional configuration parameters.
# @option options [Boolean] :keep_retval Whether or not to use return value
# of the block for return of `command` or just to return `nil`
# (the default).
# @yield The action to perform. The parameters in the block
# determines the parameters the command will receive. All
# parameters passed into the block will be strings. Successive
# command parameters are separated by whitespace at the Pry prompt.
# @example
# class MyCommands < Pry::CommandBase
# command "greet", "Greet somebody" do |name|
# puts "Good afternoon #{name.capitalize}!"
# end
# end
#
# # From pry:
# # pry(main)> _pry_.commands = MyCommands
# # pry(main)> greet john
# # Good afternoon John!
# # pry(main)> help greet
# # Greet somebody
def command(names, description="No description.", options={}, &block)
options = {
:keep_retval => false,
:requires_gem => nil
}.merge!(options)
@commands ||= {}
if command_dependencies_met?(options)
Array(names).each do |name|
commands[name] = {
:description => description,
:action => block,
:keep_retval => options[:keep_retval]
}
end
else
create_command_stub(names, description, options, block)
end
end
# Delete a command or an array of commands.
# Useful when inheriting from another command set and pruning
# those commands down to the ones you want.
# @param [Array<String>] names The command name or array
# of command names you want to delete
# @example Deleteing inherited commands
# class MyCommands < Pry::Commands
# delete "show_method", "show_imethod", "show_doc", "show_idoc"
# end
# Pry.commands = MyCommands
def delete(*names)
names.each { |name| commands.delete(name) }
end
# Execute a command (this enables commands to call other commands).
# @param [String] name The command to execute
# @param [Array] args The parameters to pass to the command.
# @example Wrap one command with another
# class MyCommands < Pry::Commands
# command "ls2" do
# output.puts "before ls"
# run "ls"
# output.puts "after ls"
# end
# end
def run(name, *args)
command_processor = CommandProcessor.new(target.eval('_pry_'))
if command_processor.system_command?(name)
command_processor.execute_system_command("#{name} #{args.join(' ')}", target)
else
raise "#{name.inspect} is not a valid pry command." unless opts[:commands].include? name
action = opts[:commands][name][:action]
instance_exec(*args, &action)
end
end
# Import commands from another command object.
# @param [Pry::CommandBase] klass The class to import from (must
# be a subclass of `Pry::CommandBase`)
# @param [Array<String>] names The commands to import.
# @example
# class MyCommands < Pry::CommandBase
# import_from Pry::Commands, "ls", "show_method", "cd"
# end
def import_from(klass, *names)
imported_hash = Hash[klass.commands.select { |k, v| names.include?(k) }]
commands.merge!(imported_hash)
end
# Create an alias for a command.
# @param [String] new_command The alias name.
# @param [String] orig_command The original command name.
# @param [String] desc The optional description.
# @example
# class MyCommands < Pry::CommandBase
# alias_command "help_alias", "help"
# end
def alias_command(new_command_name, orig_command_name, desc=nil)
commands[new_command_name] = commands[orig_command_name].dup
commands[new_command_name][:description] = desc if desc
end
# Set the description for a command (replacing the old
# description.)
# @param [String] name The command name.
# @param [String] description The command description.
# @example
# class MyCommands < Pry::CommandBase
# desc "help", "help description"
# end
def desc(name, description)
commands[name][:description] = description
end
end
command "help", "This menu." do |cmd|
command_info = opts[:commands]
if !cmd
output.puts
help_text = heading("Command List:") + "\n"
command_info.each do |k, data|
if !data[:stub_info]
help_text << ("#{k}".ljust(18) + data[:description] + "\n") if !data[:description].empty?
else
help_text << (bold("#{k}".ljust(18) + data[:description] + "\n")) if !data[:description].empty?
end
end
stagger_output(help_text)
else
if command_info[cmd]
output.puts command_info[cmd][:description]
else
output.puts "No info for command: #{cmd}"
end
end
end
command "install", "Install a disabled command." do |name|
stub_info = commands[name][:stub_info]
if !stub_info
output.puts "Not a command stub. Nothing to do."
next
end
output.puts "Attempting to install `#{name}` command..."
gems_to_install = Array(stub_info[:requires_gem])
gem_install_failed = false
gems_to_install.each do |g|
next if gem_installed?(g)
output.puts "Installing `#{g}` gem..."
begin
Gem::DependencyInstaller.new.install(g)
rescue Gem::GemNotFoundException
output.puts "Required Gem: `#{g}` not found. Aborting command installation."
gem_install_failed = true
next
end
end
next if gem_install_failed
Gem.refresh
load "#{File.dirname(__FILE__)}/commands.rb"
output.puts "Installation of `#{name}` successful! Type `help #{name}` for information"
end
# Ensures that commands can be inherited
def self.inherited(klass)
klass.commands = commands.dup
end
end
end

View file

@ -7,7 +7,7 @@ class Pry
attr_accessor :opts
attr_accessor :commands
include Pry::CommandBase::CommandBaseHelpers
include Pry::CommandHelpers
include Pry::Helpers::BaseHelpers
include Pry::Helpers::CommandHelpers
end
end

View file

@ -1,327 +0,0 @@
class Pry
module CommandHelpers
private
def try_to_load_pry_doc
# YARD crashes on rbx, so do not require it
if !Object.const_defined?(:RUBY_ENGINE) || RUBY_ENGINE !~ /rbx/
require "pry-doc"
end
rescue LoadError
end
def meth_name_from_binding(b)
meth_name = b.eval('__method__')
if [:__script__, nil, :__binding__, :__binding_impl__].include?(meth_name)
nil
else
meth_name
end
end
def set_file_and_dir_locals(file_name)
return if !target
$_file_temp = File.expand_path(file_name)
$_dir_temp = File.dirname($_file_temp)
target.eval("_file_ = $_file_temp")
target.eval("_dir_ = $_dir_temp")
end
def add_line_numbers(lines, start_line)
line_array = lines.each_line.to_a
line_array.each_with_index.map do |line, idx|
adjusted_index = idx + start_line
if Pry.color
cindex = CodeRay.scan("#{adjusted_index}", :ruby).term
"#{cindex}: #{line}"
else
"#{idx}: #{line}"
end
end.join
end
# if start_line is not false then add line numbers starting with start_line
def render_output(should_flood, start_line, doc)
if start_line
doc = add_line_numbers(doc, start_line)
if should_flood
output.puts doc
else
stagger_output(doc)
end
end
end
def editor_with_start_line(line_number)
case Pry.editor
when /^[gm]?vi/, /^emacs/, /^nano/, /^pico/, /^gedit/, /^kate/
"#{Pry.editor} +#{line_number}"
when /^mate/
"#{Pry.editor} -l#{line_number}"
else
if RUBY_PLATFORM =~ /mswin|mingw/
Pry.editor
else
"#{Pry.editor} +#{line_number}"
end
end
end
def is_a_dynamically_defined_method?(meth)
file, _ = meth.source_location
!!(file =~ /(\(.*\))|<.*>/)
end
def check_for_dynamically_defined_method(meth)
if is_a_dynamically_defined_method?(meth)
raise "Cannot retrieve source for dynamically defined method."
end
end
def check_for_dynamically_defined_method(meth)
file, _ = meth.source_location
if file =~ /(\(.*\))|<.*>/
raise "Cannot retrieve source for dynamically defined method."
end
end
def remove_first_word(text)
text.split.drop(1).join(' ')
end
# turn off color for duration of block
def no_color(&block)
old_color_state = Pry.color
Pry.color = false
yield
ensure
Pry.color = old_color_state
end
def code_and_code_type_for(meth)
case code_type = code_type_for(meth)
when nil
return nil
when :c
code = Pry::MethodInfo.info_for(meth).source
code = strip_comments_from_c_code(code)
when :ruby
code = strip_leading_whitespace(meth.source)
set_file_and_dir_locals(meth.source_location.first)
end
[code, code_type]
end
def doc_and_code_type_for(meth)
case code_type = code_type_for(meth)
when nil
return nil
when :c
doc = Pry::MethodInfo.info_for(meth).docstring
when :ruby
doc = meth.comment
doc = strip_leading_hash_and_whitespace_from_ruby_comments(doc)
set_file_and_dir_locals(meth.source_location.first)
end
[doc, code_type]
end
def get_method_object(meth_name, target, options)
if !meth_name
return nil
end
if options[:M]
target.eval("instance_method(:#{meth_name})")
elsif options[:m]
target.eval("method(:#{meth_name})")
else
begin
target.eval("instance_method(:#{meth_name})")
rescue
begin
target.eval("method(:#{meth_name})")
rescue
return nil
end
end
end
end
def make_header(meth, code_type, content)
num_lines = "Number of lines: #{bold(content.each_line.count.to_s)}"
case code_type
when :ruby
file, line = meth.source_location
"\n#{bold('From:')} #{file} @ line #{line}:\n#{num_lines}\n\n"
else
file = Pry::MethodInfo.info_for(meth).file
"\n#{bold('From:')} #{file} in Ruby Core (C Method):\n#{num_lines}\n\n"
end
end
def is_a_c_method?(meth)
meth.source_location.nil?
end
def should_use_pry_doc?(meth)
Pry.has_pry_doc && is_a_c_method?(meth)
end
def code_type_for(meth)
# only C methods
if should_use_pry_doc?(meth)
info = Pry::MethodInfo.info_for(meth)
if info && info.source
code_type = :c
else
output.puts "Cannot find C method: #{meth.name}"
code_type = nil
end
else
if is_a_c_method?(meth)
output.puts "Cannot locate this method: #{meth.name}. Try `gem install pry-doc` to get access to Ruby Core documentation."
code_type = nil
else
check_for_dynamically_defined_method(meth)
code_type = :ruby
end
end
code_type
end
def file_map
{
[".c", ".h"] => :c,
[".cpp", ".hpp", ".cc", ".h", "cxx"] => :cpp,
[".rb", "Rakefile", ".irbrc", ".gemspec", ".pryrc"] => :ruby,
".py" => :python,
".diff" => :diff,
".css" => :css,
".html" => :html,
[".yaml", ".yml"] => :yaml,
".xml" => :xml,
".php" => :php,
".js" => :javascript,
".java" => :java,
".rhtml" => :rhtml,
".json" => :json
}
end
def syntax_highlight_by_file_type_or_specified(contents, file_name, file_type)
_, language_detected = file_map.find do |k, v|
Array(k).any? do |matcher|
matcher == File.extname(file_name) || matcher == File.basename(file_name)
end
end
language_detected = file_type if file_type
CodeRay.scan(contents, language_detected).term
end
# convert negative line numbers to positive by wrapping around
# last line (as per array indexing with negative numbers)
def normalized_line_number(line_number, total_lines)
line_number < 0 ? line_number + total_lines : line_number
end
# returns the file content between the lines and the normalized
# start and end line numbers.
def read_between_the_lines(file_name, start_line, end_line)
content = File.read(File.expand_path(file_name))
lines_array = content.each_line.to_a
[lines_array[start_line..end_line].join, normalized_line_number(start_line, lines_array.size),
normalized_line_number(end_line, lines_array.size)]
end
# documentation related helpers
def strip_color_codes(str)
str.gsub(/\e\[.*?(\d)+m/, '')
end
def process_rdoc(comment, code_type)
comment = comment.dup
comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { Pry.color ? "\e[34m#{$1}\e[0m" : $1 }.
gsub(/\B\+(\w*?)\+\B/) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
gsub(/`(?:\s*\n)?(.*?)\s*`/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }
end
def process_yardoc_tag(comment, tag)
in_tag_block = nil
output = comment.lines.map do |v|
if in_tag_block && v !~ /^\S/
strip_color_codes(strip_color_codes(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 = ["param", "return", "option", "yield", "attr", "attr_reader", "attr_writer",
"deprecate", "example"]
(yard_tags - ["example"]).inject(comment) { |a, v| process_yardoc_tag(a, v) }.
gsub(/^@(#{yard_tags.join("|")})/) { Pry.color ? "\e[33m#{$1}\e[0m": $1 }
end
def process_comment_markup(comment, code_type)
process_yardoc process_rdoc(comment, code_type)
end
# strip leading whitespace but preserve indentation
def strip_leading_whitespace(text)
return text if text.empty?
leading_spaces = text.lines.first[/^(\s+)/, 1]
text.gsub(/^#{leading_spaces}/, '')
end
def strip_leading_hash_and_whitespace_from_ruby_comments(comment)
comment = comment.dup
comment.gsub!(/\A\#+?$/, '')
comment.gsub!(/^\s*#/, '')
strip_leading_whitespace(comment)
end
def strip_comments_from_c_code(code)
code.sub /\A\s*\/\*.*?\*\/\s*/m, ''
end
def prompt(message, options="Yn")
opts = options.scan(/./)
optstring = opts.join("/") # case maintained
defaults = opts.select{|o| o.upcase == o }
opts = opts.map{|o| o.downcase}
raise "Error: Too many default values for the prompt: #{default.inspect}" if defaults.size > 1
default = defaults.first
loop do
response = Pry.input.readline("#{message} (#{optstring}) ").downcase
case response
when *opts
return response
when ""
return default.downcase
else
output.puts " |_ Invalid option: #{response.inspect}. Try again."
end
end
end
end
end

View file

@ -19,7 +19,7 @@ class Pry
end
end
include Pry::CommandBase::CommandBaseHelpers
include Pry::Helpers::BaseHelpers
attr_reader :commands
attr_reader :name

View file

@ -1,17 +1,8 @@
require "optparse"
require "method_source"
require 'slop'
require 'rubygems/dependency_installer'
require "pry/command_base"
require "pry/pry_instance"
require "pry/command_helpers"
class Pry
# Default commands used by Pry.
Commands = Pry::CommandSet.new :default do
extend CommandHelpers
try_to_load_pry_doc
Helpers::CommandHelpers.try_to_load_pry_doc
command "!", "Clear the input buffer. Useful if the parsing process goes wrong and you get stuck in the read loop." do
output.puts "Input buffer cleared!"

2
lib/pry/helpers.rb Normal file
View file

@ -0,0 +1,2 @@
require "pry/helpers/base_helpers"
require "pry/helpers/command_helpers"

View file

@ -1,8 +1,8 @@
class Pry
class CommandBase
module CommandBaseHelpers
module Helpers
private
module BaseHelpers
module_function
def gem_installed?(gem_name)
require 'rubygems'
@ -237,5 +237,3 @@ class Pry
end
end
end

View file

@ -0,0 +1,331 @@
class Pry
module Helpers
module CommandHelpers
module_function
def try_to_load_pry_doc
# YARD crashes on rbx, so do not require it
if !Object.const_defined?(:RUBY_ENGINE) || RUBY_ENGINE !~ /rbx/
require "pry-doc"
end
rescue LoadError
end
def meth_name_from_binding(b)
meth_name = b.eval('__method__')
if [:__script__, nil, :__binding__, :__binding_impl__].include?(meth_name)
nil
else
meth_name
end
end
def set_file_and_dir_locals(file_name)
return if !target
$_file_temp = File.expand_path(file_name)
$_dir_temp = File.dirname($_file_temp)
target.eval("_file_ = $_file_temp")
target.eval("_dir_ = $_dir_temp")
end
def add_line_numbers(lines, start_line)
line_array = lines.each_line.to_a
line_array.each_with_index.map do |line, idx|
adjusted_index = idx + start_line
if Pry.color
cindex = CodeRay.scan("#{adjusted_index}", :ruby).term
"#{cindex}: #{line}"
else
"#{idx}: #{line}"
end
end.join
end
# if start_line is not false then add line numbers starting with start_line
def render_output(should_flood, start_line, doc)
if start_line
doc = add_line_numbers(doc, start_line)
if should_flood
output.puts doc
else
stagger_output(doc)
end
end
end
def editor_with_start_line(line_number)
case Pry.editor
when /^[gm]?vi/, /^emacs/, /^nano/, /^pico/, /^gedit/, /^kate/
"#{Pry.editor} +#{line_number}"
when /^mate/
"#{Pry.editor} -l#{line_number}"
else
if RUBY_PLATFORM =~ /mswin|mingw/
Pry.editor
else
"#{Pry.editor} +#{line_number}"
end
end
end
def is_a_dynamically_defined_method?(meth)
file, _ = meth.source_location
!!(file =~ /(\(.*\))|<.*>/)
end
def check_for_dynamically_defined_method(meth)
if is_a_dynamically_defined_method?(meth)
raise "Cannot retrieve source for dynamically defined method."
end
end
def check_for_dynamically_defined_method(meth)
file, _ = meth.source_location
if file =~ /(\(.*\))|<.*>/
raise "Cannot retrieve source for dynamically defined method."
end
end
def remove_first_word(text)
text.split.drop(1).join(' ')
end
# turn off color for duration of block
def no_color(&block)
old_color_state = Pry.color
Pry.color = false
yield
ensure
Pry.color = old_color_state
end
def code_and_code_type_for(meth)
case code_type = code_type_for(meth)
when nil
return nil
when :c
code = Pry::MethodInfo.info_for(meth).source
code = strip_comments_from_c_code(code)
when :ruby
code = strip_leading_whitespace(meth.source)
set_file_and_dir_locals(meth.source_location.first)
end
[code, code_type]
end
def doc_and_code_type_for(meth)
case code_type = code_type_for(meth)
when nil
return nil
when :c
doc = Pry::MethodInfo.info_for(meth).docstring
when :ruby
doc = meth.comment
doc = strip_leading_hash_and_whitespace_from_ruby_comments(doc)
set_file_and_dir_locals(meth.source_location.first)
end
[doc, code_type]
end
def get_method_object(meth_name, target, options)
if !meth_name
return nil
end
if options[:M]
target.eval("instance_method(:#{meth_name})")
elsif options[:m]
target.eval("method(:#{meth_name})")
else
begin
target.eval("instance_method(:#{meth_name})")
rescue
begin
target.eval("method(:#{meth_name})")
rescue
return nil
end
end
end
end
def make_header(meth, code_type, content)
num_lines = "Number of lines: #{bold(content.each_line.count.to_s)}"
case code_type
when :ruby
file, line = meth.source_location
"\n#{bold('From:')} #{file} @ line #{line}:\n#{num_lines}\n\n"
else
file = Pry::MethodInfo.info_for(meth).file
"\n#{bold('From:')} #{file} in Ruby Core (C Method):\n#{num_lines}\n\n"
end
end
def is_a_c_method?(meth)
meth.source_location.nil?
end
def should_use_pry_doc?(meth)
Pry.has_pry_doc && is_a_c_method?(meth)
end
def code_type_for(meth)
# only C methods
if should_use_pry_doc?(meth)
info = Pry::MethodInfo.info_for(meth)
if info && info.source
code_type = :c
else
output.puts "Cannot find C method: #{meth.name}"
code_type = nil
end
else
if is_a_c_method?(meth)
output.puts "Cannot locate this method: #{meth.name}. Try `gem install pry-doc` to get access to Ruby Core documentation."
code_type = nil
else
check_for_dynamically_defined_method(meth)
code_type = :ruby
end
end
code_type
end
def file_map
{
[".c", ".h"] => :c,
[".cpp", ".hpp", ".cc", ".h", "cxx"] => :cpp,
[".rb", "Rakefile", ".irbrc", ".gemspec", ".pryrc"] => :ruby,
".py" => :python,
".diff" => :diff,
".css" => :css,
".html" => :html,
[".yaml", ".yml"] => :yaml,
".xml" => :xml,
".php" => :php,
".js" => :javascript,
".java" => :java,
".rhtml" => :rhtml,
".json" => :json
}
end
def syntax_highlight_by_file_type_or_specified(contents, file_name, file_type)
_, language_detected = file_map.find do |k, v|
Array(k).any? do |matcher|
matcher == File.extname(file_name) || matcher == File.basename(file_name)
end
end
language_detected = file_type if file_type
CodeRay.scan(contents, language_detected).term
end
# convert negative line numbers to positive by wrapping around
# last line (as per array indexing with negative numbers)
def normalized_line_number(line_number, total_lines)
line_number < 0 ? line_number + total_lines : line_number
end
# returns the file content between the lines and the normalized
# start and end line numbers.
def read_between_the_lines(file_name, start_line, end_line)
content = File.read(File.expand_path(file_name))
lines_array = content.each_line.to_a
[lines_array[start_line..end_line].join, normalized_line_number(start_line, lines_array.size),
normalized_line_number(end_line, lines_array.size)]
end
# documentation related helpers
def strip_color_codes(str)
str.gsub(/\e\[.*?(\d)+m/, '')
end
def process_rdoc(comment, code_type)
comment = comment.dup
comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { Pry.color ? "\e[34m#{$1}\e[0m" : $1 }.
gsub(/\B\+(\w*?)\+\B/) { Pry.color ? "\e[32m#{$1}\e[0m": $1 }.
gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }.
gsub(/`(?:\s*\n)?(.*?)\s*`/) { Pry.color ? CodeRay.scan($1, code_type).term : $1 }
end
def process_yardoc_tag(comment, tag)
in_tag_block = nil
output = comment.lines.map do |v|
if in_tag_block && v !~ /^\S/
strip_color_codes(strip_color_codes(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 = ["param", "return", "option", "yield", "attr", "attr_reader", "attr_writer",
"deprecate", "example"]
(yard_tags - ["example"]).inject(comment) { |a, v| process_yardoc_tag(a, v) }.
gsub(/^@(#{yard_tags.join("|")})/) { Pry.color ? "\e[33m#{$1}\e[0m": $1 }
end
def process_comment_markup(comment, code_type)
process_yardoc process_rdoc(comment, code_type)
end
# strip leading whitespace but preserve indentation
def strip_leading_whitespace(text)
return text if text.empty?
leading_spaces = text.lines.first[/^(\s+)/, 1]
text.gsub(/^#{leading_spaces}/, '')
end
def strip_leading_hash_and_whitespace_from_ruby_comments(comment)
comment = comment.dup
comment.gsub!(/\A\#+?$/, '')
comment.gsub!(/^\s*#/, '')
strip_leading_whitespace(comment)
end
def strip_comments_from_c_code(code)
code.sub /\A\s*\/\*.*?\*\/\s*/m, ''
end
def prompt(message, options="Yn")
opts = options.scan(/./)
optstring = opts.join("/") # case maintained
defaults = opts.select{|o| o.upcase == o }
opts = opts.map{|o| o.downcase}
raise "Error: Too many default values for the prompt: #{default.inspect}" if defaults.size > 1
default = defaults.first
loop do
response = Pry.input.readline("#{message} (#{optstring}) ").downcase
case response
when *opts
return response
when ""
return default.downcase
else
output.puts " |_ Invalid option: #{response.inspect}. Try again."
end
end
end
end
end
end