Permalink
Browse files

Add -m, -c, -f options to whereami command.

-m, the current method in its totality
-c, the current class/module that is the current 'candidate' (monkeypatch)
-f, the entire file
  • Loading branch information...
1 parent 97f1be8 commit 2561179ba008ab56198bb4c5835504e975d519c0 @banister banister committed Feb 2, 2013
Showing with 147 additions and 30 deletions.
  1. +87 −19 lib/pry/commands/whereami.rb
  2. +60 −11 spec/commands/whereami_spec.rb
@@ -1,16 +1,23 @@
class Pry
class Command::Whereami < Pry::ClassCommand
+
+ class << self
+ attr_accessor :method_size_cutoff
+ end
+
+ @method_size_cutoff = 30
+
match 'whereami'
description 'Show code surrounding the current context.'
group 'Context'
banner <<-'BANNER'
- Usage: whereami [-qn] [N]
+ Usage: whereami [-qn] [LINES]
Describe the current location. If you use `binding.pry` inside a method then
whereami will print out the source for that method.
- If a number is passed, then N lines before and after the current line will be
+ If a number is passed, then LINES lines before and after the current line will be
shown instead of the method itself.
The `-q` flag can be used to suppress error messages in the case that there's
@@ -25,29 +32,53 @@ class Command::Whereami < Pry::ClassCommand
BANNER
def setup
- @method = Pry::Method.from_binding(target)
- @file = target.eval('__FILE__')
+ @file = expand_path(target.eval('__FILE__'))
@line = target.eval('__LINE__')
+ @method = Pry::Method.from_binding(target)
end
def options(opt)
opt.on :q, :quiet, "Don't display anything in case of an error"
opt.on :n, :"no-line-numbers", "Do not display line numbers"
+ opt.on :m, :"method", "Show the complete source for the current method."
+ opt.on :c, :"class", "Show the complete source for the current class or module."
+ opt.on :f, :"file", "Show the complete source for the current file."
end
def code
- @code ||= if show_method?
- Pry::Code.from_method(@method)
+ @code ||= if opts.present?(:m)
+ method_code or raise CommandError, "Cannot find method code."
+ elsif opts.present?(:c)
+ class_code or raise CommandError, "Cannot find class code."
+ elsif opts.present?(:f)
+ Pry::Code.from_file(@file)
+ elsif args.any?
+ code_window
else
- Pry::Code.from_file(@file).around(@line, window_size)
+ default_code
end
end
+ def code?
+ !!code
+ rescue MethodSource::SourceNotFoundError
+ false
+ end
+
+ def bad_option_combination?
+ [opts.present?(:m), opts.present?(:f),
+ opts.present?(:c), args.any?].count(true) > 1
+ end
+
def location
"#{@file} @ line #{@line} #{@method && @method.name_with_owner}"
end
def process
+ if bad_option_combination?
+ raise CommandError, "Only one of -m, -c, -f, and LINES may be specified."
+ end
+
if nothing_to_do?
return
elsif internal_binding?(target)
@@ -57,9 +88,10 @@ def process
set_file_and_dir_locals(@file)
- output.puts "\n#{text.bold('From:')} #{location}:\n\n"
- output.puts code.with_line_numbers(use_line_numbers?).with_marker(marker)
- output.puts
+ out = "\n#{text.bold('From:')} #{location}:\n\n" +
+ code.with_line_numbers(use_line_numbers?).with_marker(marker) + "\n"
+
+ stagger_output(out)
end
private
@@ -88,17 +120,53 @@ def handle_internal_binding
end
end
- def show_method?
- args.empty? && @method && @method.source? && @method.source_range.count < 20 &&
- # These checks are needed in case of an eval with a binding and file/line
- # numbers set to outside the function. As in rails' use of ERB.
- @method.source_file == @file && @method.source_range.include?(@line)
+ def small_method?
+ @method.source_range.count < self.class.method_size_cutoff
end
- def code?
- !!code
- rescue MethodSource::SourceNotFoundError
- false
+ def default_code
+ if method_code && small_method?
+ method_code
+ else
+ code_window
+ end
+ end
+
+ def code_window
+ Pry::Code.from_file(@file).around(@line, window_size)
+ end
+
+ def method_code
+ return @method_code if @method_code
+
+ if valid_method?
+ @method_code = Pry::Code.from_method(@method)
+ end
+ end
+
+ def class_code
+ return @class_code if @class_code
+
+ if valid_method?
+ mod = Pry::WrappedModule(@method.owner)
+ idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file }
+ @class_code = idx && Pry::Code.from_module(mod, idx)
+ end
+ end
+
+ def valid_method?
+ @method && @method.source? && expand_path(@method.source_file) == @file &&
+ @method.source_range.include?(@line)
+ end
+
+ def expand_path(f)
+ return if !f
+
+ if Pry.eval_path == f
+ f
+ else
+ File.expand_path(f)
+ end
end
def window_size
@@ -63,7 +63,6 @@ def blimey!
end
Cor.instance_method(:blimey!).source.should =~ /pry_eval/
-
Cor.new.blimey!.should =~ /Cor#blimey!.*Look at me/m
Object.remove_const(:Cor)
end
@@ -85,26 +84,76 @@ def blimey!
Object.remove_const(:Cor)
end
- it 'should display a description and and error if reading the file goes wrong' do
+ # Now that we use stagger_output (paging output) we no longer get
+ # the "From: " line, as we output everything in one go (not separate output.puts)
+ # and so the user just gets a single `Error: Cannot open
+ # "not.found.file.erb" for reading.`
+ # which is good enough IMO. Unfortunately we can't test for it
+ # though, as we don't hook stdout.
+ #
+ # it 'should display a description and error if reading the file goes wrong' do
+ # class Cor
+ # def blimey!
+ # eval <<-END, binding, "not.found.file.erb", 7
+ # Pad.tester = pry_tester(binding)
+ # Pad.tester.eval('whereami')
+ # END
+ # end
+ # end
+
+ # proc { Cor.new.blimey! }.should.raise(MethodSource::SourceNotFoundError)
+
+ # Pad.tester.last_output.should =~
+ # /From: not.found.file.erb @ line 7 Cor#blimey!:/
+ # Object.remove_const(:Cor)
+ # end
+
+ it 'should show code window (not just method source) if parameter passed to whereami' do
class Cor
def blimey!
- eval <<-END, binding, "not.found.file.erb", 7
- Pad.tester = pry_tester(binding)
- Pad.tester.eval('whereami')
- END
+ pry_eval(binding, 'whereami 3').should =~ /class Cor/
+ end
+ end
+ Cor.new.blimey!
+ Object.remove_const(:Cor)
+ end
+
+ it 'should show entire method when -m option used' do
+ old_size, Pry.config.default_window_size = Pry.config.default_window_size, 1
+ old_cutoff, Pry::Command::Whereami.method_size_cutoff = Pry::Command::Whereami.method_size_cutoff, 1
+ class Cor
+ def blimey!
+ 1
+ 2
+ pry_eval(binding, 'whereami -m').should =~ /def blimey/
end
end
+ Pry::Command::Whereami.method_size_cutoff, Pry.config.default_window_size = old_cutoff, old_size
+ Cor.new.blimey!
+ Object.remove_const(:Cor)
+ end
- proc { Cor.new.blimey! }.should.raise(MethodSource::SourceNotFoundError)
- Pad.tester.last_output.should =~
- /From: not.found.file.erb @ line 7 Cor#blimey!:/
+ it 'should show entire file when -f option used' do
+ class Cor
+ def blimey!
+ 1
+ 2
+ pry_eval(binding, 'whereami -f').should =~ /show entire file when -f option used/
+ end
+ end
+ Cor.new.blimey!
Object.remove_const(:Cor)
end
- it 'should show code window (not just method source) if parameter passed to whereami' do
+ it 'should show class when -c option used, and locate correct candidate' do
+ require 'fixtures/whereami_helper'
class Cor
def blimey!
- pry_eval(binding, 'whereami 3').should =~ /class Cor/
+ 1
+ 2
+ out = pry_eval(binding, 'whereami -c')
+ out.should =~ /class Cor/
+ out.should =~ /blimey/
end
end
Cor.new.blimey!

0 comments on commit 2561179

Please sign in to comment.