mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
refactor cat
This commit is contained in:
parent
95061e306c
commit
e7fed21ded
7 changed files with 167 additions and 138 deletions
|
@ -1,4 +1,17 @@
|
|||
class Pry
|
||||
class << self
|
||||
def Code(obj)
|
||||
case obj
|
||||
when Code
|
||||
obj
|
||||
when ::Method, Pry::Method, UnboundMethod, Proc
|
||||
Code.from_method(obj)
|
||||
else
|
||||
Code.new(obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Code
|
||||
class << self
|
||||
# Instantiate a `Code` object containing code loaded from a file or
|
||||
|
@ -13,7 +26,7 @@ class Pry
|
|||
if File.readable?(fn)
|
||||
f = File.open(fn, 'r')
|
||||
else
|
||||
raise CommandError, "Cannot open #{file.inspect} for reading."
|
||||
raise CommandError, "Cannot open #{fn.inspect} for reading."
|
||||
end
|
||||
end
|
||||
new(f, 1, code_type)
|
||||
|
@ -34,6 +47,8 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
attr_accessor :code_type
|
||||
|
||||
# @param [Array<String>] lines
|
||||
# @param [Fixnum?] (1) start_line
|
||||
# @param [Symbol?] (:ruby) code_type
|
||||
|
@ -57,6 +72,8 @@ class Pry
|
|||
alias << push
|
||||
|
||||
def before(line_num, lines=1)
|
||||
return self unless line_num
|
||||
|
||||
dup.instance_eval do
|
||||
@lines = @lines.select { |l, ln| ln >= line_num - lines && ln < line_num }
|
||||
self
|
||||
|
@ -64,13 +81,19 @@ class Pry
|
|||
end
|
||||
|
||||
def between(start_line, end_line)
|
||||
return self unless start_line && end_line
|
||||
start_line -= 1 unless start_line < 0
|
||||
end_line -= 1 unless end_line < 0
|
||||
|
||||
dup.instance_eval do
|
||||
@lines = @lines[(start_line + 1)..(end_line + 1)]
|
||||
@lines = @lines[start_line..end_line] || []
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
def around(line_num, lines=1)
|
||||
return self unless line_num
|
||||
|
||||
dup.instance_eval do
|
||||
@lines = @lines.select { |l, ln| ln >= line_num - lines && ln <= line_num + lines }
|
||||
self
|
||||
|
@ -78,6 +101,8 @@ class Pry
|
|||
end
|
||||
|
||||
def after(line_num, lines=1)
|
||||
return self unless line_num
|
||||
|
||||
dup.instance_eval do
|
||||
@lines = @lines.select { |l, ln| ln > line_num && ln <= line_num + lines }
|
||||
self
|
||||
|
@ -101,6 +126,14 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
def with_indentation(spaces=0)
|
||||
dup.instance_eval do
|
||||
@with_indentation = !!spaces
|
||||
@indentation_num = spaces
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
# @return [String]
|
||||
def inspect
|
||||
Object.instance_method(:to_s).bind(self).call
|
||||
|
@ -134,7 +167,13 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
lines.map(&:first).join("\n")
|
||||
if @with_indentation
|
||||
lines.each do |l|
|
||||
l[0] = "#{' ' * @indentation_num}#{l[0]}"
|
||||
end
|
||||
end
|
||||
|
||||
lines.map { |l| "#{l.first}\n" }.join
|
||||
end
|
||||
|
||||
def method_missing(name, *args, &blk)
|
||||
|
|
|
@ -28,67 +28,93 @@ class Pry
|
|||
_pry_.instance_eval(&Pry::FILE_COMPLETIONS)
|
||||
end
|
||||
end
|
||||
|
||||
alias_command "file-mode", "shell-mode"
|
||||
|
||||
command "cat", "Show code from a file or Pry's input buffer. Type `cat --help` for more information." do |*args|
|
||||
start_line = 0
|
||||
end_line = -1
|
||||
file_name = nil
|
||||
bt_index = 0
|
||||
command_class "cat", "Show code from a file or Pry's input buffer. Type `cat --help` for more information." do
|
||||
banner <<-USAGE
|
||||
Usage: cat FILE
|
||||
cat --ex [STACK_INDEX]
|
||||
cat --in [INPUT_INDEX_OR_RANGE]
|
||||
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
opt.on :s, :start, "Start line (defaults to start of file)Line 1 is the first line.", true, :as => Integer do |line|
|
||||
start_line = line - 1
|
||||
cat is capable of showing part or all of a source file, the context of the
|
||||
last exception, or an expression from Pry's input history.
|
||||
|
||||
cat --ex defaults to showing the lines surrounding the location of the last
|
||||
exception. Invoking it more than once travels up the exception's backtrace,
|
||||
and providing a number shows the context of the given index of the backtrace.
|
||||
USAGE
|
||||
|
||||
def options(opt)
|
||||
opt.on :ex, "Show the context of the last exception.", :optional => true, :as => Integer
|
||||
opt.on :i, :in, "Show one or more entries from Pry's expression history.", :optional => true, :as => Range, :default => -5..-1
|
||||
|
||||
opt.on :s, :start, "Starting line (defaults to the first line).", :optional => true, :as => Integer
|
||||
opt.on :e, :end, "Ending line (defaults to the last line).", :optional => 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').", true, :as => Symbol
|
||||
|
||||
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
||||
end
|
||||
|
||||
opt.on :e, :end, "End line (defaults to end of file). Line -1 is the last line", true, :as => Integer do |line|
|
||||
end_line = line - 1
|
||||
def process
|
||||
handler = case
|
||||
when opts.present?(:ex)
|
||||
method :process_ex
|
||||
when opts.present?(:in)
|
||||
method :process_in
|
||||
else
|
||||
method :process_file
|
||||
end
|
||||
|
||||
opt.on :ex, "Show a window of N lines either side of the last exception (defaults to 5).", :optional => true, :as => Integer do |bt_index_arg|
|
||||
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
|
||||
end
|
||||
|
||||
def process_ex
|
||||
window_size = Pry.config.exception_window_size || 5
|
||||
ex = _pry_.last_exception
|
||||
next if !ex
|
||||
if bt_index_arg
|
||||
bt_index = bt_index_arg
|
||||
else
|
||||
|
||||
raise CommandError, "No exception found." unless ex
|
||||
|
||||
if opts[:ex].nil?
|
||||
bt_index = ex.bt_index
|
||||
ex.inc_bt_index
|
||||
else
|
||||
bt_index = opts[:ex]
|
||||
end
|
||||
ex.bt_index = (bt_index + 1) % ex.backtrace.size
|
||||
|
||||
ex_file, ex_line = ex.bt_source_location_for(bt_index)
|
||||
start_line = (ex_line - 1) - window_size
|
||||
start_line = start_line < 0 ? 0 : start_line
|
||||
end_line = (ex_line - 1) + window_size
|
||||
if ex_file && RbxPath.is_core_path?(ex_file)
|
||||
file_name = RbxPath.convert_path_to_full(ex_file)
|
||||
else
|
||||
file_name = ex_file
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
opt.on :i, :in, "Show entries from Pry's input expression history. Takes an index or range.", :optional => true, :as => Range, :default => -5..-1
|
||||
start_line = ex_line - window_size
|
||||
start_line = 1 if start_line < 1
|
||||
end_line = ex_line + window_size
|
||||
|
||||
opt.on :l, "line-numbers", "Show line numbers."
|
||||
opt.on :t, :type, "The specific file type for syntax higlighting (e.g ruby, python)", true, :as => Symbol
|
||||
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
||||
opt.on :h, :help, "This message." do
|
||||
output.puts opt.help
|
||||
end
|
||||
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))
|
||||
|
||||
render_output("#{header}#{code}", opts)
|
||||
end
|
||||
|
||||
next if opts.present?(:help)
|
||||
|
||||
if opts.present?(:ex)
|
||||
if file_name.nil?
|
||||
raise CommandError, "No Exception or Exception has no associated file."
|
||||
end
|
||||
else
|
||||
file_name = args.shift
|
||||
end
|
||||
|
||||
if opts.present?(:in)
|
||||
def process_in
|
||||
normalized_range = absolute_index_range(opts[:i], _pry_.input_array.length)
|
||||
input_items = _pry_.input_array[normalized_range] || []
|
||||
|
||||
|
@ -98,66 +124,30 @@ class Pry
|
|||
raise CommandError, "No expressions found."
|
||||
end
|
||||
|
||||
if opts[:i].is_a?(Range)
|
||||
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
|
||||
|
||||
code = syntax_highlight_by_file_type_or_specified(s, nil, :ruby)
|
||||
render_output(contents, opts)
|
||||
end
|
||||
|
||||
def process_file
|
||||
file_name = args.shift
|
||||
|
||||
if opts.present?(:'line-numbers')
|
||||
contents << text.indent(text.with_line_numbers(code, 1), 2)
|
||||
else
|
||||
contents << text.indent(code, 2)
|
||||
end
|
||||
end
|
||||
else
|
||||
contents = syntax_highlight_by_file_type_or_specified(zipped_items.first.last, nil, :ruby)
|
||||
end
|
||||
else
|
||||
unless file_name
|
||||
raise CommandError, "Must provide a file name."
|
||||
raise CommandError, "Must provide a filename, --in, or --ex."
|
||||
end
|
||||
|
||||
begin
|
||||
contents, _, _ = read_between_the_lines(file_name, start_line, end_line)
|
||||
rescue Errno::ENOENT
|
||||
raise CommandError, "Could not find file: #{file_name}"
|
||||
end
|
||||
|
||||
contents = syntax_highlight_by_file_type_or_specified(contents, file_name, opts[:type])
|
||||
|
||||
if opts.present?(:'line-numbers')
|
||||
contents = text.with_line_numbers contents, start_line + 1
|
||||
end
|
||||
end
|
||||
|
||||
# add the arrow pointing to line that caused the exception
|
||||
if opts.present?(:ex)
|
||||
ex_file, ex_line = _pry_.last_exception.bt_source_location_for(bt_index)
|
||||
contents = text.with_line_numbers contents, start_line + 1, :bright_red
|
||||
|
||||
contents = contents.lines.each_with_index.map do |line, idx|
|
||||
l = idx + start_line
|
||||
if l == (ex_line - 1)
|
||||
" =>#{line}"
|
||||
else
|
||||
" #{line}"
|
||||
end
|
||||
end.join
|
||||
|
||||
# header for exceptions
|
||||
output.puts "\n#{Pry::Helpers::Text.bold('Exception:')} #{_pry_.last_exception.class}: #{_pry_.last_exception.message}\n--"
|
||||
output.puts "#{Pry::Helpers::Text.bold('From:')} #{ex_file} @ line #{ex_line} @ #{text.bold('level: ')} #{bt_index} of backtrace (of #{_pry_.last_exception.backtrace.size - 1}).\n\n"
|
||||
end
|
||||
code = yield(Pry::Code.from_file(file_name))
|
||||
|
||||
set_file_and_dir_locals(file_name)
|
||||
|
||||
if opts.present?(:flood)
|
||||
output.puts contents
|
||||
else
|
||||
stagger_output(contents)
|
||||
render_output(code, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,14 @@ class Pry
|
|||
file.close(opts[:unlink])
|
||||
end
|
||||
|
||||
def render_output(str, opts={})
|
||||
if opts[:flood]
|
||||
output.puts str
|
||||
else
|
||||
stagger_output str
|
||||
end
|
||||
end
|
||||
|
||||
def get_method_or_raise(name, target, opts={}, omit_help=false)
|
||||
meth = Pry::Method.from_str(name, target, opts)
|
||||
|
||||
|
|
|
@ -15,10 +15,11 @@ class Pry
|
|||
attr_accessor :binding_stack
|
||||
|
||||
attr_accessor :last_result
|
||||
attr_accessor :last_exception
|
||||
attr_accessor :last_file
|
||||
attr_accessor :last_dir
|
||||
|
||||
attr_reader :last_exception
|
||||
|
||||
attr_reader :input_array
|
||||
attr_reader :output_array
|
||||
|
||||
|
@ -228,7 +229,8 @@ class Pry
|
|||
result = set_last_result(target.eval(code, Pry.eval_path, Pry.current_line), target)
|
||||
result
|
||||
rescue RescuableException => e
|
||||
result = set_last_exception(e, target)
|
||||
self.last_exception = e
|
||||
e
|
||||
ensure
|
||||
update_input_history(code)
|
||||
hooks.exec_hook :after_eval, result, self
|
||||
|
@ -406,16 +408,18 @@ class Pry
|
|||
end
|
||||
|
||||
# Set the last exception for a session.
|
||||
# This method should not need to be invoked directly.
|
||||
# @param [Exception] ex The exception.
|
||||
# @param [Binding] target The binding to set `_ex_` on.
|
||||
def set_last_exception(ex, target)
|
||||
# @param [Exception] ex
|
||||
def last_exception=(ex)
|
||||
class << ex
|
||||
attr_accessor :file, :line, :bt_index
|
||||
def bt_source_location_for(index)
|
||||
backtrace[index] =~ /(.*):(\d+)/
|
||||
[$1, $2.to_i]
|
||||
end
|
||||
|
||||
def inc_bt_index
|
||||
@bt_index = (@bt_index + 1) % backtrace.size
|
||||
end
|
||||
end
|
||||
|
||||
ex.bt_index = 0
|
||||
|
@ -423,8 +427,7 @@ class Pry
|
|||
|
||||
@last_result_is_exception = true
|
||||
@output_array << ex
|
||||
|
||||
self.last_exception = ex
|
||||
@last_exception = ex
|
||||
end
|
||||
|
||||
# Update Pry's internal state after evalling code.
|
||||
|
|
|
@ -30,23 +30,12 @@ class << Pry
|
|||
end
|
||||
end
|
||||
|
||||
class MockPryException
|
||||
attr_accessor :bt_index
|
||||
attr_accessor :backtrace
|
||||
|
||||
def initialize(*backtrace)
|
||||
@backtrace = backtrace
|
||||
@bt_index = 0
|
||||
end
|
||||
|
||||
def message
|
||||
"mock exception"
|
||||
end
|
||||
|
||||
def bt_source_location_for(index)
|
||||
backtrace[index] =~ /(.*):(\d+)/
|
||||
[$1, $2.to_i]
|
||||
def mock_exception(*mock_backtrace)
|
||||
e = StandardError.new("mock exception")
|
||||
(class << e; self; end).class_eval do
|
||||
define_method(:backtrace) { mock_backtrace }
|
||||
end
|
||||
e
|
||||
end
|
||||
|
||||
Pry.reset_defaults
|
||||
|
|
|
@ -133,7 +133,7 @@ describe "Pry::DefaultCommands::Introspection" do
|
|||
|
||||
it 'should start editor on first level of backtrace when --ex used with no argument ' do
|
||||
pry_instance = Pry.new(:input => StringIO.new("edit -n --ex"), :output => StringIO.new)
|
||||
pry_instance.last_exception = MockPryException.new("a:1", "b:2", "c:3")
|
||||
pry_instance.last_exception = mock_exception("a:1", "b:2", "c:3")
|
||||
pry_instance.rep(self)
|
||||
@__ex_file__.should == "a"
|
||||
@__ex_line__.should == 1
|
||||
|
@ -141,7 +141,7 @@ describe "Pry::DefaultCommands::Introspection" do
|
|||
|
||||
it 'should start editor on first level of backtrace when --ex 0 used ' do
|
||||
pry_instance = Pry.new(:input => StringIO.new("edit -n --ex 0"), :output => StringIO.new)
|
||||
pry_instance.last_exception = MockPryException.new("a:1", "b:2", "c:3")
|
||||
pry_instance.last_exception = mock_exception("a:1", "b:2", "c:3")
|
||||
pry_instance.rep(self)
|
||||
@__ex_file__.should == "a"
|
||||
@__ex_line__.should == 1
|
||||
|
@ -149,7 +149,7 @@ describe "Pry::DefaultCommands::Introspection" do
|
|||
|
||||
it 'should start editor on second level of backtrace when --ex 1 used' do
|
||||
pry_instance = Pry.new(:input => StringIO.new("edit -n --ex 1"), :output => StringIO.new)
|
||||
pry_instance.last_exception = MockPryException.new("a:1", "b:2", "c:3")
|
||||
pry_instance.last_exception = mock_exception("a:1", "b:2", "c:3")
|
||||
pry_instance.rep(self)
|
||||
@__ex_file__.should == "b"
|
||||
@__ex_line__.should == 2
|
||||
|
@ -157,7 +157,7 @@ describe "Pry::DefaultCommands::Introspection" do
|
|||
|
||||
it 'should start editor on third level of backtrace when --ex 2 used' do
|
||||
pry_instance = Pry.new(:input => StringIO.new("edit -n --ex 2"), :output => StringIO.new)
|
||||
pry_instance.last_exception = MockPryException.new("a:1", "b:2", "c:3")
|
||||
pry_instance.last_exception = mock_exception("a:1", "b:2", "c:3")
|
||||
pry_instance.rep(self)
|
||||
@__ex_file__.should == "c"
|
||||
@__ex_line__.should == 3
|
||||
|
@ -165,7 +165,7 @@ describe "Pry::DefaultCommands::Introspection" do
|
|||
|
||||
it 'should display error message when backtrace level is out of bounds (using --ex 4)' do
|
||||
pry_instance = Pry.new(:input => StringIO.new("edit -n --ex 4"), :output => str_output = StringIO.new)
|
||||
pry_instance.last_exception = MockPryException.new("a:1", "b:2", "c:3")
|
||||
pry_instance.last_exception = mock_exception("a:1", "b:2", "c:3")
|
||||
pry_instance.rep(self)
|
||||
str_output.string.should =~ /Exception has no associated file/
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ describe "Pry::DefaultCommands::Shell" do
|
|||
|
||||
describe "on receiving a file that does not exist" do
|
||||
it 'should display an error message' do
|
||||
mock_pry("cat supercalifragilicious66").should =~ /Could not find file/
|
||||
mock_pry("cat supercalifragilicious66").should =~ /Cannot open/
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,7 +65,7 @@ describe "Pry::DefaultCommands::Shell" do
|
|||
temp_file do |f|
|
||||
f << "bt number 1"
|
||||
f.flush
|
||||
pry_instance.last_exception = MockPryException.new("#{f.path}:1", "x", "x")
|
||||
pry_instance.last_exception = mock_exception("#{f.path}:1", "x", "x")
|
||||
pry_instance.rep(self)
|
||||
end
|
||||
|
||||
|
@ -78,7 +78,7 @@ describe "Pry::DefaultCommands::Shell" do
|
|||
temp_file do |f|
|
||||
f << "bt number 1"
|
||||
f.flush
|
||||
pry_instance.last_exception = MockPryException.new("#{f.path}:1", "x", "x")
|
||||
pry_instance.last_exception = mock_exception("#{f.path}:1", "x", "x")
|
||||
pry_instance.rep(self)
|
||||
end
|
||||
|
||||
|
@ -91,31 +91,31 @@ describe "Pry::DefaultCommands::Shell" do
|
|||
temp_file do |f|
|
||||
f << "bt number 2"
|
||||
f.flush
|
||||
pry_instance.last_exception = MockPryException.new("x", "#{f.path}:1", "x")
|
||||
pry_instance.last_exception = mock_exception("x", "#{f.path}:1", "x")
|
||||
pry_instance.rep(self)
|
||||
end
|
||||
|
||||
str_output.string.should =~ /bt number 2/
|
||||
end
|
||||
|
||||
it 'should cat third level of backtrace when --ex 2 used ' do
|
||||
it 'should cat third level of backtrace when --ex 2 used' do
|
||||
pry_instance = Pry.new(:input => StringIO.new("cat --ex 2"), :output => str_output = StringIO.new)
|
||||
|
||||
temp_file do |f|
|
||||
f << "bt number 3"
|
||||
f.flush
|
||||
pry_instance.last_exception = MockPryException.new("x", "x", "#{f.path}:1")
|
||||
pry_instance.last_exception = mock_exception("x", "x", "#{f.path}:1")
|
||||
pry_instance.rep(self)
|
||||
end
|
||||
|
||||
str_output.string.should =~ /bt number 3/
|
||||
end
|
||||
|
||||
it 'should show error when backtrace level out of bounds ' do
|
||||
it 'should show error when backtrace level out of bounds' do
|
||||
pry_instance = Pry.new(:input => StringIO.new("cat --ex 3"), :output => str_output = StringIO.new)
|
||||
pry_instance.last_exception = MockPryException.new("x", "x", "x")
|
||||
pry_instance.last_exception = mock_exception("x", "x", "x")
|
||||
pry_instance.rep(self)
|
||||
str_output.string.should =~ /No Exception or Exception has no associated file/
|
||||
str_output.string.should =~ /out of bounds/
|
||||
end
|
||||
|
||||
it 'each successive cat --ex should show the next level of backtrace, and going past the final level should return to the first' do
|
||||
|
@ -129,7 +129,7 @@ describe "Pry::DefaultCommands::Shell" do
|
|||
pry_instance = Pry.new(:input => StringIO.new("cat --ex\n" * 4),
|
||||
:output => (str_output = StringIO.new))
|
||||
|
||||
pry_instance.last_exception = MockPryException.new(*temp_files.map { |f| "#{f.path}:1" })
|
||||
pry_instance.last_exception = mock_exception(*temp_files.map { |f| "#{f.path}:1" })
|
||||
|
||||
3.times do |i|
|
||||
pry_instance.rep(self)
|
||||
|
|
Loading…
Reference in a new issue