pry-moves 0.1.13 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +6 -4
  4. data/README.md +15 -7
  5. data/lib/commands/debug.rb +17 -0
  6. data/lib/commands/finish.rb +26 -0
  7. data/lib/commands/goto.rb +19 -0
  8. data/lib/commands/iterate.rb +22 -0
  9. data/lib/commands/next.rb +37 -0
  10. data/lib/commands/next_breakpoint.rb +22 -0
  11. data/lib/commands/step.rb +83 -0
  12. data/lib/commands/trace_command.rb +87 -0
  13. data/lib/commands/trace_helpers.rb +49 -0
  14. data/lib/commands/traced_method.rb +71 -0
  15. data/lib/debug_sugar.rb +72 -0
  16. data/lib/pry-moves/add_suffix.rb +88 -0
  17. data/lib/pry-moves/backtrace.rb +67 -43
  18. data/lib/pry-moves/bindings_stack.rb +97 -0
  19. data/lib/pry-moves/commands.rb +42 -3
  20. data/lib/pry-moves/formatter.rb +74 -0
  21. data/lib/pry-moves/painter.rb +3 -2
  22. data/lib/pry-moves/pry_ext.rb +64 -16
  23. data/lib/pry-moves/pry_wrapper.rb +30 -17
  24. data/lib/pry-moves/restartable.rb +38 -0
  25. data/lib/pry-moves/version.rb +1 -1
  26. data/lib/pry-moves/watch.rb +3 -0
  27. data/lib/pry-moves.rb +65 -6
  28. data/lib/pry-stack_explorer/frame_manager.rb +6 -9
  29. data/lib/pry-stack_explorer/pry-stack_explorer.rb +1 -16
  30. data/lib/pry-stack_explorer/{commands.rb → stack_commands.rb} +10 -6
  31. data/lib/pry-stack_explorer/when_started_hook.rb +10 -65
  32. data/playground/Gemfile.lock +6 -4
  33. data/playground/README.md +1 -0
  34. data/playground/playground.rb +94 -9
  35. data/playground/test.rb +5 -1
  36. data/playground/test.sh +1 -0
  37. data/pry-moves.gemspec +3 -2
  38. data/publish.sh +2 -1
  39. data/spec/backtrace_spec.rb +11 -13
  40. data/spec/blocks_spec.rb +41 -8
  41. data/spec/commands_spec.rb +26 -25
  42. data/spec/pry_debugger.rb +5 -1
  43. data/spec/redirection_spec.rb +7 -0
  44. data/spec/spec_helper.rb +9 -4
  45. data/spec/step_spec.rb +51 -0
  46. metadata +43 -13
  47. data/lib/pry-moves/helpers.rb +0 -56
  48. data/lib/pry-moves/trace_commands.rb +0 -109
  49. data/lib/pry-moves/traced_method.rb +0 -61
  50. data/lib/pry-moves/tracer.rb +0 -140
  51. data/lib/pry-moves/traversing.rb +0 -69
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8826581b1684b35099d08f56064e2f3b38ccf3e0d24e3bf40779de9da58151f1
4
- data.tar.gz: d73a5a0b30ac023416e901a3a965d95b4d232bdef5c0f44ab49ec4dfc7708585
3
+ metadata.gz: 2c08b34502f7649fecea8dbf09d8bbc04e917341a260c92bc1e1b50e70ad7fe3
4
+ data.tar.gz: 27be890e5b41dafdbdf58279c85618682ef8a3f24fa2a89094053ec8d5af9455
5
5
  SHA512:
6
- metadata.gz: 2ade46c2440bcd4e14a296b9f3785ce0fff501abe953670ce19c9851d0b52488795113179717dffab6b57221c81839317236417bd1ce76380ceed96c3ed4a596
7
- data.tar.gz: b5b959011051e252cb8a65c1b049f0cdfd8dcd6056b47b7cc7ba87052bb84ec5e1fd767d641385ab2ac1c162f0230d6a7bc760f77ac2699bc5fdbd5d6e812a05
6
+ metadata.gz: b3d5d0a64ab6d910529b3e228154d609b638892913e66ea66c0403e152aea5aa9dceb8e66a46e8515978142af942bb7f59801ec233e1abcb2be44c2338279bc2
7
+ data.tar.gz: f288273d0b30e3bde39ec975e3bd5cb360a7d1a2f747cf8ab95d17e9190ac4f38797c9b21a90a3398383fd5b384dbd4df675a250ad23723360e60ccaed8ebf75
data/Gemfile CHANGED
@@ -4,6 +4,6 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development, :test do
7
- gem 'pry'
7
+ gem 'pry', '0.11.3'
8
8
  gem 'rspec'
9
9
  end
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pry-moves (0.1.13)
4
+ pry-moves (1.0.0)
5
5
  binding_of_caller (~> 0.7)
6
- pry (>= 0.10.4, < 0.12.0)
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
- 2.1.1
50
+ 1.17.3
data/README.md CHANGED
@@ -9,19 +9,22 @@ _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**
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
20
23
  * `g 10` - **goto** line 10
21
- * `bt` - show latest 5 lines from backtrace
22
- * `bt 10` - latest 10 lines
23
- * `bt full` - full backtrace
24
- * `bt +` - full backtrace with hidden frames. Aliases: `bt hidden` `bt vapid` `bt all`
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
25
28
  * `bt > foo` - write backtrace to file `log/backtrace_foo.log`
26
29
  * `up`/`down`/`top`/`bottom` - move over call stack
27
30
  * `up +` - move up, including vapid frames (block callers, hidden frames)
@@ -29,8 +32,12 @@ _An execution control add-on for [Pry][pry]._
29
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
30
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`
31
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
32
37
  * `!` - exit
33
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`
34
41
 
35
42
  ## Examples
36
43
 
@@ -63,10 +70,9 @@ end
63
70
 
64
71
  ## Configuration
65
72
 
66
- Here is default configuration, you can override it:
73
+ Here is default configuration, you can reassign it:
67
74
 
68
75
  ```ruby
69
- PryMoves::Backtrace::lines_count = 5
70
76
  PryMoves::Backtrace::filter =
71
77
  /(\/gems\/|\/rubygems\/|\/bin\/|\/lib\/ruby\/|\/pry-moves\/)/
72
78
  ```
@@ -122,6 +128,8 @@ Please note that debugging functionality is implemented through
122
128
 
123
129
  ```
124
130
  bundle exec rspec
131
+
132
+ DEBUG=true bundle exec rspec -e 'backtrace should backtrace'
125
133
  ```
126
134
 
127
135
  ## ToDo
@@ -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
@@ -0,0 +1,49 @@
1
+ module PryMoves::TraceHelpers
2
+
3
+ def redirect_step?(binding_)
4
+ return false unless binding_.local_variable_defined? :debug_redirect
5
+
6
+ debug_redirect = binding_.local_variable_get(:debug_redirect)
7
+ @step_into_funcs = [debug_redirect] if debug_redirect
8
+ true
9
+ end
10
+
11
+ def debug_info(file, line, id)
12
+ puts "📽 call_depth:#{@call_depth} #{@method[:file]}:#{file}"
13
+ puts "#{id} #{@method[:start]} > #{line} > #{@method[:end]}"
14
+ end
15
+
16
+ def current_frame_digest(upward: 0)
17
+ # binding_ from tracing_func doesn't have @iseq,
18
+ # therefore binding should be re-retrieved using 'binding_of_caller' lib
19
+ frame_digest(binding.of_caller(3 + upward))
20
+ end
21
+
22
+ def frame_digest(binding_)
23
+ #puts "frame_digest for: #{binding_.eval '__callee__'}"
24
+ iseq = binding_.instance_variable_get('@iseq')
25
+ Digest::MD5.hexdigest iseq.disasm
26
+ end
27
+
28
+ def current_frame_type(upward: 0)
29
+ # binding_ from tracing_func doesn't have @iseq,
30
+ # therefore binding should be re-retrieved using 'binding_of_caller' lib
31
+ frame_type(binding.of_caller(3 + upward))
32
+ end
33
+
34
+ def frame_type(binding_)
35
+ line = binding_.instance_variable_get('@iseq').disasm.split("\n").first
36
+ m = line.match /\== disasm: #<ISeq:([\w ]+)@/
37
+ if m
38
+ str = m[1]
39
+ if str.start_with? 'block in '
40
+ :block
41
+ else
42
+ :method
43
+ end
44
+ else
45
+ :unknown
46
+ end
47
+ end
48
+
49
+ end