pry-stack 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemspec +30 -0
- data/.gemtest +0 -0
- data/.gitignore +7 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/CHANGELOG +0 -0
- data/Gemfile +2 -0
- data/LICENSE +25 -0
- data/README.md +39 -0
- data/Rakefile +30 -0
- data/VERSION +1 -0
- data/examples/example.rb +45 -0
- data/examples/example2.rb +18 -0
- data/examples/example3.rb +46 -0
- data/lib/pry-stack.rb +133 -0
- data/lib/pry-stack/commands.rb +316 -0
- data/lib/pry-stack/frame_manager.rb +85 -0
- data/lib/pry-stack/when_started_hook.rb +94 -0
- data/test/helper.rb +92 -0
- data/test/test_commands.rb +358 -0
- data/test/test_frame_manager.rb +65 -0
- data/test/test_stack.rb +393 -0
- data/tester.rb +27 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 055a3091baf5b3f516b238784efa827b4d8cd615
|
4
|
+
data.tar.gz: fa1f4f9e593c14a613328d159dfe9d6a153b679f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 39af5fb5cc996bd09f4bb2b19ea493dca5c5043c47f3dd0cc68d49c7f5c77403e8966f4b7addf50106aa776b13aaa5c0bf2a409f7ac8f903987bccff1301f313
|
7
|
+
data.tar.gz: 3eaff3b80174d881409ab03911041f16e17d82c623d5b9c27e0b0f4450bc4fd2c64e65d3f93bb0f83d71382dbc4e016c5d00e258806010e604504996bb9a3184
|
data/.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "pry-stack"
|
5
|
+
s.version = File.read("VERSION").strip
|
6
|
+
s.date = File.mtime("VERSION").strftime("%Y-%m-%d")
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
|
+
|
10
|
+
s.authors = ["John Mair (banisterfiend)", "Chris Gahan (epitron)"]
|
11
|
+
s.email = ["jrmair@gmail.com", "chris@ill-logic.com"]
|
12
|
+
s.description = "Walk the stack in Pry, with STYLE."
|
13
|
+
s.summary = "Tired of your 'up' and 'down' commands going in the wrong direction? Longing for a shorter command to show the stack? Are your fingers raw from typing overly long gem names and unnecessary underscores? Then pry-stack is for you!"
|
14
|
+
s.homepage = "https://github.com/epitron/pry-stack"
|
15
|
+
s.licenses = ["MIT"]
|
16
|
+
|
17
|
+
s.files = `git ls`.lines.map(&:strip)
|
18
|
+
s.extra_rdoc_files = ["README.md", "CHANGELOG"]
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.rubygems_version = "2.4.5"
|
22
|
+
|
23
|
+
s.test_files = ["test/helper.rb", "test/test_commands.rb", "test/test_frame_manager.rb", "test/test_stack.rb"]
|
24
|
+
|
25
|
+
s.specification_version = 4
|
26
|
+
s.add_dependency("binding_of_caller", "~> 0.7")
|
27
|
+
s.add_dependency("pry", ["~> 0.9", ">= 0.9.11"])
|
28
|
+
s.add_development_dependency("bacon", "~> 1.1")
|
29
|
+
s.add_development_dependency("rake", "~> 0.9")
|
30
|
+
end
|
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/CHANGELOG
ADDED
File without changes
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
License
|
2
|
+
-------
|
3
|
+
|
4
|
+
(The MIT License)
|
5
|
+
|
6
|
+
Copyright (c) 2011 John Mair (banisterfiend)
|
7
|
+
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
a copy of this software and associated documentation files (the
|
10
|
+
'Software'), to deal in the Software without restriction, including
|
11
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
13
|
+
permit persons to whom the Software is furnished to do so, subject to
|
14
|
+
the following conditions:
|
15
|
+
|
16
|
+
The above copyright notice and this permission notice shall be
|
17
|
+
included in all copies or substantial portions of the Software.
|
18
|
+
|
19
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
20
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
25
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
pry-stack
|
2
|
+
===========
|
3
|
+
|
4
|
+
A Pry plugin to navigate the stack.
|
5
|
+
|
6
|
+
This gem is almost exactly the same as [pry-stack_explorer](http://github.com/pry/pry-stack_explorer), with three exceptions:
|
7
|
+
|
8
|
+
* Its name is shorter
|
9
|
+
* "up" and "down" actually move the stack pointer in those directions
|
10
|
+
* You can type "stack" instead of "show-stack"
|
11
|
+
* It has a fresh pine scent
|
12
|
+
|
13
|
+
License
|
14
|
+
-------
|
15
|
+
|
16
|
+
(The MIT License)
|
17
|
+
|
18
|
+
Copyright (c) 2015 Barry Allard ([steakknife](http://github.com/steakknife))
|
19
|
+
Copyright (c) 2014 Chris Gahan ([epitron](http://github.com/epitron))
|
20
|
+
Copyright (c) 2011 John Mair ([banisterfiend](http://github.com/banisterfiend))
|
21
|
+
|
22
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
23
|
+
a copy of this software and associated documentation files (the
|
24
|
+
'Software'), to deal in the Software without restriction, including
|
25
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
26
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
27
|
+
permit persons to whom the Software is furnished to do so, subject to
|
28
|
+
the following conditions:
|
29
|
+
|
30
|
+
The above copyright notice and this permission notice shall be
|
31
|
+
included in all copies or substantial portions of the Software.
|
32
|
+
|
33
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
34
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
35
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
36
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
37
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
38
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
39
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
gem_version = File.read("VERSION").strip
|
2
|
+
gem_name = "pry-stack"
|
3
|
+
|
4
|
+
desc "Build the gem"
|
5
|
+
task :build do
|
6
|
+
system "gem build .gemspec"
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Build and push the gem to rubygems.org"
|
10
|
+
task :release => :build do
|
11
|
+
system "gem push #{gem_name}-#{gem_version}.gem"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Build and install the gem"
|
15
|
+
task :install => :build do
|
16
|
+
system "gem install #{gem_name}-#{gem_version}.gem"
|
17
|
+
end
|
18
|
+
|
19
|
+
task :default => :test
|
20
|
+
|
21
|
+
desc "Run bacon tests"
|
22
|
+
task :test do
|
23
|
+
sh "bacon -Itest -rubygems -a -q"
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Load this checked out git repo in pry"
|
27
|
+
task :pry do
|
28
|
+
system "pry --gem"
|
29
|
+
end
|
30
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
data/examples/example.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
unless Object.const_defined? :PryStack
|
2
|
+
$:.unshift File.expand_path '../../lib', __FILE__
|
3
|
+
require 'pry'
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'pry-stack'
|
7
|
+
|
8
|
+
def alphabet(y)
|
9
|
+
x = 20
|
10
|
+
b
|
11
|
+
end
|
12
|
+
|
13
|
+
def b
|
14
|
+
x = 30
|
15
|
+
proc {
|
16
|
+
c
|
17
|
+
}.call
|
18
|
+
end
|
19
|
+
|
20
|
+
def c
|
21
|
+
u = 50
|
22
|
+
binding.pry
|
23
|
+
end
|
24
|
+
|
25
|
+
# hello
|
26
|
+
def beta
|
27
|
+
gamma
|
28
|
+
end
|
29
|
+
|
30
|
+
def gamma
|
31
|
+
zeta
|
32
|
+
end
|
33
|
+
|
34
|
+
def zeta
|
35
|
+
vitamin = 100
|
36
|
+
binding.pry
|
37
|
+
end
|
38
|
+
#
|
39
|
+
|
40
|
+
proc {
|
41
|
+
class J
|
42
|
+
alphabet(22)
|
43
|
+
end
|
44
|
+
}.call
|
45
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
unless Object.const_defined? :PryStack
|
2
|
+
$:.unshift File.expand_path '../../lib', __FILE__
|
3
|
+
require 'pry'
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'pry-stack'
|
7
|
+
|
8
|
+
def b
|
9
|
+
x = 30
|
10
|
+
proc {
|
11
|
+
c
|
12
|
+
}.call
|
13
|
+
end
|
14
|
+
|
15
|
+
def c
|
16
|
+
u = 50
|
17
|
+
V.new.beta
|
18
|
+
end
|
19
|
+
|
20
|
+
# hello
|
21
|
+
class V
|
22
|
+
def beta
|
23
|
+
gamma
|
24
|
+
end
|
25
|
+
|
26
|
+
def gamma
|
27
|
+
zeta
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def zeta
|
32
|
+
vitamin = 100
|
33
|
+
binding.pry
|
34
|
+
end
|
35
|
+
#
|
36
|
+
|
37
|
+
proc {
|
38
|
+
class J
|
39
|
+
def alphabet(y)
|
40
|
+
x = 20
|
41
|
+
b
|
42
|
+
end
|
43
|
+
end
|
44
|
+
}.call
|
45
|
+
|
46
|
+
J.new.alphabet(122)
|
data/lib/pry-stack.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# pry-stack.rb
|
2
|
+
# (C) John Mair (banisterfiend); MIT license
|
3
|
+
|
4
|
+
require "pry-stack/commands"
|
5
|
+
require "pry-stack/frame_manager"
|
6
|
+
require "pry-stack/when_started_hook"
|
7
|
+
require "binding_of_caller"
|
8
|
+
|
9
|
+
module PryStack
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# @return [Hash] The hash storing all frames for all Pry instances for
|
13
|
+
# the current thread.
|
14
|
+
def frame_hash
|
15
|
+
Thread.current[:__pry_frame_managers__] ||= Hash.new { |h, k| h[k] = [] }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return the complete frame manager stack for the Pry instance
|
19
|
+
# @param [Pry] _pry_ The Pry instance associated with the frame
|
20
|
+
# managers
|
21
|
+
# @return [Array] The stack of Pry::FrameManager objections
|
22
|
+
def frame_managers(_pry_)
|
23
|
+
frame_hash[_pry_]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a `Pry::FrameManager` object and push it onto the frame
|
27
|
+
# manager stack for the relevant `_pry_` instance.
|
28
|
+
# @param [Array] bindings The array of bindings (frames)
|
29
|
+
# @param [Pry] _pry_ The Pry instance associated with the frame manager
|
30
|
+
def create_and_push_frame_manager(bindings, _pry_, options={})
|
31
|
+
fm = FrameManager.new(bindings, _pry_)
|
32
|
+
frame_hash[_pry_].push fm
|
33
|
+
push_helper(fm, options)
|
34
|
+
fm
|
35
|
+
end
|
36
|
+
|
37
|
+
# Update the Pry instance to operate on the specified frame for the
|
38
|
+
# current frame manager.
|
39
|
+
# @param [PryStack::FrameManager] fm The active frame manager.
|
40
|
+
# @param [Hash] options The options hash.
|
41
|
+
def push_helper(fm, options={})
|
42
|
+
options = {
|
43
|
+
:initial_frame => 0
|
44
|
+
}.merge!(options)
|
45
|
+
|
46
|
+
fm.change_frame_to(options[:initial_frame], false)
|
47
|
+
end
|
48
|
+
|
49
|
+
private :push_helper
|
50
|
+
|
51
|
+
# Delete the currently active frame manager
|
52
|
+
# @param [Pry] _pry_ The Pry instance associated with the frame
|
53
|
+
# managers.
|
54
|
+
# @return [Pry::FrameManager] The popped frame manager.
|
55
|
+
def pop_frame_manager(_pry_)
|
56
|
+
return if frame_managers(_pry_).empty?
|
57
|
+
|
58
|
+
popped_fm = frame_managers(_pry_).pop
|
59
|
+
pop_helper(popped_fm, _pry_)
|
60
|
+
popped_fm
|
61
|
+
end
|
62
|
+
|
63
|
+
# Restore the Pry instance to operate on the previous
|
64
|
+
# binding. Also responsible for restoring Pry instance's backtrace.
|
65
|
+
# @param [Pry::FrameManager] popped_fm The recently popped frame manager.
|
66
|
+
# @param [Pry] _pry_ The Pry instance associated with the frame managers.
|
67
|
+
def pop_helper(popped_fm, _pry_)
|
68
|
+
if frame_managers(_pry_).empty?
|
69
|
+
if _pry_.binding_stack.empty?
|
70
|
+
_pry_.binding_stack.push popped_fm.prior_binding
|
71
|
+
else
|
72
|
+
_pry_.binding_stack[-1] = popped_fm.prior_binding
|
73
|
+
end
|
74
|
+
|
75
|
+
frame_hash.delete(_pry_)
|
76
|
+
else
|
77
|
+
frame_manager(_pry_).refresh_frame(false)
|
78
|
+
end
|
79
|
+
|
80
|
+
# restore backtrace
|
81
|
+
_pry_.backtrace = popped_fm.prior_backtrace
|
82
|
+
end
|
83
|
+
|
84
|
+
private :pop_helper
|
85
|
+
|
86
|
+
# Clear the stack of frame managers for the Pry instance
|
87
|
+
# @param [Pry] _pry_ The Pry instance associated with the frame managers
|
88
|
+
def clear_frame_managers(_pry_)
|
89
|
+
pop_frame_manager(_pry_) until frame_managers(_pry_).empty?
|
90
|
+
frame_hash.delete(_pry_) # this line should be unnecessary!
|
91
|
+
end
|
92
|
+
|
93
|
+
alias_method :delete_frame_managers, :clear_frame_managers
|
94
|
+
|
95
|
+
# @return [PryStack::FrameManager] The currently active frame manager
|
96
|
+
def frame_manager(_pry_)
|
97
|
+
frame_hash[_pry_].last
|
98
|
+
end
|
99
|
+
|
100
|
+
# Simple test to check whether two `Binding` objects are equal.
|
101
|
+
# @param [Binding] b1 First binding.
|
102
|
+
# @param [Binding] b2 Second binding.
|
103
|
+
# @return [Boolean] Whether the `Binding`s are equal.
|
104
|
+
def bindings_equal?(b1, b2)
|
105
|
+
(b1.eval('self').equal?(b2.eval('self'))) &&
|
106
|
+
(b1.eval('__method__') == b2.eval('__method__')) &&
|
107
|
+
(b1.eval('local_variables').map { |v| b1.eval("#{v}") }.equal?(
|
108
|
+
b2.eval('local_variables').map { |v| b2.eval("#{v}") }))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
Pry.config.hooks.add_hook(:after_session, :delete_frame_manager) do |_, _, _pry_|
|
114
|
+
PryStack.clear_frame_managers(_pry_)
|
115
|
+
end
|
116
|
+
|
117
|
+
Pry.config.hooks.add_hook(:when_started, :save_caller_bindings, PryStack::WhenStartedHook.new)
|
118
|
+
|
119
|
+
# Import the Stack commands
|
120
|
+
Pry.config.commands.import PryStack::Commands
|
121
|
+
|
122
|
+
# monkey-patch the whereami command to show some frame information,
|
123
|
+
# useful for navigating stack.
|
124
|
+
Pry.config.commands.before_command("whereami") do |num|
|
125
|
+
if PryStack.frame_manager(_pry_) && !internal_binding?(target)
|
126
|
+
bindings = PryStack.frame_manager(_pry_).bindings
|
127
|
+
binding_index = PryStack.frame_manager(_pry_).binding_index
|
128
|
+
|
129
|
+
output.puts "\n"
|
130
|
+
output.puts "#{Pry::Helpers::Text.bold('Frame number:')} #{binding_index}/#{bindings.size - 1}"
|
131
|
+
output.puts "#{Pry::Helpers::Text.bold('Frame type:')} #{bindings[binding_index].frame_type}" if bindings[binding_index].frame_type
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
module PryStack
|
2
|
+
module FrameHelpers
|
3
|
+
private
|
4
|
+
|
5
|
+
# @return [PryStack::FrameManager] The active frame manager for
|
6
|
+
# the current `Pry` instance.
|
7
|
+
def frame_manager
|
8
|
+
PryStack.frame_manager(_pry_)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [Array<PryStack::FrameManager>] All the frame
|
12
|
+
# managers for the current `Pry` instance.
|
13
|
+
def frame_managers
|
14
|
+
PryStack.frame_managers(_pry_)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Boolean] Whether there is a context to return to once
|
18
|
+
# the current `frame_manager` is popped.
|
19
|
+
def prior_context_exists?
|
20
|
+
frame_managers.count > 1 || frame_manager.prior_binding
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return a description of the frame (binding).
|
24
|
+
# This is only useful for regular old bindings that have not been
|
25
|
+
# enhanced by `#of_caller`.
|
26
|
+
# @param [Binding] b The binding.
|
27
|
+
# @return [String] A description of the frame (binding).
|
28
|
+
def frame_description(b)
|
29
|
+
b_self = b.eval('self')
|
30
|
+
b_method = b.eval('__method__')
|
31
|
+
|
32
|
+
if b_method && b_method != :__binding__ && b_method != :__binding_impl__
|
33
|
+
b_method.to_s
|
34
|
+
elsif b_self.instance_of?(Module)
|
35
|
+
"<module:#{b_self}>"
|
36
|
+
elsif b_self.instance_of?(Class)
|
37
|
+
"<class:#{b_self}>"
|
38
|
+
else
|
39
|
+
"<main>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return a description of the passed binding object. Accepts an
|
44
|
+
# optional `verbose` parameter.
|
45
|
+
# @param [Binding] b The binding.
|
46
|
+
# @param [Boolean] verbose Whether to generate a verbose description.
|
47
|
+
# @return [String] The description of the binding.
|
48
|
+
def frame_info(b, verbose = false)
|
49
|
+
meth = b.eval('__method__')
|
50
|
+
b_self = b.eval('self')
|
51
|
+
meth_obj = Pry::Method.from_binding(b) if meth
|
52
|
+
|
53
|
+
type = b.frame_type ? "[#{b.frame_type}]".ljust(9) : ""
|
54
|
+
desc = b.frame_description ? "#{b.frame_description}" : "#{frame_description(b)}"
|
55
|
+
sig = meth_obj ? "<#{signature_with_owner(meth_obj)}>" : ""
|
56
|
+
|
57
|
+
self_clipped = "#{Pry.view_clip(b_self)}"
|
58
|
+
path = "@ #{b.eval('__FILE__')}:#{b.eval('__LINE__')}"
|
59
|
+
|
60
|
+
if !verbose
|
61
|
+
"#{type} #{desc} #{sig}"
|
62
|
+
else
|
63
|
+
"#{type} #{desc} #{sig}\n in #{self_clipped} #{path}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param [Pry::Method] meth_obj The method object.
|
68
|
+
# @return [String] Signature for the method object in Class#method format.
|
69
|
+
def signature_with_owner(meth_obj)
|
70
|
+
if !meth_obj.undefined?
|
71
|
+
args = meth_obj.parameters.inject([]) do |arr, (type, name)|
|
72
|
+
name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
|
73
|
+
arr << case type
|
74
|
+
when :req then name.to_s
|
75
|
+
when :opt then "#{name}=?"
|
76
|
+
when :rest then "*#{name}"
|
77
|
+
when :block then "&#{name}"
|
78
|
+
else '?'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
"#{meth_obj.name_with_owner}(#{args.join(', ')})"
|
82
|
+
else
|
83
|
+
"#{meth_obj.name_with_owner}(UNKNOWN) (undefined method)"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Regexp.new(args[0])
|
88
|
+
def find_frame_by_regex(regex, up_or_down)
|
89
|
+
frame_index = find_frame_by_block(up_or_down) do |b|
|
90
|
+
b.eval("__method__").to_s =~ regex
|
91
|
+
end
|
92
|
+
|
93
|
+
if frame_index
|
94
|
+
frame_index
|
95
|
+
else
|
96
|
+
raise Pry::CommandError, "No frame that matches #{regex.source} found!"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_frame_by_object_regex(class_regex, method_regex, up_or_down)
|
101
|
+
frame_index = find_frame_by_block(up_or_down) do |b|
|
102
|
+
class_match = b.eval("self.class").to_s =~ class_regex
|
103
|
+
meth_match = b.eval("__method__").to_s =~ method_regex
|
104
|
+
|
105
|
+
class_match && meth_match
|
106
|
+
end
|
107
|
+
|
108
|
+
if frame_index
|
109
|
+
frame_index
|
110
|
+
else
|
111
|
+
raise Pry::CommandError, "No frame that matches #{class_regex.source}" + '#' + "#{method_regex.source} found!"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def find_frame_by_block(up_or_down)
|
116
|
+
start_index = frame_manager.binding_index
|
117
|
+
|
118
|
+
if up_or_down == :down
|
119
|
+
enum = frame_manager.bindings[0..start_index - 1].reverse_each
|
120
|
+
else
|
121
|
+
enum = frame_manager.bindings[start_index + 1..-1]
|
122
|
+
end
|
123
|
+
|
124
|
+
new_frame = enum.find do |b|
|
125
|
+
yield(b)
|
126
|
+
end
|
127
|
+
|
128
|
+
frame_index = frame_manager.bindings.index(new_frame)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
Commands = Pry::CommandSet.new do
|
134
|
+
create_command "up", "Go up to the caller's context." do
|
135
|
+
include FrameHelpers
|
136
|
+
|
137
|
+
banner <<-BANNER
|
138
|
+
Usage: up [OPTIONS]
|
139
|
+
Go up to the caller's context. Accepts optional numeric parameter for how many frames to move up.
|
140
|
+
Also accepts a string (regex) instead of numeric; for jumping to nearest parent method frame which matches the regex.
|
141
|
+
e.g: up #=> Move up 1 stack frame.
|
142
|
+
e.g: up 3 #=> Move up 2 stack frames.
|
143
|
+
e.g: up meth #=> Jump to nearest parent stack frame whose method matches /meth/ regex, i.e `my_method`.
|
144
|
+
BANNER
|
145
|
+
|
146
|
+
def process
|
147
|
+
inc = args.first.nil? ? "1" : args.first
|
148
|
+
|
149
|
+
if !frame_manager
|
150
|
+
raise Pry::CommandError, "Nowhere to go!"
|
151
|
+
else
|
152
|
+
if inc =~ /\d+/
|
153
|
+
frame_manager.change_frame_to frame_manager.binding_index + inc.to_i
|
154
|
+
elsif match = /^([A-Z]+[^#.]*)(#|\.)(.+)$/.match(inc)
|
155
|
+
new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :up)
|
156
|
+
frame_manager.change_frame_to new_frame_index
|
157
|
+
elsif inc =~ /^[^-].*$/
|
158
|
+
new_frame_index = find_frame_by_regex(Regexp.new(inc), :up)
|
159
|
+
frame_manager.change_frame_to new_frame_index
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
create_command "down", "Go down to the callee's context." do
|
166
|
+
include FrameHelpers
|
167
|
+
|
168
|
+
banner <<-BANNER
|
169
|
+
Usage: down [OPTIONS]
|
170
|
+
Go down to the callee's context. Accepts optional numeric parameter for how many frames to move down.
|
171
|
+
Also accepts a string (regex) instead of numeric; for jumping to nearest child method frame which matches the regex.
|
172
|
+
e.g: down #=> Move down 1 stack frame.
|
173
|
+
e.g: down 3 #=> Move down 2 stack frames.
|
174
|
+
e.g: down meth #=> Jump to nearest child stack frame whose method matches /meth/ regex, i.e `my_method`.
|
175
|
+
BANNER
|
176
|
+
|
177
|
+
def process
|
178
|
+
inc = args.first.nil? ? "1" : args.first
|
179
|
+
|
180
|
+
if !frame_manager
|
181
|
+
raise Pry::CommandError, "Nowhere to go!"
|
182
|
+
else
|
183
|
+
if inc =~ /\d+/
|
184
|
+
if frame_manager.binding_index - inc.to_i < 0
|
185
|
+
raise Pry::CommandError, "At bottom of stack, cannot go further!"
|
186
|
+
else
|
187
|
+
frame_manager.change_frame_to frame_manager.binding_index - inc.to_i
|
188
|
+
end
|
189
|
+
elsif match = /^([A-Z]+[^#.]*)(#|\.)(.+)$/.match(inc)
|
190
|
+
new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :down)
|
191
|
+
frame_manager.change_frame_to new_frame_index
|
192
|
+
elsif inc =~ /^[^-].*$/
|
193
|
+
new_frame_index = find_frame_by_regex(Regexp.new(inc), :down)
|
194
|
+
frame_manager.change_frame_to new_frame_index
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
create_command "frame", "Switch to a particular frame." do
|
201
|
+
include FrameHelpers
|
202
|
+
|
203
|
+
banner <<-BANNER
|
204
|
+
Usage: frame [OPTIONS]
|
205
|
+
Switch to a particular frame. Accepts numeric parameter (or regex for method name) for the target frame to switch to (use with show-stack).
|
206
|
+
Negative frame numbers allowed. When given no parameter show information about the current frame.
|
207
|
+
|
208
|
+
e.g: frame 4 #=> jump to the 4th frame
|
209
|
+
e.g: frame meth #=> jump to nearest parent stack frame whose method matches /meth/ regex, i.e `my_method`
|
210
|
+
e.g: frame -2 #=> jump to the second-to-last frame
|
211
|
+
e.g: frame #=> show information info about current frame
|
212
|
+
BANNER
|
213
|
+
|
214
|
+
def process
|
215
|
+
if !frame_manager
|
216
|
+
raise Pry::CommandError, "nowhere to go!"
|
217
|
+
else
|
218
|
+
|
219
|
+
if args[0] =~ /\d+/
|
220
|
+
frame_manager.change_frame_to args[0].to_i
|
221
|
+
elsif match = /^([A-Z]+[^#.]*)(#|\.)(.+)$/.match(args[0])
|
222
|
+
new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :up)
|
223
|
+
frame_manager.change_frame_to new_frame_index
|
224
|
+
elsif args[0] =~ /^[^-].*$/
|
225
|
+
new_frame_index = find_frame_by_regex(Regexp.new(args[0]), :up)
|
226
|
+
frame_manager.change_frame_to new_frame_index
|
227
|
+
else
|
228
|
+
output.puts "##{frame_manager.binding_index} #{frame_info(target, true)}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
create_command "stack", "Show all frames" do
|
235
|
+
include FrameHelpers
|
236
|
+
|
237
|
+
banner <<-BANNER
|
238
|
+
Usage: stack [OPTIONS]
|
239
|
+
Show all accessible stack frames.
|
240
|
+
e.g: show-stack -v
|
241
|
+
BANNER
|
242
|
+
|
243
|
+
def options(opt)
|
244
|
+
opt.on :v, :verbose, "Include extra information."
|
245
|
+
opt.on :H, :head, "Display the first N stack frames (defaults to 10).", :optional_argument => true, :as => Integer, :default => 10
|
246
|
+
opt.on :T, :tail, "Display the last N stack frames (defaults to 10).", :optional_argument => true, :as => Integer, :default => 10
|
247
|
+
opt.on :c, :current, "Display N frames either side of current frame (default to 5).", :optional_argument => true, :as => Integer, :default => 5
|
248
|
+
end
|
249
|
+
|
250
|
+
def memoized_info(index, b, verbose)
|
251
|
+
frame_manager.user[:frame_info] ||= Hash.new { |h, k| h[k] = [] }
|
252
|
+
|
253
|
+
if verbose
|
254
|
+
frame_manager.user[:frame_info][:v][index] ||= frame_info(b, verbose)
|
255
|
+
else
|
256
|
+
frame_manager.user[:frame_info][:normal][index] ||= frame_info(b, verbose)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
private :memoized_info
|
261
|
+
|
262
|
+
# @return [Array<Fixnum, Array<Binding>>] Return tuple of
|
263
|
+
# base_frame_index and the array of frames.
|
264
|
+
def selected_stack_frames
|
265
|
+
if opts.present?(:head)
|
266
|
+
[0, frame_manager.bindings[0..(opts[:head] - 1)]]
|
267
|
+
|
268
|
+
elsif opts.present?(:tail)
|
269
|
+
tail = opts[:tail]
|
270
|
+
if tail > frame_manager.bindings.size
|
271
|
+
tail = frame_manager.bindings.size
|
272
|
+
end
|
273
|
+
|
274
|
+
base_frame_index = frame_manager.bindings.size - tail
|
275
|
+
[base_frame_index, frame_manager.bindings[base_frame_index..-1]]
|
276
|
+
|
277
|
+
elsif opts.present?(:current)
|
278
|
+
first_frame_index = frame_manager.binding_index - (opts[:current])
|
279
|
+
first_frame_index = 0 if first_frame_index < 0
|
280
|
+
last_frame_index = frame_manager.binding_index + (opts[:current])
|
281
|
+
[first_frame_index, frame_manager.bindings[first_frame_index..last_frame_index]]
|
282
|
+
|
283
|
+
else
|
284
|
+
[0, frame_manager.bindings]
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
private :selected_stack_frames
|
289
|
+
|
290
|
+
def process
|
291
|
+
if !frame_manager
|
292
|
+
output.puts "No caller stack available!"
|
293
|
+
else
|
294
|
+
content = ""
|
295
|
+
content << "\n#{text.bold("Showing all accessible frames in stack (#{frame_manager.bindings.size} in total):")}\n--\n"
|
296
|
+
|
297
|
+
base_frame_index, frames = selected_stack_frames
|
298
|
+
frames.each_with_index.to_a.reverse.each do |b, index|
|
299
|
+
i = index + base_frame_index
|
300
|
+
if i == frame_manager.binding_index
|
301
|
+
content << "=> ##{i} #{memoized_info(i, b, opts[:v])}\n"
|
302
|
+
else
|
303
|
+
content << " ##{i} #{memoized_info(i, b, opts[:v])}\n"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
stagger_output content
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
alias_command "show-stack", "stack"
|
314
|
+
|
315
|
+
end
|
316
|
+
end
|