pry-moves 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/README.md +10 -2
- data/lib/commands/debug.rb +5 -1
- data/lib/debug_sugar.rb +11 -10
- data/lib/pry-moves/backtrace.rb +31 -13
- data/lib/pry-moves/bindings_stack.rb +28 -19
- data/lib/pry-moves/error_with_data.rb +10 -0
- data/lib/pry-moves/formatter.rb +4 -0
- data/lib/pry-moves/pry_ext.rb +3 -3
- data/lib/pry-moves/pry_wrapper.rb +2 -2
- data/lib/pry-moves/restartable.rb +4 -3
- data/lib/pry-moves/version.rb +1 -1
- data/lib/pry-moves.rb +18 -7
- data/lib/pry-stack_explorer/frame_helpers.rb +114 -0
- data/lib/pry-stack_explorer/pry-stack_explorer.rb +1 -1
- data/lib/pry-stack_explorer/stack_commands.rb +6 -113
- data/lib/pry-stack_explorer/when_started_hook.rb +1 -1
- data/playground/Gemfile.lock +4 -2
- data/playground/sand.rb +12 -10
- data/pry-moves.gemspec +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9dc991b1aed888cf23fd1e9a76b29d274ce56989119f802982e9fbdc46cc034
|
4
|
+
data.tar.gz: 25e331897d83de799b696e84d1de474166095d4e80853a7f9cde99ba64290432
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbaa701da70c665fe7795de825db1540e5a1ab2d4f2021c41d4294c14bce3429b34b7e64d21949f51fd08dbbc24b6ffc047776bb7207b8c73834893e0e03ced7
|
7
|
+
data.tar.gz: 169ee4d3b3e40f8f63c7969c2b709c82dfc23881c994a335b25635fbf90b798ea21616bc33c08dd0bd44418a2c996f0b5eb6079732d769ff5c061757af35f5e7
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pry-moves (1.0.
|
4
|
+
pry-moves (1.0.2)
|
5
5
|
binding_of_caller (~> 0.7)
|
6
6
|
colorize (~> 0.8)
|
7
|
+
diffy (~> 3.4.0)
|
7
8
|
pry (>= 0.10.4, < 0.13)
|
8
9
|
|
9
10
|
GEM
|
@@ -15,6 +16,7 @@ GEM
|
|
15
16
|
colorize (0.8.1)
|
16
17
|
debug_inspector (1.1.0)
|
17
18
|
diff-lcs (1.3)
|
19
|
+
diffy (3.4.0)
|
18
20
|
method_source (0.9.0)
|
19
21
|
pry (0.11.3)
|
20
22
|
coderay (~> 1.1.0)
|
data/README.md
CHANGED
@@ -25,10 +25,14 @@ Documentation for latest version. For [v0.1.12 see documentation here](https://g
|
|
25
25
|
* `bt +` `bt hidden` - show backtrace including hidden frames
|
26
26
|
* `bt a` `bt all` - full backtrace with system and hidden frames
|
27
27
|
* `bt 10` - go to backtrace line 10
|
28
|
+
* `bt save` - save backtrace to memory
|
29
|
+
* `bt diff` - diff with saved backtrace
|
28
30
|
* `bt > foo` - write backtrace to file `log/backtrace_foo.log`
|
29
|
-
* `up`/`down`/`top`/`bottom` - move over call stack
|
31
|
+
* `up`/`down`/`top`/`bottom` (`bm`) - move over call stack
|
30
32
|
* `up +` - move up, including vapid frames (block callers, hidden frames)
|
31
33
|
* `up pattern` - move up till first frame which method name or file position in format `folder/script.rb:12` matches regexp pattern
|
34
|
+
* `%` - print current frame of call stack (alias to `whereami`)
|
35
|
+
* `$` - fully print current function without line numbers
|
32
36
|
* `debug some_method(some_param)` - call `some_method(some_param)` and interactively step into it. This way you can virtually "step back" by executing previous pieces of code from current method
|
33
37
|
* `.method` or `123` or `:hash_key` - Continue traversing of last object in history. E.g. `orders` will list array, then `3` will enter `orders[3]`, then `.price` will enter `orders[3].price`
|
34
38
|
* `watch variable` - display variable's value on each step
|
@@ -58,7 +62,7 @@ _Demo class source [here](https://github.com/garmoshka-mo/pry-moves/issues/1)_
|
|
58
62
|
|
59
63
|
## Backtrace and call stack
|
60
64
|
|
61
|
-
You can explicitly hide frames from
|
65
|
+
You can explicitly hide frames from call stack by defining variables like this:
|
62
66
|
|
63
67
|
```ruby
|
64
68
|
def insignificant_method
|
@@ -68,6 +72,10 @@ def insignificant_method
|
|
68
72
|
end
|
69
73
|
```
|
70
74
|
|
75
|
+
* `hide_from_stack` - hide this function from stack
|
76
|
+
* `pry_moves_stack_tip` - stop on first frame above this function
|
77
|
+
* `pry_moves_stack_end` - limits stack from bottom, not possible to step below this frame
|
78
|
+
|
71
79
|
## Configuration
|
72
80
|
|
73
81
|
Here is default configuration, you can reassign it:
|
data/lib/commands/debug.rb
CHANGED
@@ -5,8 +5,12 @@ class PryMoves::Debug < PryMoves::TraceCommand
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def trace(event, file, line, method, binding_)
|
8
|
-
return
|
8
|
+
return if event != 'line' or @cancel_debug
|
9
9
|
if @first_line_skipped
|
10
|
+
if binding_.local_variable_defined?(:pry_cancel_debug)
|
11
|
+
@cancel_debug = true
|
12
|
+
return
|
13
|
+
end
|
10
14
|
true
|
11
15
|
else
|
12
16
|
@first_line_skipped = true
|
data/lib/debug_sugar.rb
CHANGED
@@ -1,36 +1,37 @@
|
|
1
1
|
def debug *args
|
2
|
-
|
2
|
+
pry_moves_stack_end = true
|
3
3
|
PryMoves.debug *args
|
4
4
|
end
|
5
5
|
|
6
|
-
def error(msg, debug_object = nil)
|
7
|
-
|
6
|
+
def error(msg = "Error", debug_object = nil)
|
7
|
+
pry_moves_stack_end = true
|
8
8
|
err = "😱 #{msg}"
|
9
9
|
unless PryMoves.open?
|
10
10
|
if PryMoves.stop_on_breakpoints
|
11
11
|
lines = [err.red]
|
12
12
|
lines.prepend debug_object.ai if debug_object
|
13
|
-
PryMoves.
|
13
|
+
PryMoves.error lines.join("\n")
|
14
14
|
else
|
15
15
|
STDERR.puts debug_object.ai if debug_object
|
16
16
|
STDERR.puts err.ljust(80, ' ').red
|
17
17
|
end
|
18
18
|
end
|
19
|
-
raise msg
|
19
|
+
raise PryMoves::ErrorWithData.new(msg, debug_object)
|
20
20
|
end
|
21
21
|
|
22
22
|
def shit!(err = 'Oh, shit!', debug_object = nil)
|
23
|
-
|
23
|
+
return if ENV['NO_SHIT']
|
24
|
+
pry_moves_stack_end = true
|
24
25
|
message = "💩 #{err.is_a?(String) ? err : err.message}"
|
25
26
|
raise err unless PryMoves.stop_on_breakpoints
|
26
27
|
lines = [message.red]
|
27
28
|
lines.prepend debug_object.ai if debug_object
|
28
|
-
PryMoves.
|
29
|
+
PryMoves.error lines.join("\n")
|
29
30
|
nil
|
30
31
|
end
|
31
32
|
|
32
33
|
def required(var)
|
33
|
-
|
34
|
+
pry_moves_stack_end = true
|
34
35
|
error("required parameter is missing") if var.nil?
|
35
36
|
var
|
36
37
|
end
|
@@ -44,7 +45,7 @@ RSpec.configure do |config|
|
|
44
45
|
end
|
45
46
|
|
46
47
|
config.around(:each) do |example|
|
47
|
-
PryMoves.restartable do
|
48
|
+
PryMoves.restartable(rspec_example: example) do
|
48
49
|
example.run
|
49
50
|
end
|
50
51
|
end
|
@@ -57,7 +58,7 @@ Rake::Task.class_eval do
|
|
57
58
|
|
58
59
|
def execute(args=nil)
|
59
60
|
args ||= EMPTY_TASK_ARGS
|
60
|
-
PryMoves.restartable do
|
61
|
+
PryMoves.restartable(rake_args: args) do
|
61
62
|
reload_actions if PryMoves.reload_rake_tasks
|
62
63
|
execute_origin_for_pry_moves args
|
63
64
|
end
|
data/lib/pry-moves/backtrace.rb
CHANGED
@@ -29,10 +29,17 @@ class PryMoves::Backtrace
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def run_command(param, param2)
|
32
|
-
if param
|
32
|
+
if param == 'save'
|
33
|
+
@filter = 'hidden'
|
34
|
+
@@backtrace = build_backtrace
|
35
|
+
@pry.output.puts "💾 Backtrace saved (#{@@backtrace.count} lines)"
|
36
|
+
elsif param == 'diff'
|
37
|
+
@filter = 'hidden'
|
38
|
+
diff
|
39
|
+
elsif param.is_a?(String) and (match = param.match /^>(.*)/)
|
33
40
|
suffix = match[1].size > 0 ? match[1] : param2
|
34
41
|
@formatter.colorize = false
|
35
|
-
write_to_file
|
42
|
+
write_to_file build_backtrace, suffix
|
36
43
|
elsif param and param.match /\d+/
|
37
44
|
index = param.to_i
|
38
45
|
frame_manager.goto_index index
|
@@ -47,10 +54,10 @@ class PryMoves::Backtrace
|
|
47
54
|
@colorize = true
|
48
55
|
@lines_numbers = true
|
49
56
|
@filter = filter if filter.is_a? String
|
50
|
-
@pry.output.puts
|
57
|
+
@pry.output.puts build_backtrace
|
51
58
|
end
|
52
59
|
|
53
|
-
def
|
60
|
+
def build_backtrace
|
54
61
|
show_all = %w(a all).include?(@filter)
|
55
62
|
show_vapid = %w(+ hidden vapid).include?(@filter) || show_all
|
56
63
|
result = []
|
@@ -58,10 +65,10 @@ class PryMoves::Backtrace
|
|
58
65
|
|
59
66
|
recursion = PryMoves::Recursion::Holder.new
|
60
67
|
|
61
|
-
frame_manager.bindings.
|
68
|
+
frame_manager.bindings.reverse.each do |binding|
|
62
69
|
next if !show_all and binding.eval('__FILE__').match self.class::filter
|
63
70
|
|
64
|
-
if !show_vapid and
|
71
|
+
if !show_vapid and binding.hidden
|
65
72
|
vapid_count += 1
|
66
73
|
next
|
67
74
|
end
|
@@ -83,7 +90,7 @@ class PryMoves::Backtrace
|
|
83
90
|
result << build_line(binding, file, line)
|
84
91
|
end
|
85
92
|
|
86
|
-
recursion.each { |t| t.apply result }
|
93
|
+
# recursion.each { |t| t.apply result }
|
87
94
|
|
88
95
|
result << "👽 frames hidden: #{vapid_count}" if vapid_count > 0
|
89
96
|
|
@@ -125,23 +132,34 @@ class PryMoves::Backtrace
|
|
125
132
|
"#{root}/backtrace_#{file_suffix}.log"
|
126
133
|
end
|
127
134
|
|
135
|
+
def diff
|
136
|
+
return STDERR.puts "No backtrace saved. Use `bt save` first".yellow unless defined? @@backtrace
|
137
|
+
|
138
|
+
diff = Diffy::Diff.new(@@backtrace.join("\n"), build_backtrace.join("\n")).to_s "color"
|
139
|
+
diff = 'Backtraces are equal' if diff.strip.empty?
|
140
|
+
@pry.output.puts diff
|
141
|
+
end
|
142
|
+
|
128
143
|
end
|
129
144
|
|
130
145
|
Pry.config.exception_handler = proc do |output, exception, _|
|
146
|
+
|
147
|
+
def print_error message, exception, output
|
148
|
+
output.puts message.red
|
149
|
+
exception.backtrace.reject! {|l| l.match /sugar\.rb/}
|
150
|
+
exception.backtrace.first(3).each { output.puts _1.white }
|
151
|
+
end
|
152
|
+
|
131
153
|
if Pry::UserError === exception && SyntaxError === exception
|
132
154
|
output.puts "SyntaxError: #{exception.message.sub(/.*syntax error, */m, '')}"
|
133
155
|
else
|
134
156
|
|
135
|
-
|
136
|
-
exception.backtrace.reject! {|l| l.match /sugar\.rb/}
|
137
|
-
output.puts "from #{exception.backtrace.first}"
|
157
|
+
print_error "#{exception.class}: #{exception.message}", exception, output
|
138
158
|
|
139
159
|
if exception.respond_to? :cause
|
140
160
|
cause = exception.cause
|
141
161
|
while cause
|
142
|
-
|
143
|
-
cause.backtrace.reject! {|l| l.match /sugar\.rb/}
|
144
|
-
output.puts "from #{cause.backtrace.first}"
|
162
|
+
print_error "Caused by #{cause.class}: #{cause}\n", exception, output
|
145
163
|
cause = cause.cause
|
146
164
|
end
|
147
165
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class PryMoves::BindingsStack < Array
|
2
2
|
|
3
|
-
def initialize
|
4
|
-
@
|
3
|
+
def initialize options
|
4
|
+
@options = options
|
5
5
|
bindings = binding.callers # binding_of_caller has bug and always returns callers of current binding,
|
6
6
|
# no matter at which binding method is called. So no need to pass here binding
|
7
7
|
pre_callers = Thread.current[:pre_callers]
|
@@ -14,20 +14,16 @@ class PryMoves::BindingsStack < Array
|
|
14
14
|
def suggest_initial_frame_index
|
15
15
|
m = PryMoves::TracedMethod.last
|
16
16
|
return 0 if m and m.binding_inside?(first)
|
17
|
-
index{|b|
|
17
|
+
index {|b| for_for_initial_frame b} || 0
|
18
18
|
end
|
19
19
|
def initial_frame
|
20
|
-
find{|b|
|
20
|
+
find {|b| for_for_initial_frame b}
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def vapid?(binding)
|
30
|
-
@vapid_bindings.include? binding
|
23
|
+
def for_for_initial_frame b
|
24
|
+
not b.hidden and (
|
25
|
+
@options[:is_error] or b.eval("__method__") != :initialize
|
26
|
+
)
|
31
27
|
end
|
32
28
|
|
33
29
|
private
|
@@ -47,25 +43,35 @@ class PryMoves::BindingsStack < Array
|
|
47
43
|
file, method, obj = binding.eval("[__FILE__, __method__, self]")
|
48
44
|
|
49
45
|
if file.match PryMoves::Backtrace::filter
|
50
|
-
|
46
|
+
binding.hidden = true
|
51
47
|
elsif stepped_out
|
52
48
|
if actual_file == file and actual_method == method or
|
53
49
|
binding.local_variable_defined? :pry_moves_deferred_call
|
54
50
|
stepped_out = false
|
55
51
|
else
|
56
|
-
|
52
|
+
binding.hidden = true
|
57
53
|
end
|
58
54
|
elsif binding.frame_type == :block
|
59
55
|
stepped_out = true
|
60
56
|
actual_file = file
|
61
57
|
actual_method = method
|
62
58
|
elsif obj and method and obj.method(method).source.strip.match /^delegate\s/
|
63
|
-
|
59
|
+
binding.hidden = true
|
64
60
|
end
|
65
61
|
|
66
62
|
if binding.local_variable_defined? :hide_from_stack
|
67
|
-
|
63
|
+
binding.hidden = true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
stack_tip_met = false
|
68
|
+
stack_tips = PryMoves.stack_tips || []
|
69
|
+
reverse.each do |binding|
|
70
|
+
if binding.local_variable_defined?(:pry_moves_stack_tip) ||
|
71
|
+
stack_tips.include?(binding.eval("__method__"))
|
72
|
+
stack_tip_met = true
|
68
73
|
end
|
74
|
+
binding.hidden = true if stack_tip_met
|
69
75
|
end
|
70
76
|
end
|
71
77
|
|
@@ -82,14 +88,17 @@ class PryMoves::BindingsStack < Array
|
|
82
88
|
end
|
83
89
|
|
84
90
|
def top_internal_frame_index(bindings)
|
91
|
+
pry_moves_debug = Thread.current[:pry_moves_debug]
|
85
92
|
bindings.rindex do |b|
|
86
|
-
if b.frame_type == :
|
87
|
-
|
93
|
+
if not pry_moves_debug and b.frame_type == :eval
|
94
|
+
true
|
95
|
+
elsif b.frame_type == :method
|
96
|
+
method, self_ = b.eval("[__method__, self, __FILE__]")
|
88
97
|
|
89
98
|
self_.equal?(Pry) && method == :start ||
|
90
99
|
self_.class == Binding && method == :pry ||
|
91
100
|
self_.is_a?(PryMoves::TraceCommand) && method == :tracing_func ||
|
92
|
-
b.local_variable_defined?(:
|
101
|
+
b.local_variable_defined?(:pry_moves_stack_end)
|
93
102
|
end
|
94
103
|
end
|
95
104
|
end
|
data/lib/pry-moves/formatter.rb
CHANGED
@@ -6,6 +6,7 @@ class PryMoves::Formatter
|
|
6
6
|
@colorize = colorize
|
7
7
|
end
|
8
8
|
|
9
|
+
MAX_PARAMS = 5
|
9
10
|
def method_signature(binding)
|
10
11
|
meth = binding.eval('__method__')
|
11
12
|
meth_obj = meth ? Pry::Method.from_binding(binding) : nil
|
@@ -31,6 +32,9 @@ class PryMoves::Formatter
|
|
31
32
|
end
|
32
33
|
show_value ? "#{name} #{value}" : name
|
33
34
|
end
|
35
|
+
if args.count > MAX_PARAMS
|
36
|
+
args = args.first(MAX_PARAMS) + ["(#{args.count - MAX_PARAMS} more params)…"]
|
37
|
+
end
|
34
38
|
"#{meth_obj.name}(#{args.join(', ')})"
|
35
39
|
end
|
36
40
|
end
|
data/lib/pry-moves/pry_ext.rb
CHANGED
@@ -18,15 +18,15 @@ end
|
|
18
18
|
|
19
19
|
Binding.class_eval do
|
20
20
|
|
21
|
-
attr_accessor :index
|
21
|
+
attr_accessor :index, :hidden
|
22
22
|
|
23
23
|
alias pry_forced pry
|
24
24
|
|
25
|
-
def pry
|
25
|
+
def pry options = nil
|
26
26
|
if !Pry.config.disable_breakpoints and
|
27
27
|
# Don't start binding.pry when semaphore locked by current thread
|
28
28
|
PryMoves.synchronize_threads
|
29
|
-
pry_forced
|
29
|
+
pry_forced options
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -11,7 +11,7 @@ class PryWrapper
|
|
11
11
|
def run
|
12
12
|
PryMoves.lock
|
13
13
|
|
14
|
-
initial_frame = PryMoves::BindingsStack.new.initial_frame
|
14
|
+
initial_frame = PryMoves::BindingsStack.new(@pry_start_options).initial_frame
|
15
15
|
if not @pry_start_options[:pry_moves_loop] and
|
16
16
|
initial_frame.local_variable_defined? :debug_redirect
|
17
17
|
debug_redirect = initial_frame.local_variable_get(:debug_redirect)
|
@@ -76,7 +76,7 @@ class PryWrapper
|
|
76
76
|
@command[:binding].eval @command[:param]
|
77
77
|
rescue StandardError, SyntaxError => e
|
78
78
|
Thread.current.set_trace_func nil
|
79
|
-
puts "❌️ #{e}"
|
79
|
+
puts "❌️ Error during #{"debug".yellow} execution: #{e}"
|
80
80
|
end
|
81
81
|
tracer.stop_tracing
|
82
82
|
end.join
|
@@ -3,14 +3,15 @@ module PryMoves::Restartable
|
|
3
3
|
attr_accessor :restart_requested, :reload_requested,
|
4
4
|
:reload_rake_tasks
|
5
5
|
|
6
|
-
def restartable
|
7
|
-
trigger :
|
6
|
+
def restartable context
|
7
|
+
trigger :each_new_run, context
|
8
8
|
yield
|
9
9
|
re_execution
|
10
10
|
rescue PryMoves::Restart
|
11
|
+
puts "🔄️ Restarting execution"
|
11
12
|
self.restart_requested = false
|
12
13
|
PryMoves.reset
|
13
|
-
trigger :restart
|
14
|
+
trigger :restart, context
|
14
15
|
retry
|
15
16
|
rescue PryMoves::Reload
|
16
17
|
puts "🔮 try to use @ with reload"
|
data/lib/pry-moves/version.rb
CHANGED
data/lib/pry-moves.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'pry' unless defined? Pry
|
2
|
+
require 'colorize'
|
3
|
+
require 'diffy'
|
2
4
|
|
3
5
|
require 'pry-moves/version'
|
4
6
|
require 'pry-moves/pry_ext'
|
@@ -12,6 +14,7 @@ require 'pry-moves/watch'
|
|
12
14
|
require 'pry-moves/painter'
|
13
15
|
require 'pry-moves/restartable'
|
14
16
|
require 'pry-moves/recursion_tracker'
|
17
|
+
require 'pry-moves/error_with_data'
|
15
18
|
|
16
19
|
require 'commands/traced_method'
|
17
20
|
require 'commands/trace_helpers'
|
@@ -36,7 +39,7 @@ module PryMoves
|
|
36
39
|
extend self
|
37
40
|
extend PryMoves::Restartable
|
38
41
|
|
39
|
-
attr_accessor :is_open, :trace,
|
42
|
+
attr_accessor :is_open, :trace, :stack_tips,
|
40
43
|
:stop_on_breakpoints, :launched_specs_examples, :debug_called_times
|
41
44
|
|
42
45
|
def reset
|
@@ -45,20 +48,26 @@ module PryMoves
|
|
45
48
|
self.debug_called_times = 0
|
46
49
|
end
|
47
50
|
|
48
|
-
def debug(message = nil, at: nil)
|
49
|
-
|
51
|
+
def debug(message = nil, at: nil, options: nil)
|
52
|
+
pry_moves_stack_end = true
|
50
53
|
PryMoves.re_execution
|
51
54
|
if PryMoves.stop_on_breakpoints
|
52
55
|
self.debug_called_times += 1
|
53
56
|
if at
|
54
57
|
return unless self.debug_called_times == at
|
55
58
|
end
|
56
|
-
|
57
|
-
|
59
|
+
if message
|
60
|
+
PryMoves.messages << (message.is_a?(String) ? message : message.ai)
|
61
|
+
end
|
62
|
+
binding.pry options
|
58
63
|
PryMoves.re_execution
|
59
64
|
end
|
60
65
|
end
|
61
66
|
|
67
|
+
def error(message)
|
68
|
+
debug message, options: {is_error: true}
|
69
|
+
end
|
70
|
+
|
62
71
|
# Checks that a binding is in a local file context. Extracted from
|
63
72
|
# https://github.com/pry/pry/blob/master/lib/pry/default_commands/context.rb
|
64
73
|
def check_file_context(target)
|
@@ -102,8 +111,8 @@ module PryMoves
|
|
102
111
|
true
|
103
112
|
end
|
104
113
|
|
105
|
-
def trigger(event)
|
106
|
-
triggers[event].each
|
114
|
+
def trigger(event, context)
|
115
|
+
triggers[event].each {|t| t.call context}
|
107
116
|
end
|
108
117
|
|
109
118
|
def triggers
|
@@ -112,7 +121,9 @@ module PryMoves
|
|
112
121
|
end
|
113
122
|
end
|
114
123
|
|
124
|
+
TRIGGERS = [:each_new_run, :restart]
|
115
125
|
def on(trigger, &block)
|
126
|
+
error "Invalid trigger, possible triggers: #{TRIGGERS}", trigger unless trigger.in? TRIGGERS
|
116
127
|
triggers[trigger] << block
|
117
128
|
end
|
118
129
|
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module PryStackExplorer; end
|
2
|
+
module PryStackExplorer::FrameHelpers
|
3
|
+
private
|
4
|
+
|
5
|
+
# @return [PryStackExplorer::FrameManager] The active frame manager for
|
6
|
+
# the current `Pry` instance.
|
7
|
+
def frame_manager
|
8
|
+
PryStackExplorer.frame_manager(_pry_)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [Array<PryStackExplorer::FrameManager>] All the frame
|
12
|
+
# managers for the current `Pry` instance.
|
13
|
+
def frame_managers
|
14
|
+
PryStackExplorer.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
|
+
b_self = b.eval('self')
|
50
|
+
type = b.frame_type ? "[#{b.frame_type}]".ljust(9) : ""
|
51
|
+
desc = b.frame_description ? "#{b.frame_description}" : "#{frame_description(b)}"
|
52
|
+
sig = PryMoves::Formatter.new.method_signature b
|
53
|
+
|
54
|
+
self_clipped = "#{Pry.view_clip(b_self)}"
|
55
|
+
path = "@ #{b.eval('__FILE__')}:#{b.eval('__LINE__')}"
|
56
|
+
|
57
|
+
if !verbose
|
58
|
+
"#{type} #{desc} #{sig}"
|
59
|
+
else
|
60
|
+
"#{type} #{desc} #{sig}\n in #{self_clipped} #{path}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_frame_by_regex(regex, up_or_down)
|
65
|
+
frame_index = find_frame_by_block(up_or_down) do |b|
|
66
|
+
(b.eval('"#{__FILE__}:#{__LINE__}"') =~ regex) or
|
67
|
+
(b.eval("__method__").to_s =~ regex)
|
68
|
+
end
|
69
|
+
|
70
|
+
frame_index || raise(Pry::CommandError, "No frame that matches #{regex.source} found")
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_frame_by_block(up_or_down)
|
74
|
+
start_index = frame_manager.binding_index
|
75
|
+
|
76
|
+
if up_or_down == :down
|
77
|
+
enum = start_index == 0 ? [].each :
|
78
|
+
frame_manager.bindings[0..start_index - 1].reverse_each
|
79
|
+
else
|
80
|
+
enum = frame_manager.bindings[start_index + 1..-1]
|
81
|
+
end
|
82
|
+
|
83
|
+
new_frame = enum.find do |b|
|
84
|
+
yield(b)
|
85
|
+
end
|
86
|
+
|
87
|
+
frame_manager.bindings.index(new_frame)
|
88
|
+
end
|
89
|
+
|
90
|
+
def find_frame_by_direction(dir, step_into_vapid: false)
|
91
|
+
frame_index = find_frame_by_block(dir) do |b|
|
92
|
+
step_into_vapid or not b.hidden
|
93
|
+
end
|
94
|
+
|
95
|
+
if !frame_index and !step_into_vapid
|
96
|
+
frame_index = find_frame_by_block(dir) {true}
|
97
|
+
end
|
98
|
+
|
99
|
+
frame_index ||
|
100
|
+
raise(Pry::CommandError, "At #{dir == :up ? 'top' : 'bottom'} of stack, cannot go further")
|
101
|
+
end
|
102
|
+
|
103
|
+
def move(direction, param)
|
104
|
+
raise Pry::CommandError, "Nowhere to go" unless frame_manager
|
105
|
+
|
106
|
+
if param == '+' or param.nil?
|
107
|
+
index = find_frame_by_direction direction, step_into_vapid: param == '+'
|
108
|
+
frame_manager.change_frame_to index
|
109
|
+
else
|
110
|
+
index = find_frame_by_regex(Regexp.new(param), direction)
|
111
|
+
frame_manager.change_frame_to index
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -121,5 +121,5 @@ end
|
|
121
121
|
Pry.config.hooks.add_hook(:when_started, :save_caller_bindings, PryStackExplorer::WhenStartedHook.new)
|
122
122
|
|
123
123
|
# Import the StackExplorer commands
|
124
|
-
Pry.config.commands.import PryStackExplorer::
|
124
|
+
Pry.config.commands.import PryStackExplorer::COMMANDS
|
125
125
|
|
@@ -1,121 +1,13 @@
|
|
1
|
-
|
2
|
-
module FrameHelpers
|
3
|
-
private
|
4
|
-
|
5
|
-
# @return [PryStackExplorer::FrameManager] The active frame manager for
|
6
|
-
# the current `Pry` instance.
|
7
|
-
def frame_manager
|
8
|
-
PryStackExplorer.frame_manager(_pry_)
|
9
|
-
end
|
10
|
-
|
11
|
-
# @return [Array<PryStackExplorer::FrameManager>] All the frame
|
12
|
-
# managers for the current `Pry` instance.
|
13
|
-
def frame_managers
|
14
|
-
PryStackExplorer.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
|
-
b_self = b.eval('self')
|
50
|
-
type = b.frame_type ? "[#{b.frame_type}]".ljust(9) : ""
|
51
|
-
desc = b.frame_description ? "#{b.frame_description}" : "#{frame_description(b)}"
|
52
|
-
sig = PryMoves::Formatter.new.method_signature b
|
53
|
-
|
54
|
-
self_clipped = "#{Pry.view_clip(b_self)}"
|
55
|
-
path = "@ #{b.eval('__FILE__')}:#{b.eval('__LINE__')}"
|
56
|
-
|
57
|
-
if !verbose
|
58
|
-
"#{type} #{desc} #{sig}"
|
59
|
-
else
|
60
|
-
"#{type} #{desc} #{sig}\n in #{self_clipped} #{path}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def find_frame_by_regex(regex, up_or_down)
|
65
|
-
frame_index = find_frame_by_block(up_or_down) do |b|
|
66
|
-
(b.eval('"#{__FILE__}:#{__LINE__}"') =~ regex) or
|
67
|
-
(b.eval("__method__").to_s =~ regex)
|
68
|
-
end
|
69
|
-
|
70
|
-
frame_index || raise(Pry::CommandError, "No frame that matches #{regex.source} found")
|
71
|
-
end
|
1
|
+
require_relative 'frame_helpers.rb'
|
72
2
|
|
73
|
-
|
74
|
-
start_index = frame_manager.binding_index
|
75
|
-
|
76
|
-
if up_or_down == :down
|
77
|
-
enum = start_index == 0 ? [].each :
|
78
|
-
frame_manager.bindings[0..start_index - 1].reverse_each
|
79
|
-
else
|
80
|
-
enum = frame_manager.bindings[start_index + 1..-1]
|
81
|
-
end
|
82
|
-
|
83
|
-
new_frame = enum.find do |b|
|
84
|
-
yield(b)
|
85
|
-
end
|
86
|
-
|
87
|
-
frame_manager.bindings.index(new_frame)
|
88
|
-
end
|
89
|
-
|
90
|
-
def find_frame_by_direction(dir, step_into_vapid: false)
|
91
|
-
frame_index = find_frame_by_block(dir) do |b|
|
92
|
-
step_into_vapid or
|
93
|
-
not frame_manager.bindings.vapid?(b)
|
94
|
-
end
|
95
|
-
|
96
|
-
if !frame_index and !step_into_vapid
|
97
|
-
frame_index = find_frame_by_block(dir) {true}
|
98
|
-
end
|
99
|
-
|
100
|
-
frame_index ||
|
101
|
-
raise(Pry::CommandError, "At #{dir == :up ? 'top' : 'bottom'} of stack, cannot go further")
|
102
|
-
end
|
3
|
+
module PryStackExplorer
|
103
4
|
|
104
|
-
|
105
|
-
raise Pry::CommandError, "Nowhere to go" unless frame_manager
|
5
|
+
COMMANDS = Pry::CommandSet.new do
|
106
6
|
|
107
|
-
|
108
|
-
|
109
|
-
frame_manager.change_frame_to index
|
110
|
-
else
|
111
|
-
index = find_frame_by_regex(Regexp.new(param), direction)
|
112
|
-
frame_manager.change_frame_to index
|
113
|
-
end
|
7
|
+
block_command '%', 'Print current stack frame' do
|
8
|
+
run 'whereami'
|
114
9
|
end
|
115
|
-
end
|
116
|
-
|
117
10
|
|
118
|
-
Commands = Pry::CommandSet.new do
|
119
11
|
create_command "up", "Go up to the caller's context." do
|
120
12
|
include FrameHelpers
|
121
13
|
|
@@ -163,6 +55,7 @@ module PryStackExplorer
|
|
163
55
|
frame_manager.change_frame_to 0
|
164
56
|
end
|
165
57
|
end
|
58
|
+
alias_command 'bm', 'bottom'
|
166
59
|
|
167
60
|
create_command "frame", "Switch to a particular frame." do
|
168
61
|
include FrameHelpers
|
@@ -22,7 +22,7 @@ module PryStackExplorer
|
|
22
22
|
raise ArgumentError, ":call_stack must be an array of bindings"
|
23
23
|
end
|
24
24
|
else
|
25
|
-
bindings = PryMoves::BindingsStack.new
|
25
|
+
bindings = PryMoves::BindingsStack.new options
|
26
26
|
initial_frame ||= bindings.suggest_initial_frame_index
|
27
27
|
# if Thread.current[:pry_moves_debug] and initial_frame > 0
|
28
28
|
if initial_frame > 0
|
data/playground/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
pry-moves (1.0.
|
4
|
+
pry-moves (1.0.1)
|
5
5
|
binding_of_caller (~> 0.7)
|
6
6
|
colorize (~> 0.8)
|
7
|
+
diffy
|
7
8
|
pry (>= 0.10.4, < 0.13)
|
8
9
|
|
9
10
|
GEM
|
@@ -13,7 +14,8 @@ GEM
|
|
13
14
|
debug_inspector (>= 0.0.1)
|
14
15
|
coderay (1.1.3)
|
15
16
|
colorize (0.8.1)
|
16
|
-
debug_inspector (
|
17
|
+
debug_inspector (1.1.0)
|
18
|
+
diffy (3.4.0)
|
17
19
|
method_source (0.9.2)
|
18
20
|
pry (0.12.2)
|
19
21
|
coderay (~> 1.1.0)
|
data/playground/sand.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# cd playground
|
2
|
+
# be ruby sand.rb
|
3
|
+
|
1
4
|
require 'pry-moves'
|
2
5
|
require './tracer.rb'
|
3
6
|
|
@@ -7,42 +10,41 @@ def fi(param)
|
|
7
10
|
puts param
|
8
11
|
end
|
9
12
|
|
10
|
-
class
|
13
|
+
class Sand
|
11
14
|
|
12
15
|
def initialize
|
13
16
|
puts :xuilo
|
14
17
|
end
|
15
18
|
|
16
|
-
def
|
19
|
+
def some_logic
|
17
20
|
puts 'aa: step 1'
|
18
21
|
puts 'aa: step 2'
|
19
22
|
end
|
20
23
|
|
21
|
-
def
|
24
|
+
def method_with_hidden_stack
|
22
25
|
debug_redirect = :aa
|
23
26
|
hide_from_stack = true
|
24
27
|
puts 'bb: step 1'
|
25
28
|
puts 'bb: step 2'
|
26
|
-
|
29
|
+
some_logic
|
27
30
|
end
|
28
31
|
|
29
|
-
def
|
32
|
+
def debugged_method
|
30
33
|
koko = :love
|
31
|
-
|
32
|
-
|
34
|
+
debug
|
35
|
+
method_with_hidden_stack
|
33
36
|
(2..4).each do |i|
|
34
37
|
puts i
|
35
38
|
end
|
36
39
|
puts :two
|
37
40
|
end
|
38
|
-
alias
|
41
|
+
alias debugged_method_alias debugged_method
|
39
42
|
|
40
43
|
end
|
41
44
|
|
42
45
|
puts :prepare
|
43
46
|
|
44
|
-
|
45
|
-
A.new.cc_al
|
47
|
+
Sand.new.method_with_hidden_stack
|
46
48
|
|
47
49
|
|
48
50
|
bb = 1
|
data/pry-moves.gemspec
CHANGED
@@ -22,5 +22,6 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_runtime_dependency 'pry', '>= 0.10.4', '< 0.13'
|
23
23
|
gem.add_runtime_dependency 'binding_of_caller', '~> 0.7'
|
24
24
|
gem.add_runtime_dependency 'colorize', '~> 0.8'
|
25
|
+
gem.add_runtime_dependency 'diffy', '~> 3.4.0'
|
25
26
|
gem.add_development_dependency 'pry-remote', '~> 0.1.6'
|
26
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pry-moves
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- garmoshka-mo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -58,6 +58,20 @@ dependencies:
|
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0.8'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: diffy
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 3.4.0
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 3.4.0
|
61
75
|
- !ruby/object:Gem::Dependency
|
62
76
|
name: pry-remote
|
63
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,6 +117,7 @@ files:
|
|
103
117
|
- lib/pry-moves/bindings_stack.rb
|
104
118
|
- lib/pry-moves/cli.rb
|
105
119
|
- lib/pry-moves/commands.rb
|
120
|
+
- lib/pry-moves/error_with_data.rb
|
106
121
|
- lib/pry-moves/formatter.rb
|
107
122
|
- lib/pry-moves/painter.rb
|
108
123
|
- lib/pry-moves/pry_ext.rb
|
@@ -113,6 +128,7 @@ files:
|
|
113
128
|
- lib/pry-moves/version.rb
|
114
129
|
- lib/pry-moves/watch.rb
|
115
130
|
- lib/pry-stack_explorer/VERSION
|
131
|
+
- lib/pry-stack_explorer/frame_helpers.rb
|
116
132
|
- lib/pry-stack_explorer/frame_manager.rb
|
117
133
|
- lib/pry-stack_explorer/pry-stack_explorer.rb
|
118
134
|
- lib/pry-stack_explorer/stack_commands.rb
|