mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
refactor cat command
Put each output formatter (input expression, exception, file) into its own class.
This commit is contained in:
parent
d77bfe285c
commit
92fe82dbd6
5 changed files with 245 additions and 141 deletions
|
@ -1,5 +1,10 @@
|
|||
class Pry
|
||||
class Command::Cat < Pry::ClassCommand
|
||||
require 'pry/commands/cat/abstract_formatter.rb'
|
||||
require 'pry/commands/cat/input_expression_formatter.rb'
|
||||
require 'pry/commands/cat/exception_formatter.rb'
|
||||
require 'pry/commands/cat/file_formatter.rb'
|
||||
|
||||
match 'cat'
|
||||
group 'Input and Output'
|
||||
description "Show code from a file, Pry's input buffer, or the last " \
|
||||
|
@ -21,160 +26,28 @@ class Pry
|
|||
def options(opt)
|
||||
opt.on :ex, "Show the context of the last exception.", :optional_argument => true, :as => Integer
|
||||
opt.on :i, :in, "Show one or more entries from Pry's expression history.", :optional_argument => true, :as => Range, :default => -5..-1
|
||||
|
||||
opt.on :s, :start, "Starting line (defaults to the first line).", :optional_argument => true, :as => Integer
|
||||
opt.on :e, :end, "Ending line (defaults to the last line).", :optional_argument => true, :as => Integer
|
||||
opt.on :l, :'line-numbers', "Show line numbers."
|
||||
opt.on :t, :type, "The file type for syntax highlighting (e.g., 'ruby' or 'python').", :argument => true, :as => Symbol
|
||||
|
||||
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
||||
end
|
||||
|
||||
def process
|
||||
handler = case
|
||||
when opts.present?(:ex)
|
||||
method :process_ex
|
||||
when opts.present?(:in)
|
||||
method :process_in
|
||||
else
|
||||
method :process_file
|
||||
end
|
||||
output = case
|
||||
when opts.present?(:ex)
|
||||
ExceptionFormatter.new(_pry_.last_exception, _pry_, opts).format
|
||||
when opts.present?(:in)
|
||||
InputExpressionFormatter.new(_pry_.input_array, opts).format
|
||||
else
|
||||
FileFormatter.new(args.first, _pry_, opts).format
|
||||
end
|
||||
|
||||
output = handler.call do |code|
|
||||
code.code_type = opts[:type] || :ruby
|
||||
|
||||
code.between(opts[:start] || 1, opts[:end] || -1).
|
||||
with_line_numbers(opts.present?(:'line-numbers') || opts.present?(:ex))
|
||||
end
|
||||
|
||||
render_output(output, opts)
|
||||
end
|
||||
|
||||
def process_ex
|
||||
window_size = Pry.config.default_window_size || 5
|
||||
ex = _pry_.last_exception
|
||||
|
||||
raise CommandError, "No exception found." unless ex
|
||||
|
||||
if opts[:ex].nil?
|
||||
bt_index = ex.bt_index
|
||||
ex.inc_bt_index
|
||||
else
|
||||
bt_index = absolute_index_number(opts[:ex], ex.backtrace.size)
|
||||
ex.bt_index = bt_index
|
||||
ex.inc_bt_index
|
||||
end
|
||||
|
||||
ex_file, ex_line = ex.bt_source_location_for(bt_index)
|
||||
|
||||
raise CommandError, "The given backtrace level is out of bounds." unless ex_file
|
||||
|
||||
if RbxPath.is_core_path?(ex_file)
|
||||
ex_file = RbxPath.convert_path_to_full(ex_file)
|
||||
end
|
||||
|
||||
set_file_and_dir_locals(ex_file)
|
||||
|
||||
start_line = ex_line - window_size
|
||||
start_line = 1 if start_line < 1
|
||||
end_line = ex_line + window_size
|
||||
|
||||
header = unindent <<-HEADER
|
||||
#{text.bold 'Exception:'} #{ex.class}: #{ex.message}
|
||||
--
|
||||
#{text.bold('From:')} #{ex_file} @ line #{ex_line} @ #{text.bold("level: #{bt_index}")} of backtrace (of #{ex.backtrace.size - 1}).
|
||||
|
||||
HEADER
|
||||
|
||||
code = yield(Pry::Code.from_file(ex_file).
|
||||
between(start_line, end_line).
|
||||
with_marker(ex_line))
|
||||
|
||||
"#{header}#{code}"
|
||||
end
|
||||
|
||||
def process_in
|
||||
normalized_range = absolute_index_range(opts[:i], _pry_.input_array.length)
|
||||
input_items = _pry_.input_array[normalized_range] || []
|
||||
|
||||
zipped_items = normalized_range.zip(input_items).reject { |_, s| s.nil? || s == "" }
|
||||
|
||||
unless zipped_items.length > 0
|
||||
raise CommandError, "No expressions found."
|
||||
end
|
||||
|
||||
if zipped_items.length > 1
|
||||
contents = ""
|
||||
zipped_items.each do |i, s|
|
||||
contents << "#{text.bold(i.to_s)}:\n"
|
||||
contents << yield(Pry::Code(s).with_indentation(2)).to_s
|
||||
end
|
||||
else
|
||||
contents = yield(Pry::Code(zipped_items.first.last))
|
||||
end
|
||||
|
||||
contents
|
||||
end
|
||||
|
||||
def process_file
|
||||
file_name = args.shift
|
||||
|
||||
unless file_name
|
||||
raise CommandError, "Must provide a filename, --in, or --ex."
|
||||
end
|
||||
|
||||
file_name, line_num = file_name.split(':')
|
||||
file_name = File.expand_path(file_name)
|
||||
set_file_and_dir_locals(file_name)
|
||||
|
||||
code = yield(Pry::Code.from_file(file_name))
|
||||
|
||||
code.code_type = opts[:type] || detect_code_type_from_file(file_name)
|
||||
if line_num
|
||||
code = code.around(line_num.to_i,
|
||||
Pry.config.default_window_size || 7)
|
||||
end
|
||||
|
||||
code
|
||||
stagger_output(output)
|
||||
end
|
||||
|
||||
def complete(search)
|
||||
super + Bond::Rc.files(search.split(" ").last || '')
|
||||
end
|
||||
|
||||
def detect_code_type_from_file(file_name)
|
||||
name, ext = File.basename(file_name).split('.', 2)
|
||||
|
||||
if ext
|
||||
case ext
|
||||
when "py"
|
||||
:python
|
||||
when "rb", "gemspec", "rakefile", "ru", "pryrc", "irbrc"
|
||||
:ruby
|
||||
when "js"
|
||||
return :javascript
|
||||
when "yml", "prytheme"
|
||||
:yaml
|
||||
when "groovy"
|
||||
:groovy
|
||||
when "c"
|
||||
:c
|
||||
when "cpp"
|
||||
:cpp
|
||||
when "java"
|
||||
:java
|
||||
else
|
||||
:text
|
||||
end
|
||||
else
|
||||
case name
|
||||
when "Rakefile", "Gemfile"
|
||||
:ruby
|
||||
else
|
||||
:text
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Pry::Commands.add_command(Pry::Command::Cat)
|
||||
|
|
27
lib/pry/commands/cat/abstract_formatter.rb
Normal file
27
lib/pry/commands/cat/abstract_formatter.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
class Pry
|
||||
class Command::Cat
|
||||
class AbstractFormatter
|
||||
include Pry::Helpers::CommandHelpers
|
||||
include Pry::Helpers::BaseHelpers
|
||||
|
||||
private
|
||||
def decorate(content)
|
||||
content.code_type = code_type
|
||||
content.between(*between_lines).
|
||||
with_line_numbers(use_line_numbers?)
|
||||
end
|
||||
|
||||
def code_type
|
||||
opts[:type] || :ruby
|
||||
end
|
||||
|
||||
def use_line_numbers?
|
||||
opts.present?(:'line-numbers') || opts.present?(:ex)
|
||||
end
|
||||
|
||||
def between_lines
|
||||
[opts[:start] || 1, opts[:end] || -1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
78
lib/pry/commands/cat/exception_formatter.rb
Normal file
78
lib/pry/commands/cat/exception_formatter.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
class Pry
|
||||
class Command::Cat
|
||||
class ExceptionFormatter < AbstractFormatter
|
||||
attr_accessor :ex
|
||||
attr_accessor :opts
|
||||
attr_accessor :_pry_
|
||||
|
||||
def initialize(exception, _pry_, opts)
|
||||
@ex = exception
|
||||
@opts = opts
|
||||
@_pry_ = _pry_
|
||||
end
|
||||
|
||||
def format
|
||||
check_for_errors
|
||||
set_file_and_dir_locals(backtrace_file, _pry_, _pry_.current_context)
|
||||
code = decorate(Pry::Code.from_file(backtrace_file).
|
||||
between(*start_and_end_line_for_code_window).
|
||||
with_marker(backtrace_line)).to_s
|
||||
"#{header}#{code}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def code_window_size
|
||||
Pry.config.default_window_size || 5
|
||||
end
|
||||
|
||||
def backtrace_level
|
||||
return @backtrace_level if @backtrace_level
|
||||
|
||||
bl = if opts[:ex].nil?
|
||||
ex.bt_index
|
||||
else
|
||||
ex.bt_index = absolute_index_number(opts[:ex], ex.backtrace.size)
|
||||
end
|
||||
|
||||
increment_backtrace_level
|
||||
@backtrace_level = bl
|
||||
end
|
||||
|
||||
def increment_backtrace_level
|
||||
ex.inc_bt_index
|
||||
end
|
||||
|
||||
def backtrace_file
|
||||
file = Array(ex.bt_source_location_for(backtrace_level)).first
|
||||
(file && RbxPath.is_core_path?(file)) ? RbxPath.convert_path_to_full(file) : file
|
||||
end
|
||||
|
||||
def backtrace_line
|
||||
Array(ex.bt_source_location_for(backtrace_level)).last
|
||||
end
|
||||
|
||||
def check_for_errors
|
||||
raise CommandError, "No exception found." unless ex
|
||||
raise CommandError, "The given backtrace level is out of bounds." unless backtrace_file
|
||||
end
|
||||
|
||||
def start_and_end_line_for_code_window
|
||||
start_line = backtrace_line - code_window_size
|
||||
start_line = 1 if start_line < 1
|
||||
|
||||
[start_line, backtrace_line + code_window_size]
|
||||
end
|
||||
|
||||
def header
|
||||
unindent %{
|
||||
#{Helpers::Text.bold 'Exception:'} #{ex.class}: #{ex.message}
|
||||
--
|
||||
#{Helpers::Text.bold('From:')} #{backtrace_file} @ line #{backtrace_line} @ #{Helpers::Text.bold("level: #{backtrace_level}")} of backtrace (of #{ex.backtrace.size - 1}).
|
||||
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
84
lib/pry/commands/cat/file_formatter.rb
Normal file
84
lib/pry/commands/cat/file_formatter.rb
Normal file
|
@ -0,0 +1,84 @@
|
|||
class Pry
|
||||
class Command::Cat
|
||||
class FileFormatter < AbstractFormatter
|
||||
attr_accessor :file_with_embedded_line
|
||||
attr_accessor :opts
|
||||
attr_accessor :_pry_
|
||||
|
||||
def initialize(file_with_embedded_line, _pry_, opts)
|
||||
@file_with_embedded_line = file_with_embedded_line
|
||||
@opts = opts
|
||||
@_pry_ = _pry_
|
||||
end
|
||||
|
||||
def file_and_line
|
||||
file_name, line_num = file_with_embedded_line.split(':')
|
||||
|
||||
[File.expand_path(file_name), line_num ? line_num.to_i : nil]
|
||||
end
|
||||
|
||||
def file_name
|
||||
file_and_line.first
|
||||
end
|
||||
|
||||
def line_number
|
||||
file_and_line.last
|
||||
end
|
||||
|
||||
def code_window_size
|
||||
Pry.config.default_window_size || 7
|
||||
end
|
||||
|
||||
def format
|
||||
raise CommandError, "Must provide a filename, --in, or --ex." if !file_with_embedded_line
|
||||
|
||||
set_file_and_dir_locals(file_name, _pry_, _pry_.current_context)
|
||||
decorate(Pry::Code.from_file(file_name))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def decorate(content)
|
||||
line_number ? super.around(line_number, code_window_size) : super
|
||||
end
|
||||
|
||||
def code_type
|
||||
opts[:type] || detect_code_type_from_file(file_name)
|
||||
end
|
||||
|
||||
def detect_code_type_from_file(file_name)
|
||||
name, ext = File.basename(file_name).split('.', 2)
|
||||
|
||||
if ext
|
||||
case ext
|
||||
when "py"
|
||||
:python
|
||||
when "rb", "gemspec", "rakefile", "ru", "pryrc", "irbrc"
|
||||
:ruby
|
||||
when "js"
|
||||
return :javascript
|
||||
when "yml", "prytheme"
|
||||
:yaml
|
||||
when "groovy"
|
||||
:groovy
|
||||
when "c"
|
||||
:c
|
||||
when "cpp"
|
||||
:cpp
|
||||
when "java"
|
||||
:java
|
||||
else
|
||||
:text
|
||||
end
|
||||
else
|
||||
case name
|
||||
when "Rakefile", "Gemfile"
|
||||
:ruby
|
||||
else
|
||||
:text
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
42
lib/pry/commands/cat/input_expression_formatter.rb
Normal file
42
lib/pry/commands/cat/input_expression_formatter.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
class Pry
|
||||
class Command::Cat
|
||||
class InputExpressionFormatter < AbstractFormatter
|
||||
attr_accessor :input_expressions
|
||||
attr_accessor :opts
|
||||
|
||||
def initialize(input_expressions, opts)
|
||||
@input_expressions = input_expressions
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def format
|
||||
raise CommandError, "No input expressions!" if numbered_input_items.length < 1
|
||||
|
||||
if numbered_input_items.length > 1
|
||||
content = ""
|
||||
numbered_input_items.each do |i, s|
|
||||
content << "#{Helpers::Text.bold(i.to_s)}:\n" << decorate(Pry::Code(s).with_indentation(2)).to_s
|
||||
end
|
||||
|
||||
content
|
||||
else
|
||||
decorate(Pry::Code(selected_input_items.first)).to_s
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def selected_input_items
|
||||
input_expressions[normalized_expression_range] || []
|
||||
end
|
||||
|
||||
def numbered_input_items
|
||||
@numbered_input_items ||= normalized_expression_range.zip(selected_input_items).
|
||||
reject { |_, s| s.nil? || s == "" }
|
||||
end
|
||||
|
||||
def normalized_expression_range
|
||||
absolute_index_range(opts[:i], input_expressions.length)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue