pry-stack 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|