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.
@@ -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