From 92fe82dbd6c6866d8ad107fb031d16c2f307148c Mon Sep 17 00:00:00 2001 From: John Mair Date: Sun, 6 Jan 2013 21:44:33 +0100 Subject: [PATCH] refactor cat command Put each output formatter (input expression, exception, file) into its own class. --- lib/pry/commands/cat.rb | 155 ++---------------- lib/pry/commands/cat/abstract_formatter.rb | 27 +++ lib/pry/commands/cat/exception_formatter.rb | 78 +++++++++ lib/pry/commands/cat/file_formatter.rb | 84 ++++++++++ .../cat/input_expression_formatter.rb | 42 +++++ 5 files changed, 245 insertions(+), 141 deletions(-) create mode 100644 lib/pry/commands/cat/abstract_formatter.rb create mode 100644 lib/pry/commands/cat/exception_formatter.rb create mode 100644 lib/pry/commands/cat/file_formatter.rb create mode 100644 lib/pry/commands/cat/input_expression_formatter.rb diff --git a/lib/pry/commands/cat.rb b/lib/pry/commands/cat.rb index 0f2e733c..951072ca 100644 --- a/lib/pry/commands/cat.rb +++ b/lib/pry/commands/cat.rb @@ -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) diff --git a/lib/pry/commands/cat/abstract_formatter.rb b/lib/pry/commands/cat/abstract_formatter.rb new file mode 100644 index 00000000..661f036b --- /dev/null +++ b/lib/pry/commands/cat/abstract_formatter.rb @@ -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 diff --git a/lib/pry/commands/cat/exception_formatter.rb b/lib/pry/commands/cat/exception_formatter.rb new file mode 100644 index 00000000..ae5cc599 --- /dev/null +++ b/lib/pry/commands/cat/exception_formatter.rb @@ -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 diff --git a/lib/pry/commands/cat/file_formatter.rb b/lib/pry/commands/cat/file_formatter.rb new file mode 100644 index 00000000..02b32f6f --- /dev/null +++ b/lib/pry/commands/cat/file_formatter.rb @@ -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 diff --git a/lib/pry/commands/cat/input_expression_formatter.rb b/lib/pry/commands/cat/input_expression_formatter.rb new file mode 100644 index 00000000..674cd061 --- /dev/null +++ b/lib/pry/commands/cat/input_expression_formatter.rb @@ -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