Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

refactor cat command

Put each output formatter (input expression, exception, file) into its own class.
  • Loading branch information...
commit 92fe82dbd6c6866d8ad107fb031d16c2f307148c 1 parent d77bfe2
@banister banister authored
View
157 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 Command::Cat < Pry::ClassCommand
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 = 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
+ 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
+
+ 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)
View
27 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
View
78 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
View
84 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
View
42 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
Please sign in to comment.
Something went wrong with that request. Please try again.