pry-stack 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
File without changes
@@ -0,0 +1,7 @@
1
+ Makefile
2
+ *.so
3
+ *.o
4
+ *.def
5
+ doc/
6
+ pkg/
7
+ .yardoc/
@@ -0,0 +1,15 @@
1
+ script:
2
+ rake test --trace
3
+
4
+ rvm:
5
+ - 1.9.2
6
+ - 1.9.3
7
+
8
+ notifications:
9
+ irc: "irc.freenode.org#pry"
10
+ recipients:
11
+ - jrmair@gmail.com
12
+
13
+ branches:
14
+ only:
15
+ - master
@@ -0,0 +1 @@
1
+ --markup markdown
File without changes
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
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.
@@ -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.
@@ -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
@@ -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,18 @@
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 alpha
9
+ x = "hello"
10
+ beta
11
+ puts x
12
+ end
13
+
14
+ def beta
15
+ binding.pry
16
+ end
17
+
18
+ alpha
@@ -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)
@@ -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