pry-moves 0.1.9 → 1.0.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 +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +6 -4
- data/README.md +28 -8
- data/lib/commands/debug.rb +17 -0
- data/lib/commands/finish.rb +26 -0
- data/lib/commands/goto.rb +19 -0
- data/lib/commands/iterate.rb +22 -0
- data/lib/commands/next.rb +37 -0
- data/lib/commands/next_breakpoint.rb +22 -0
- data/lib/commands/step.rb +83 -0
- data/lib/commands/trace_command.rb +87 -0
- data/lib/commands/trace_helpers.rb +49 -0
- data/lib/commands/traced_method.rb +71 -0
- data/lib/debug_sugar.rb +72 -0
- data/lib/pry-moves/add_suffix.rb +88 -0
- data/lib/pry-moves/backtrace.rb +70 -46
- data/lib/pry-moves/bindings_stack.rb +97 -0
- data/lib/pry-moves/commands.rb +50 -7
- data/lib/pry-moves/formatter.rb +74 -0
- data/lib/pry-moves/painter.rb +5 -0
- data/lib/pry-moves/pry_ext.rb +64 -16
- data/lib/pry-moves/pry_wrapper.rb +30 -17
- data/lib/pry-moves/restartable.rb +38 -0
- data/lib/pry-moves/version.rb +1 -1
- data/lib/pry-moves/watch.rb +3 -0
- data/lib/pry-moves.rb +65 -4
- data/lib/pry-stack_explorer/VERSION +2 -0
- data/lib/pry-stack_explorer/frame_manager.rb +6 -9
- data/lib/pry-stack_explorer/pry-stack_explorer.rb +3 -17
- data/lib/pry-stack_explorer/{commands.rb → stack_commands.rb} +10 -6
- data/lib/pry-stack_explorer/when_started_hook.rb +17 -63
- data/playground/Gemfile.lock +9 -9
- data/playground/README.md +1 -0
- data/playground/playground.rb +94 -9
- data/playground/sand.rb +46 -12
- data/playground/test.rb +5 -1
- data/playground/test.sh +1 -0
- data/pry-moves.gemspec +3 -2
- data/publish.sh +3 -0
- data/spec/backtrace_spec.rb +11 -13
- data/spec/blocks_spec.rb +41 -8
- data/spec/commands_spec.rb +26 -25
- data/spec/pry_debugger.rb +5 -1
- data/spec/redirection_spec.rb +7 -0
- data/spec/spec_helper.rb +9 -4
- data/spec/step_spec.rb +51 -0
- metadata +43 -10
- data/lib/pry-moves/helpers.rb +0 -50
- data/lib/pry-moves/trace_commands.rb +0 -105
- data/lib/pry-moves/tracer.rb +0 -169
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c08b34502f7649fecea8dbf09d8bbc04e917341a260c92bc1e1b50e70ad7fe3
|
4
|
+
data.tar.gz: 27be890e5b41dafdbdf58279c85618682ef8a3f24fa2a89094053ec8d5af9455
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3d5d0a64ab6d910529b3e228154d609b638892913e66ea66c0403e152aea5aa9dceb8e66a46e8515978142af942bb7f59801ec233e1abcb2be44c2338279bc2
|
7
|
+
data.tar.gz: f288273d0b30e3bde39ec975e3bd5cb360a7d1a2f747cf8ab95d17e9190ac4f38797c9b21a90a3398383fd5b384dbd4df675a250ad23723360e60ccaed8ebf75
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pry-moves (0.
|
4
|
+
pry-moves (1.0.0)
|
5
5
|
binding_of_caller (~> 0.7)
|
6
|
-
|
6
|
+
colorize (~> 0.8)
|
7
|
+
pry (>= 0.10.4, < 0.13)
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: https://rubygems.org/
|
@@ -11,6 +12,7 @@ GEM
|
|
11
12
|
binding_of_caller (0.8.0)
|
12
13
|
debug_inspector (>= 0.0.1)
|
13
14
|
coderay (1.1.2)
|
15
|
+
colorize (0.8.1)
|
14
16
|
debug_inspector (0.0.3)
|
15
17
|
diff-lcs (1.3)
|
16
18
|
method_source (0.9.0)
|
@@ -39,10 +41,10 @@ PLATFORMS
|
|
39
41
|
ruby
|
40
42
|
|
41
43
|
DEPENDENCIES
|
42
|
-
pry
|
44
|
+
pry (= 0.11.3)
|
43
45
|
pry-moves!
|
44
46
|
pry-remote (~> 0.1.6)
|
45
47
|
rspec
|
46
48
|
|
47
49
|
BUNDLED WITH
|
48
|
-
1.
|
50
|
+
1.17.3
|
data/README.md
CHANGED
@@ -9,26 +9,35 @@ _An execution control add-on for [Pry][pry]._
|
|
9
9
|
|
10
10
|
## Commands:
|
11
11
|
|
12
|
+
Documentation for latest version. For [v0.1.12 see documentation here](https://github.com/garmoshka-mo/pry-moves/tree/v0.1.12#commands)
|
13
|
+
|
12
14
|
* `n` - **next** line in current frame, including block lines (moving to next line goes as naturally expected)
|
13
15
|
* `nn` - **next** line in current frame, skipping block lines
|
14
16
|
* `s` - **step** into function execution
|
15
17
|
* `s method_name` - step into method `method_name` (For example from `User.new.method_name`). Partial name match supported.
|
16
18
|
* `s +` - step into function, including hidden frames
|
17
19
|
* `f` - **finish** execution of current frame (block or method) and stop at next line on higher level
|
18
|
-
* `iterate` - go to next iteration of current block
|
19
20
|
* `c` - **continue**
|
20
|
-
* `
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
* `b` - go to next breakpoint (breakpoints currently are methods which contain "debug" in their name)
|
22
|
+
* `iterate` - go to next iteration of current block
|
23
|
+
* `g 10` - **goto** line 10
|
24
|
+
* `bt` - show backtrace, excluding hidden frames
|
25
|
+
* `bt +` `bt hidden` - show backtrace including hidden frames
|
26
|
+
* `bt a` `bt all` - full backtrace with system and hidden frames
|
27
|
+
* `bt 10` - go to backtrace line 10
|
24
28
|
* `bt > foo` - write backtrace to file `log/backtrace_foo.log`
|
25
29
|
* `up`/`down`/`top`/`bottom` - move over call stack
|
26
30
|
* `up +` - move up, including vapid frames (block callers, hidden frames)
|
27
31
|
* `up pattern` - move up till first frame which method name or file position in format `folder/script.rb:12` matches regexp pattern
|
28
32
|
* `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
|
+
* `.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`
|
29
34
|
* `watch variable` - display variable's value on each step
|
35
|
+
* `@` - restart. Set config `PryMoves.reload_rake_tasks = true` to automatically reload rake tasks
|
36
|
+
* `#` - exit with code 3, can be wrapped in bash script to fully reload ruby scripts
|
30
37
|
* `!` - exit
|
31
38
|
|
39
|
+
Variable & methods names takes precedence over commands.
|
40
|
+
So if you have variable named `step`, to execute command `step` type `cmd step` or command's alias, e.g. `s`
|
32
41
|
|
33
42
|
## Examples
|
34
43
|
|
@@ -61,16 +70,25 @@ end
|
|
61
70
|
|
62
71
|
## Configuration
|
63
72
|
|
64
|
-
Here is default configuration, you can
|
73
|
+
Here is default configuration, you can reassign it:
|
65
74
|
|
66
75
|
```ruby
|
67
|
-
PryMoves::Backtrace::lines_count = 5
|
68
76
|
PryMoves::Backtrace::filter =
|
69
77
|
/(\/gems\/|\/rubygems\/|\/bin\/|\/lib\/ruby\/|\/pry-moves\/)/
|
70
78
|
```
|
71
79
|
|
72
80
|
## Threads, helpers
|
73
81
|
|
82
|
+
To allow traveling to parent thread, use:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
pre_callers = binding.callers
|
86
|
+
Thread.new do
|
87
|
+
Thread.current[:pre_callers] = pre_callers
|
88
|
+
#...
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
74
92
|
`pry-moves` can't stop other threads on `binding.pry`, so they will continue to run.
|
75
93
|
This makes `pry-moves` not always suitable for debugging of multi-thread projects.
|
76
94
|
|
@@ -110,6 +128,8 @@ Please note that debugging functionality is implemented through
|
|
110
128
|
|
111
129
|
```
|
112
130
|
bundle exec rspec
|
131
|
+
|
132
|
+
DEBUG=true bundle exec rspec -e 'backtrace should backtrace'
|
113
133
|
```
|
114
134
|
|
115
135
|
## ToDo
|
@@ -126,7 +146,7 @@ bundle exec rspec
|
|
126
146
|
* Ivo Anjo ([@ivoanjo](https://github.com/ivoanjo))
|
127
147
|
|
128
148
|
Patches and bug reports are welcome. Just send a [pull request][pullrequests] or
|
129
|
-
file an [issue][issues].
|
149
|
+
file an [issue][issues].
|
130
150
|
|
131
151
|
## Acknowledgments
|
132
152
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class PryMoves::Debug < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
#
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
return unless event == 'line'
|
9
|
+
if @first_line_skipped
|
10
|
+
true
|
11
|
+
else
|
12
|
+
@first_line_skipped = true
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class PryMoves::Finish < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@block_to_finish = frame_digest(binding_) if binding_.frame_type == :block
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
return true if @on_exit_from_method
|
9
|
+
return if @call_depth > 0 or event == 'c-return'
|
10
|
+
|
11
|
+
return true if @call_depth < 0
|
12
|
+
|
13
|
+
# early return:
|
14
|
+
return true if event == 'return' and
|
15
|
+
@call_depth == 0 and @method.within?(file, line) and
|
16
|
+
method == @method[:name] and @method.before_end?(line)
|
17
|
+
|
18
|
+
# for finishing blocks inside current method
|
19
|
+
if @block_to_finish
|
20
|
+
((@call_depth == 0) ^ (event == 'return')) and
|
21
|
+
@method.within?(file, line) and
|
22
|
+
@block_to_finish != current_frame_digest
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class PryMoves::Goto < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@goto_line = @command[:param].to_i
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
if @call_depth < 0 or
|
9
|
+
@call_depth == 0 and event == 'return' and @method.within?(file, line)
|
10
|
+
PryMoves.messages << "⚠️ Unable to reach line #{@goto_line} in current frame"
|
11
|
+
return true
|
12
|
+
end
|
13
|
+
|
14
|
+
event == 'line' && @goto_line == line and
|
15
|
+
@method[:file] == file and
|
16
|
+
@call_depth == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class PryMoves::Iterate < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@iteration_start_line = binding_.eval('__LINE__')
|
5
|
+
@caller_digest = frame_digest(binding_)
|
6
|
+
end
|
7
|
+
|
8
|
+
def trace(event, file, line, method, binding_)
|
9
|
+
return true if event == 'return' and
|
10
|
+
@method.within?(file, line)
|
11
|
+
|
12
|
+
# промотка итерации -
|
13
|
+
# попасть на ту же или предыдущую строку или выйти из дайджеста
|
14
|
+
# будучи в том же методе
|
15
|
+
event == 'line' and @call_depth == 0 and
|
16
|
+
@method.within?(file, line) and
|
17
|
+
(line <= @iteration_start_line or
|
18
|
+
@caller_digest != current_frame_digest
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class PryMoves::Next < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@start_line = binding_.eval('__LINE__')
|
5
|
+
@start_digest = frame_digest(binding_)
|
6
|
+
if @command[:param] == 'blockless'
|
7
|
+
@stay_at_frame = @start_digest
|
8
|
+
end
|
9
|
+
@events_traced = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def trace(event, file, line, method, binding_)
|
13
|
+
@events_traced += 1
|
14
|
+
|
15
|
+
return true if @call_depth < 0
|
16
|
+
|
17
|
+
return unless @call_depth == 0 and @method.within?(file, line)
|
18
|
+
|
19
|
+
if event == 'line'
|
20
|
+
if @stay_at_frame
|
21
|
+
return (
|
22
|
+
@stay_at_frame == current_frame_digest or
|
23
|
+
@c_stack_level < 0
|
24
|
+
)
|
25
|
+
elsif @start_line != line or (
|
26
|
+
@events_traced > 1 and # чтобы не застревало на while (vx = shift)
|
27
|
+
@start_digest == current_frame_digest # for correct iterating over one_line_in_block
|
28
|
+
)
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
true if event == 'return' and
|
34
|
+
method == @method[:name] and @method.before_end?(line)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class PryMoves::NextBreakpoint < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@reach_digest = frame_digest(binding_)
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
if @reach_digest
|
9
|
+
if @reach_digest == current_frame_digest
|
10
|
+
@reach_digest = nil
|
11
|
+
else
|
12
|
+
return
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
if method.to_s.include? "debug"
|
17
|
+
@pry_start_options[:initial_frame] = 1
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class PryMoves::Step < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@step_into_funcs = nil
|
5
|
+
@start_line = binding_.eval('__LINE__')
|
6
|
+
@caller_digest = frame_digest(binding_)
|
7
|
+
func = @command[:param]
|
8
|
+
redirect_step? binding_ # set @step_into_funcs from initial binding
|
9
|
+
if func == '+'
|
10
|
+
@step_in_everywhere = true
|
11
|
+
elsif func
|
12
|
+
@find_straight_descendant = true
|
13
|
+
@step_into_funcs = [func]
|
14
|
+
@step_into_funcs << '=initialize' if ['new', '=new'].include? func
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def trace(event, file, line, method, binding_)
|
19
|
+
if binding_.local_variable_defined? :pry_moves_skip
|
20
|
+
finish_cmd = {binding: binding_}
|
21
|
+
PryMoves::Finish.new finish_cmd, @pry_start_options do |binding|
|
22
|
+
start_tracing
|
23
|
+
end
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
@const_missing_level ||= 0
|
28
|
+
if method == :const_missing
|
29
|
+
if event == 'call'
|
30
|
+
@const_missing_level += 1
|
31
|
+
elsif event == 'return'
|
32
|
+
@const_missing_level -= 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return if @const_missing_level > 0
|
36
|
+
|
37
|
+
return unless event == 'line'
|
38
|
+
|
39
|
+
if @step_in_everywhere
|
40
|
+
return true
|
41
|
+
elsif @step_into_funcs
|
42
|
+
if @call_depth < 0
|
43
|
+
PryMoves.messages << "⚠️ Unable to find function with name #{@step_into_funcs.join(',')}"
|
44
|
+
return true
|
45
|
+
end
|
46
|
+
|
47
|
+
return false if keep_search_method? binding_
|
48
|
+
elsif redirect_step? binding_
|
49
|
+
return false
|
50
|
+
elsif binding_.local_variable_defined? :hide_from_stack and
|
51
|
+
not @method.within?(file, line, method)
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def keep_search_method? binding_
|
59
|
+
return true unless method_matches? binding_.eval('__callee__')
|
60
|
+
|
61
|
+
return true if @find_straight_descendant &&
|
62
|
+
# if we want to step-in only into straight descendant
|
63
|
+
@caller_digest != current_frame_digest(upward: 1 + 1) # 1 for getting parent and 1 for 'def keep_search_method?'
|
64
|
+
@find_straight_descendant = false
|
65
|
+
|
66
|
+
return true if redirect_step? binding_
|
67
|
+
end
|
68
|
+
|
69
|
+
def method_matches?(method)
|
70
|
+
@step_into_funcs.any? do |pattern|
|
71
|
+
if pattern.is_a? Symbol
|
72
|
+
method == pattern
|
73
|
+
elsif pattern.is_a? Regexp
|
74
|
+
method.to_s.match pattern
|
75
|
+
elsif pattern.start_with? '='
|
76
|
+
"=#{method}" == pattern
|
77
|
+
else
|
78
|
+
method.to_s.include? pattern
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'pry' unless defined? Pry
|
3
|
+
|
4
|
+
module PryMoves
|
5
|
+
class TraceCommand
|
6
|
+
|
7
|
+
include PryMoves::TraceHelpers
|
8
|
+
|
9
|
+
def self.trace(command, pry_start_options, &callback)
|
10
|
+
cls = command[:action].to_s.split('_').collect(&:capitalize).join
|
11
|
+
cls = Object.const_get "PryMoves::#{cls}"
|
12
|
+
cls.new command, pry_start_options, &callback
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(command, pry_start_options, &callback)
|
16
|
+
@command = command
|
17
|
+
@pry_start_options = pry_start_options
|
18
|
+
@pry_start_options[:pry_moves_loop] = true
|
19
|
+
@callback = callback
|
20
|
+
@call_depth = 0
|
21
|
+
@c_stack_level = 0
|
22
|
+
|
23
|
+
binding_ = @command[:binding] # =Command.target - more rich, contains required @iseq
|
24
|
+
unless binding_.instance_variable_get('@iseq')
|
25
|
+
binding_ = PryMoves::BindingsStack.new.initial_frame
|
26
|
+
end
|
27
|
+
@method = PryMoves::TracedMethod.new binding_
|
28
|
+
|
29
|
+
if @pry_start_options.delete :exit_from_method
|
30
|
+
@on_exit_from_method = true
|
31
|
+
@call_depth -= 1
|
32
|
+
end
|
33
|
+
@pry_start_options.delete :initial_frame
|
34
|
+
|
35
|
+
init binding_
|
36
|
+
start_tracing
|
37
|
+
end
|
38
|
+
|
39
|
+
def start_tracing
|
40
|
+
#puts "##trace_obj #{trace_obj}"
|
41
|
+
Pry.config.disable_breakpoints = true
|
42
|
+
trace_obj.set_trace_func method(:tracing_func).to_proc
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_tracing
|
46
|
+
trace_obj.set_trace_func nil
|
47
|
+
Pry.config.disable_breakpoints = false
|
48
|
+
end
|
49
|
+
|
50
|
+
# You can't call set_trace_func or Thread.current.set_trace_func recursively
|
51
|
+
# even in different threads 😪
|
52
|
+
# But! 💡
|
53
|
+
# The hack is - you can call Thread.current.set_trace_func
|
54
|
+
# from inside of set_trace_func! 🤗
|
55
|
+
def trace_obj
|
56
|
+
Thread.current[:pry_moves_debug] ?
|
57
|
+
Thread.current : Kernel
|
58
|
+
end
|
59
|
+
|
60
|
+
def tracing_func(event, file, line, id, binding_, klass)
|
61
|
+
|
62
|
+
# Ignore traces inside pry-moves code
|
63
|
+
return if file && TRACE_IGNORE_FILES.include?(File.expand_path(file))
|
64
|
+
return unless binding_ # ignore strange cases
|
65
|
+
|
66
|
+
# for cases when currently traced method called more times recursively
|
67
|
+
if event == "call" and @method.within?(file, line, id)
|
68
|
+
@call_depth += 1
|
69
|
+
elsif %w(c-call c-return).include?(event)
|
70
|
+
# todo: может быть, c-return тоже правильнее делать после trace
|
71
|
+
delta = event == 'c-call' ? 1 : -1
|
72
|
+
@c_stack_level += delta
|
73
|
+
end
|
74
|
+
|
75
|
+
printf "👟 %8s %s:%-2d %10s %8s dep:#{@call_depth} c_st:#{@c_stack_level}\n", event, file, line, id, klass if PryMoves.trace # TRACE_MOVES=1
|
76
|
+
|
77
|
+
if trace event, file, line, id, binding_
|
78
|
+
@pry_start_options[:exit_from_method] = true if event == 'return'
|
79
|
+
stop_tracing
|
80
|
+
@callback.call binding_
|
81
|
+
elsif event == "return" and @method.within?(file, line, id)
|
82
|
+
@call_depth -= 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|