Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Refactor #57

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/pry-stack_explorer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require "pry" unless defined?(::Pry)
require "pry-stack_explorer/version"
require "pry-stack_explorer/commands"
require "pry-stack_explorer/frame"
require "pry-stack_explorer/frame_manager"
require "pry-stack_explorer/when_started_hook"
require "binding_of_caller"
Expand Down
221 changes: 24 additions & 197 deletions lib/pry-stack_explorer/commands.rb
Original file line number Diff line number Diff line change
@@ -1,134 +1,4 @@
module PryStackExplorer
module FrameHelpers
private

# @return [PryStackExplorer::FrameManager] The active frame manager for
# the current `Pry` instance.
def frame_manager
PryStackExplorer.frame_manager(pry_instance)
end

# @return [Array<PryStackExplorer::FrameManager>] All the frame
# managers for the current `Pry` instance.
def frame_managers
PryStackExplorer.frame_managers(pry_instance)
end

# @return [Boolean] Whether there is a context to return to once
# the current `frame_manager` is popped.
def prior_context_exists?
frame_managers.count > 1 || frame_manager.prior_binding
end

# Return a description of the frame (binding).
# This is only useful for regular old bindings that have not been
# enhanced by `#of_caller`.
# @param [Binding] b The binding.
# @return [String] A description of the frame (binding).
def frame_description(b)
b_self = b.eval('self')
b_method = b.eval('__method__')

if b_method && b_method != :__binding__ && b_method != :__binding_impl__
b_method.to_s
elsif b_self.instance_of?(Module)
"<module:#{b_self}>"
elsif b_self.instance_of?(Class)
"<class:#{b_self}>"
else
"<main>"
end
end

# Return a description of the passed binding object. Accepts an
# optional `verbose` parameter.
# @param [Binding] b The binding.
# @param [Boolean] verbose Whether to generate a verbose description.
# @return [String] The description of the binding.
def frame_info(b, verbose = false)
meth = b.eval('__method__')
b_self = b.eval('self')
meth_obj = Pry::Method.from_binding(b) if meth

type = b.frame_type ? "[#{b.frame_type}]".ljust(9) : ""
desc = b.frame_description ? "#{b.frame_description}" : "#{frame_description(b)}"
sig = meth_obj ? "<#{signature_with_owner(meth_obj)}>" : ""

self_clipped = "#{Pry.view_clip(b_self)}"
path = '@ ' + b.source_location.join(':')

if !verbose
"#{type} #{desc} #{sig}"
else
"#{type} #{desc} #{sig}\n in #{self_clipped} #{path}"
end
end

# @param [Pry::Method] meth_obj The method object.
# @return [String] Signature for the method object in Class#method format.
def signature_with_owner(meth_obj)
if !meth_obj.undefined?
args = meth_obj.parameters.inject([]) do |arr, (type, name)|
name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
arr << case type
when :req then name.to_s
when :opt then "#{name}=?"
when :rest then "*#{name}"
when :block then "&#{name}"
else '?'
end
end
"#{meth_obj.name_with_owner}(#{args.join(', ')})"
else
"#{meth_obj.name_with_owner}(UNKNOWN) (undefined method)"
end
end

# Regexp.new(args[0])
def find_frame_by_regex(regex, up_or_down)
frame_index = find_frame_by_block(up_or_down) do |b|
b.eval("__method__").to_s =~ regex
end

if frame_index
frame_index
else
raise Pry::CommandError, "No frame that matches #{regex.source} found!"
end
end

def find_frame_by_object_regex(class_regex, method_regex, up_or_down)
frame_index = find_frame_by_block(up_or_down) do |b|
class_match = b.eval("self.class").to_s =~ class_regex
meth_match = b.eval("__method__").to_s =~ method_regex

class_match && meth_match
end

if frame_index
frame_index
else
raise Pry::CommandError, "No frame that matches #{class_regex.source}" + '#' + "#{method_regex.source} found!"
end
end

def find_frame_by_block(up_or_down)
start_index = frame_manager.binding_index

if up_or_down == :down
enum = frame_manager.bindings[0..start_index - 1].reverse_each
else
enum = frame_manager.bindings[start_index + 1..-1]
end

new_frame = enum.find do |b|
yield(b)
end

frame_manager.bindings.index(new_frame)
end
end


Commands = Pry::CommandSet.new do
create_command "up", "Go up to the caller's context." do
Expand All @@ -145,20 +15,7 @@ def find_frame_by_block(up_or_down)

def process
inc = args.first.nil? ? "1" : args.first

if !frame_manager
raise Pry::CommandError, "Nowhere to go!"
else
if inc =~ /\d+/
frame_manager.change_frame_to frame_manager.binding_index + inc.to_i
elsif match = /^([A-Z]+[^#.]*)(#|\.)(.+)$/.match(inc)
new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :up)
frame_manager.change_frame_to new_frame_index
elsif inc =~ /^[^-].*$/
new_frame_index = find_frame_by_regex(Regexp.new(inc), :up)
frame_manager.change_frame_to new_frame_index
end
end
go_updown(:up, inc)
end
end

Expand All @@ -176,24 +33,7 @@ def process

def process
inc = args.first.nil? ? "1" : args.first

if !frame_manager
raise Pry::CommandError, "Nowhere to go!"
else
if inc =~ /\d+/
if frame_manager.binding_index - inc.to_i < 0
raise Pry::CommandError, "At bottom of stack, cannot go further!"
else
frame_manager.change_frame_to frame_manager.binding_index - inc.to_i
end
elsif match = /^([A-Z]+[^#.]*)(#|\.)(.+)$/.match(inc)
new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :down)
frame_manager.change_frame_to new_frame_index
elsif inc =~ /^[^-].*$/
new_frame_index = find_frame_by_regex(Regexp.new(inc), :down)
frame_manager.change_frame_to new_frame_index
end
end
go_updown(:down, inc)
end
end

Expand All @@ -212,21 +52,11 @@ def process
BANNER

def process
if !frame_manager
raise Pry::CommandError, "nowhere to go!"
if args[0].empty?
frame = PryStackExplorer::Frame.make(target)
output.puts "##{frame_manager.binding_index} #{frame.info(verbose: true)}"
else

if args[0] =~ /\d+/
frame_manager.change_frame_to args[0].to_i
elsif match = /^([A-Z]+[^#.]*)(#|\.)(.+)$/.match(args[0])
new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :up)
frame_manager.change_frame_to new_frame_index
elsif args[0] =~ /^[^-].*$/
new_frame_index = find_frame_by_regex(Regexp.new(args[0]), :up)
frame_manager.change_frame_to new_frame_index
else
output.puts "##{frame_manager.binding_index} #{frame_info(target, true)}"
end
go_updown(:up, args[0])
end
end
end
Expand All @@ -250,18 +80,6 @@ def options(opt)
opt.on :a, :app, "Display application frames only", optional_argument: true
end

def memoized_info(index, b, verbose)
frame_manager.user[:frame_info] ||= Hash.new { |h, k| h[k] = [] }

if verbose
frame_manager.user[:frame_info][:v][index] ||= frame_info(b, verbose)
else
frame_manager.user[:frame_info][:normal][index] ||= frame_info(b, verbose)
end
end

private :memoized_info

# @return [Array<Fixnum, Array<Binding>>] Return tuple of
# base_frame_index and the array of frames.
def selected_stack_frames
Expand Down Expand Up @@ -291,9 +109,8 @@ def selected_stack_frames
private :selected_stack_frames

def process
return no_stack_available! unless frame_manager

title = "Showing all accessible frames in stack (#{frame_manager.bindings.size} in total):"
stack_size = frame_manager.bindings.size
title = "Showing all accessible frames in stack (#{stack_size} in total):"

content = [
bold(title),
Expand All @@ -320,11 +137,25 @@ def frames_with_indices
end
end

ARROW = "=>"
EMPTY = " "

# "=> #0 method_name <Class#method(...)>"
def make_stack_line(b, i, active)
arw = active ? "=>" : " "
arrow = active ? ARROW : EMPTY
frame_no = i.to_s.rjust(2)
frame_info = memoized_frame(i, b).info(verbose: opts[:v])

"#{arw} ##{i} #{memoized_info(i, b, opts[:v])}"
[
arrow,
blue(bold frame_no) + ":",
frame_info,
].join(" ")
end

def memoized_frame(index, b)
frame_manager.user[:frame_info] ||= {}
frame_manager.user[:frame_info][index] ||= PryStackExplorer::Frame.make(b)
end

def offset_frames
Expand All @@ -335,10 +166,6 @@ def offset_frames
end
end

def no_stack_available!
output.puts "No caller stack available!"
end

LOCATION_LAMBDA = ->(_binding){ _binding.source_location[0] }

def app_frames
Expand Down
Loading